Merge topic 'cxxmodules-nmc-duplicate-synthetic-targets'

5261af9424 cmGeneratorTarget: store synthetic targets in its cache
e0633a9517 Tests/CXXModules: add a test importing from a `Ninja` install
150d7dbd68 Tests/CXXModules: support building a project with `Ninja`
e48e5e5506 Tests/CXXModules: document `CMake_TEST_MODULE_COMPILATION` items

Acked-by: Kitware Robot <kwrobot@kitware.com>
Tested-by: buildbot <buildbot@kitware.com>
Merge-request: !9263
diff --git a/.gitignore b/.gitignore
index 816dced..1780a41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,6 @@
 /.vs/
 # Visual Studio build directory
 /out/
+
+# clang-tidy output
+/clang-tidy-fixes.patch
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b8bb4ab..b2c90eb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -112,6 +112,7 @@
     extends:
         - .fedora39_tidy
         - .cmake_build_linux
+        - .cmake_tidy_artifacts
         - .linux_x86_64_tags
         - .cmake_cdash_artifacts
         - .run_automatically
@@ -754,6 +755,13 @@
         CMAKE_CI_BUILD_NAME: intel2021.9.0_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8
 
+t:intel2021.10.0-makefiles:
+    extends:
+        - .cmake_test_linux_intelclassic_makefiles
+    variables:
+        CMAKE_CI_BUILD_NAME: intel2021.10.0_makefiles
+        CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8
+
 t:oneapi2021.1.1-makefiles:
     extends:
         - .cmake_test_linux_inteloneapi_makefiles
@@ -824,6 +832,20 @@
         CMAKE_CI_BUILD_NAME: oneapi2023.1.0_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.1.0-el8
 
+t:oneapi2023.2.0-makefiles:
+    extends:
+        - .cmake_test_linux_inteloneapi_makefiles
+    variables:
+        CMAKE_CI_BUILD_NAME: oneapi2023.2.1_makefiles
+        CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2023.2.1-el8
+
+t:oneapi2024.0.0-makefiles:
+    extends:
+        - .cmake_test_linux_inteloneapi_makefiles
+    variables:
+        CMAKE_CI_BUILD_NAME: oneapi2024.0.0_makefiles
+        CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2024.0.0-el8
+
 b:linux-x86_64-package:
     extends:
         - .linux_package
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore
index 19796ef..83e21d5 100644
--- a/.gitlab/.gitignore
+++ b/.gitlab/.gitignore
@@ -16,6 +16,10 @@
 /python*
 /qt*
 /sccache*
+/swift
+/ticlang
 /unstable-jom*
 /watcom
 /wix*
+/clang-tidy-fixes
+/num_warnings.txt
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 2326bcf..b2315be 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -119,6 +119,8 @@
             # Take the sphinx logs.
             - ${CMAKE_CI_BUILD_DIR}/build-*.log
             - ${CMAKE_CI_BUILD_DIR}/linkcheck/output.*
+            # Take the HTML output.
+            - ${CMAKE_CI_BUILD_DIR}/html/
 
 .cmake_test_artifacts:
     artifacts:
@@ -151,3 +153,10 @@
             - ${CMAKE_CI_BUILD_DIR}/html
         exclude:
             - ${CMAKE_CI_BUILD_DIR}/html/.buildinfo
+
+.cmake_tidy_artifacts:
+    artifacts:
+        expire_in: 1d
+        when: always
+        paths:
+            - clang-tidy-fixes.patch
diff --git a/.gitlab/ci/configure_debian12_makefiles_clang.cmake b/.gitlab/ci/configure_debian12_makefiles_clang.cmake
index 20863a2..69f1e7c 100644
--- a/.gitlab/ci/configure_debian12_makefiles_clang.cmake
+++ b/.gitlab/ci/configure_debian12_makefiles_clang.cmake
@@ -1 +1,5 @@
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMake_TEST_TICLANG_TOOLCHAINS "$ENV{CI_PROJECT_DIR}/.gitlab/ticlang" CACHE PATH "")
+endif()
+
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_ninja_clang.cmake b/.gitlab/ci/configure_debian12_ninja_clang.cmake
index 20863a2..69f1e7c 100644
--- a/.gitlab/ci/configure_debian12_ninja_clang.cmake
+++ b/.gitlab/ci/configure_debian12_ninja_clang.cmake
@@ -1 +1,5 @@
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMake_TEST_TICLANG_TOOLCHAINS "$ENV{CI_PROJECT_DIR}/.gitlab/ticlang" CACHE PATH "")
+endif()
+
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora39_tidy.cmake b/.gitlab/ci/configure_fedora39_tidy.cmake
index 4ed1eb3..7a3eaa6 100644
--- a/.gitlab/ci/configure_fedora39_tidy.cmake
+++ b/.gitlab/ci/configure_fedora39_tidy.cmake
@@ -1,5 +1,6 @@
 set(CMake_RUN_CLANG_TIDY ON CACHE BOOL "")
 set(CMake_USE_CLANG_TIDY_MODULE ON CACHE BOOL "")
 set(CMake_CLANG_TIDY_MODULE "$ENV{CI_PROJECT_DIR}/Utilities/ClangTidyModule/build/libcmake-clang-tidy-module.so" CACHE FILEPATH "")
+set(CMake_CLANG_TIDY_EXPORT_FIXES_DIR "$ENV{CI_PROJECT_DIR}/.gitlab/clang-tidy-fixes" CACHE PATH "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora39_common.cmake")
diff --git a/.gitlab/ci/configure_windows_clang_common.cmake b/.gitlab/ci/configure_windows_clang_common.cmake
index 55dce1d..3d93aae 100644
--- a/.gitlab/ci/configure_windows_clang_common.cmake
+++ b/.gitlab/ci/configure_windows_clang_common.cmake
@@ -1,3 +1,7 @@
+set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_Fortran "OFF" CACHE BOOL "")
 set(CMake_TEST_Java OFF CACHE BOOL "")
 
 set(configure_no_sccache 1)
diff --git a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
index 54abf72..a41ec8b 100644
--- a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
+++ b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
@@ -1,6 +1,7 @@
 if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
   set(CMake_TEST_CPACK_INNOSETUP "ON" CACHE STRING "")
   set(CMake_TEST_ISPC "ON" CACHE STRING "")
+  set(CMake_TEST_Swift "ON" CACHE STRING "")
 endif()
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
 
diff --git a/.gitlab/ci/ctest_build.cmake b/.gitlab/ci/ctest_build.cmake
index b1b9830..4a18cf9 100644
--- a/.gitlab/ci/ctest_build.cmake
+++ b/.gitlab/ci/ctest_build.cmake
@@ -65,6 +65,7 @@
   message(FATAL_ERROR
     "Found ${num_warnings} warnings (treating as fatal).")
 endif ()
+file(WRITE "$ENV{CI_PROJECT_DIR}/.gitlab/num_warnings.txt" "${num_warnings}\n")
 
 if (ctest_build_args)
   message(FATAL_ERROR
diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake
index a2789c3..ed5e1dd 100644
--- a/.gitlab/ci/ctest_exclusions.cmake
+++ b/.gitlab/ci/ctest_exclusions.cmake
@@ -34,6 +34,13 @@
     )
 endif()
 
+if ("$ENV{CMAKE_CONFIGURATION}" STREQUAL "windows_vs2022_x64_ninja")
+  list(APPEND test_exclusions
+    # FIXME(#25573): This test failure needs further investigation.
+    "^SwiftMixLib$"
+    )
+endif()
+
 string(REPLACE ";" "|" test_exclusions "${test_exclusions}")
 if (test_exclusions)
   set(test_exclusions "(${test_exclusions})")
diff --git a/.gitlab/ci/env_debian12_makefiles_clang.sh b/.gitlab/ci/env_debian12_makefiles_clang.sh
index eda7c1f..e46aa29 100644
--- a/.gitlab/ci/env_debian12_makefiles_clang.sh
+++ b/.gitlab/ci/env_debian12_makefiles_clang.sh
@@ -1,2 +1,6 @@
+if test "$CMAKE_CI_NIGHTLY" = "true"; then
+  source .gitlab/ci/ticlang-env.sh
+fi
+
 export CC=/usr/bin/clang-15
 export CXX=/usr/bin/clang++-15
diff --git a/.gitlab/ci/env_debian12_ninja_clang.sh b/.gitlab/ci/env_debian12_ninja_clang.sh
index eda7c1f..e46aa29 100644
--- a/.gitlab/ci/env_debian12_ninja_clang.sh
+++ b/.gitlab/ci/env_debian12_ninja_clang.sh
@@ -1,2 +1,6 @@
+if test "$CMAKE_CI_NIGHTLY" = "true"; then
+  source .gitlab/ci/ticlang-env.sh
+fi
+
 export CC=/usr/bin/clang-15
 export CXX=/usr/bin/clang++-15
diff --git a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
index 50a03ca..b512e37 100755
--- a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
+++ b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
@@ -1,4 +1,5 @@
 if ("$env:CMAKE_CI_NIGHTLY" -eq "true") {
   . ".gitlab/ci/innosetup-env.ps1"
   . ".gitlab/ci/ispc-env.ps1"
+  . ".gitlab/ci/swift-env.ps1"
 }
diff --git a/.gitlab/ci/post_build_fedora39_tidy.sh b/.gitlab/ci/post_build_fedora39_tidy.sh
new file mode 100644
index 0000000..a36663a
--- /dev/null
+++ b/.gitlab/ci/post_build_fedora39_tidy.sh
@@ -0,0 +1,21 @@
+git config user.name "Kitware Robot"
+git config user.email "kwrobot@kitware.com"
+
+clang-apply-replacements --style=file .gitlab/clang-tidy-fixes
+git add .
+
+if [ -n "$(git status --porcelain)" ]; then
+  quietly git commit --file=- <<EOF
+WIP: clang-tidy: <SHORT DESCRIPTION OF CHANGE HERE>
+
+<LONGER DESCRIPTION OF CHANGE HERE.>
+EOF
+  git format-patch --output=clang-tidy-fixes.patch -1 -N
+  echo "Patch from clang-tidy available, check artifacts of this CI job." >&2
+fi
+
+readonly num_warnings="$(cat .gitlab/num_warnings.txt)"
+if [ "$num_warnings" -ne 0 ]; then
+  echo "Found $num_warnings warnings (treating as fatal)." >&2
+  exit 1
+fi
diff --git a/.gitlab/ci/swift-env.ps1 b/.gitlab/ci/swift-env.ps1
new file mode 100755
index 0000000..871b31c
--- /dev/null
+++ b/.gitlab/ci/swift-env.ps1
@@ -0,0 +1,6 @@
+$pwdpath = $pwd.Path
+& "$pwsh" -File ".gitlab/ci/swift.ps1"
+Set-Item -Force -Path "env:DEVELOPER_DIR" -Value "$pwdpath\.gitlab\swift"
+Set-Item -Force -Path "env:SDKROOT" -Value "$pwdpath\.gitlab\swift\Platforms\Windows.platform\Developer\SDKs\Windows.sdk"
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\swift\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin;$env:PATH"
+swiftc --version
diff --git a/.gitlab/ci/swift.ps1 b/.gitlab/ci/swift.ps1
new file mode 100755
index 0000000..b970dce
--- /dev/null
+++ b/.gitlab/ci/swift.ps1
@@ -0,0 +1,38 @@
+$erroractionpreference = "stop"
+
+$version = "5.9.2"
+$sha256sum = "8C053108528EB2DAD84C33D6F0834A3A1444D21BA1A89D591AB149304A62F6B5"
+$filename = "swift-$version-win-x86_64-1"
+$tarball = "$filename.zip"
+
+$outdir = $pwd.Path
+$outdir = "$outdir\.gitlab"
+$ProgressPreference = 'SilentlyContinue'
+# This URL is only visible inside of Kitware's network.  See above filename table.
+Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$tarball" -OutFile "$outdir\$tarball"
+$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256
+if ($hash.Hash -ne $sha256sum) {
+    exit 1
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir")
+Move-Item -Path "$outdir\$filename" -Destination "$outdir\swift"
+Remove-Item "$outdir\$tarball"
+
+$bin = "$outdir\swift\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin"
+$null = New-Item -ItemType HardLink -Path "$bin\clang++.exe"      -Target "$bin\clang.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\clang-cl.exe"     -Target "$bin\clang.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\clang-cpp.exe"    -Target "$bin\clang.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\ld.lld.exe"       -Target "$bin\lld.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\ld64.lld.exe"     -Target "$bin\lld.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\lld-link.exe"     -Target "$bin\lld.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\llvm-dlltool.exe" -Target "$bin\llvm-ar.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\llvm-lib.exe"     -Target "$bin\llvm-ar.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\llvm-ranlib.exe"  -Target "$bin\llvm-ar.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\llvm-objcopy.exe" -Target "$bin\llvm-strip.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\swiftc.exe"       -Target "$bin\swift.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\swift-api-digester.exe"        -Target "$bin\swift-frontend.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\swift-autolink-extract.exe"    -Target "$bin\swift-frontend.exe"
+$null = New-Item -ItemType HardLink -Path "$bin\swift-symbolgraph-extract.exe" -Target "$bin\swift-frontend.exe"
+Clear-Variable -Name bin
diff --git a/.gitlab/ci/ticlang-env.sh b/.gitlab/ci/ticlang-env.sh
new file mode 100644
index 0000000..448c0d7
--- /dev/null
+++ b/.gitlab/ci/ticlang-env.sh
@@ -0,0 +1,2 @@
+.gitlab/ci/ticlang.sh
+.gitlab/ticlang/bin/tiarmclang --version
diff --git a/.gitlab/ci/ticlang.sh b/.gitlab/ci/ticlang.sh
new file mode 100755
index 0000000..66fa863
--- /dev/null
+++ b/.gitlab/ci/ticlang.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+set -e
+
+case "$(uname -s)-$(uname -m)" in
+    Linux-x86_64)
+        shatool="sha256sum"
+        sha256sum="c69ac58e403b82eac1c407cc67b35fab5d95c5d8db75b019095f9412aacff27d"
+        filename="ti_cgt_armllvm_3.2.1.LTS_linux-x64_installer.bin"
+        dirname="ti-cgt-armllvm_3.2.1.LTS"
+        ;;
+    *)
+        echo "Unrecognized platform $(uname -s)-$(uname -m)"
+        exit 1
+        ;;
+esac
+readonly shatool
+readonly sha256sum
+
+cd .gitlab
+
+echo "$sha256sum  $filename" > ticlang.sha256sum
+curl -OL "https://cmake.org/files/dependencies/internal/$filename"
+$shatool --check ticlang.sha256sum
+chmod +x "$filename"
+"./$filename" --mode unattended --prefix .
+mv "$dirname" ticlang
+rm -f "$filename" ticlang.sha256sum
diff --git a/.gitlab/ci/wix.ps1 b/.gitlab/ci/wix.ps1
index b7cb3f3..9313c0f 100755
--- a/.gitlab/ci/wix.ps1
+++ b/.gitlab/ci/wix.ps1
@@ -1,14 +1,15 @@
 $erroractionpreference = "stop"
 
-$release = "v3.14.0.6526"
-$sha256sum = "4C89898DF3BCAB13E12F7CA54399C35AD273475AD2CB6284611D00AE2D063C2C"
-$filename = "wix-3.14.0.6526-win-i386"
+$release = "wix314rtm"
+$sha256sum = "13F067F38969FAF163D93A804B48EA0576790A202C8F10291F2000F0E356E934"
+#$filename = "wix314-binaries"
+$filename = "wix-3.14.0.8606-win-i386"
 $tarball = "$filename.zip"
 
 $outdir = $pwd.Path
 $outdir = "$outdir\.gitlab"
 $ProgressPreference = 'SilentlyContinue'
-#Invoke-WebRequest -Uri "https://wixtoolset.org/downloads/$release/$tarball" -OutFile "$outdir\$tarball"
+#Invoke-WebRequest -Uri "https://github.com/wixtoolset/wix3/releases/download/$release/$tarball" -OutFile "$outdir\$tarball"
 Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball"
 $hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256
 if ($hash.Hash -ne $sha256sum) {
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index f529ab2..e0364a5 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -82,7 +82,6 @@
 
     variables:
         CMAKE_CONFIGURATION: fedora39_tidy
-        CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
 .fedora39_clang_analyzer:
diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el
index 6bd23bf..b5e5ff4 100644
--- a/Auxiliary/cmake-mode.el
+++ b/Auxiliary/cmake-mode.el
@@ -258,15 +258,6 @@
       (forward-line)
       t)))
 
-(defun cmake-mark-defun ()
-  "Mark the current CMake function or macro.
-
-This puts the mark at the end, and point at the beginning."
-  (interactive)
-  (cmake-end-of-defun)
-  (push-mark nil :nomsg :activate)
-  (cmake-beginning-of-defun))
-
 
 ;------------------------------------------------------------------------------
 
@@ -346,6 +337,10 @@
 (define-derived-mode cmake-mode prog-mode "CMake"
   "Major mode for editing CMake source files."
 
+  ;; Setup jumping to beginning/end of a CMake function/macro.
+  (set (make-local-variable 'beginning-of-defun-function) #'cmake-beginning-of-defun)
+  (set (make-local-variable 'end-of-defun-function) #'cmake-end-of-defun)
+
   ; Setup font-lock mode.
   (set (make-local-variable 'font-lock-defaults) '(cmake-font-lock-keywords))
   ; Setup indentation function.
@@ -356,11 +351,6 @@
   (set (make-local-variable 'syntax-propertize-function) cmake--syntax-propertize-function)
   (add-hook 'syntax-propertize-extend-region-functions #'syntax-propertize-multiline nil t))
 
-;; Default cmake-mode key bindings
-(define-key cmake-mode-map "\e\C-a" #'cmake-beginning-of-defun)
-(define-key cmake-mode-map "\e\C-e" #'cmake-end-of-defun)
-(define-key cmake-mode-map "\e\C-h" #'cmake-mark-defun)
-
 
 ; Help mode starts here
 
diff --git a/Auxiliary/vim/indent/cmake.vim b/Auxiliary/vim/indent/cmake.vim
index 0c662fa..28ecf84 100644
--- a/Auxiliary/vim/indent/cmake.vim
+++ b/Auxiliary/vim/indent/cmake.vim
@@ -17,6 +17,8 @@
 setlocal indentexpr=CMakeGetIndent(v:lnum)
 setlocal indentkeys+==ENDIF(,ENDFOREACH(,ENDMACRO(,ELSE(,ELSEIF(,ENDWHILE(
 
+let b:undo_indent = "setl inde< indk<"
+
 " Only define the function once.
 if exists("*CMakeGetIndent")
   finish
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index 4bbdc65..5629356 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -19,10 +19,10 @@
 let s:keepcpo= &cpo
 set cpo&vim
 
-syn region cmakeBracketArgument start="\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
+syn region cmakeBracketArgument start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
-syn region cmakeComment start="#" end="$" contains=cmakeTodo,@Spell
-syn region cmakeBracketComment start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
+syn region cmakeComment start="#\(\[=*\[\)\@!" end="$" contains=cmakeTodo,@Spell
+syn region cmakeBracketComment start="#\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
 syn match cmakeEscaped /\(\\\\\|\\"\|\\n\|\\t\)/ contained
 syn region cmakeRegistry start="\[" end="]" contained oneline contains=cmakeTodo,cmakeEscaped
@@ -70,6 +70,7 @@
             \ ATTACHED_FILES
             \ ATTACHED_FILES_ON_FAIL
             \ AUTOGEN_BUILD_DIR
+            \ AUTOGEN_COMMAND_LINE_LENGTH_MAX
             \ AUTOGEN_ORIGIN_DEPENDS
             \ AUTOGEN_PARALLEL
             \ AUTOGEN_SOURCE_GROUP
@@ -77,6 +78,7 @@
             \ AUTOGEN_TARGETS_FOLDER
             \ AUTOGEN_TARGET_DEPENDS
             \ AUTOGEN_USE_SYSTEM_INCLUDE
+            \ AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
             \ AUTOMOC
             \ AUTOMOC_COMPILER_PREDEFINES
             \ AUTOMOC_DEPEND_FILTERS
@@ -374,6 +376,7 @@
             \ Swift_LANGUAGE_VERSION
             \ Swift_MODULE_DIRECTORY
             \ Swift_MODULE_NAME
+            \ Swift_COMPILATION_MODE
             \ TARGET_ARCHIVES_MAY_BE_SHARED_LIBS
             \ TARGET_MESSAGES
             \ TARGET_SUPPORTS_SHARED_LIBS
@@ -764,6 +767,8 @@
             \ CMAKE_ASM_STANDARD_REQUIRED
             \ CMAKE_ASM_SUPPORTED
             \ CMAKE_ASM_VISIBILITY_PRESET
+            \ CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
+            \ CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX
             \ CMAKE_AUTOGEN_ORIGIN_DEPENDS
             \ CMAKE_AUTOGEN_PARALLEL
             \ CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE
@@ -1656,6 +1661,7 @@
             \ CMAKE_SKIP_INSTALL_RPATH
             \ CMAKE_SKIP_INSTALL_RULES
             \ CMAKE_SKIP_RPATH
+            \ CMAKE_SKIP_TEST_ALL_DEPENDENCY
             \ CMAKE_SOURCE_DIR
             \ CMAKE_STAGING_PREFIX
             \ CMAKE_STATIC_LIBRARY_PREFIX
@@ -2760,7 +2766,6 @@
             \ DIRECTORY
             \ EVAL
             \ FALSE
-            \ FETCHCONTENT_MAKEAVAILABE_SERIAL
             \ FETCHCONTENT_MAKEAVAILABLE_SERIAL
             \ FETCHCONTENT_SOURCE_DIR_
             \ FETCHCONTENT_TRY_FIND_PACKAGE_MODE
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2823ca4..5f661a9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
 
@@ -140,6 +140,7 @@
     if(CMAKE_SYSTEM_NAME MATCHES "Windows|Darwin|Linux|BSD|DragonFly|CYGWIN|MSYS"
         AND NOT (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.16)
         AND NOT (CMAKE_CXX_COMPILER_ID STREQUAL "XLClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.1)
+        AND NOT (CMAKE_CXX_COMPILER_ID STREQUAL "LCC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 1.23)
         )
       set(CMake_ENABLE_DEBUGGER 1)
     else()
diff --git a/Copyright.txt b/Copyright.txt
index 515e403..2074109 100644
--- a/Copyright.txt
+++ b/Copyright.txt
@@ -1,5 +1,5 @@
 CMake - Cross Platform Makefile Generator
-Copyright 2000-2023 Kitware, Inc. and Contributors
+Copyright 2000-2024 Kitware, Inc. and Contributors
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index 5fe4326..5996d64 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -373,6 +373,11 @@
     :manual:`generator expressions <cmake-generator-expressions(7)>` was also
     added.
 
+  .. versionadded:: 3.29
+    The :ref:`Ninja Generators` will now incorporate the dependencies into its
+    "deps log" database if the file is not listed in ``OUTPUTS`` or
+    ``BYPRODUCTS``.
+
   Using ``DEPFILE`` with generators other than those listed above is an error.
 
   If the ``DEPFILE`` argument is relative, it should be relative to
@@ -549,6 +554,9 @@
 .. versionadded:: 3.21
   Support for target-dependent generator expressions.
 
+.. versionadded:: 3.29
+  The ``<target>`` may be an :ref:`ALIAS target <Alias Targets>`.
+
 Examples: Build Events
 ^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/Help/command/add_executable.rst b/Help/command/add_executable.rst
index d9ea0da..b6833b4 100644
--- a/Help/command/add_executable.rst
+++ b/Help/command/add_executable.rst
@@ -10,15 +10,28 @@
 Normal Executables
 ^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
+.. signature::
+  add_executable(<name> <options>... <sources>...)
+  :target: normal
 
-  add_executable(<name> [WIN32] [MACOSX_BUNDLE]
-                 [EXCLUDE_FROM_ALL]
-                 [source1] [source2 ...])
+  Add an executable target called ``<name>`` to be built from the source
+  files listed in the command invocation.
 
-Adds an executable target called ``<name>`` to be built from the source
-files listed in the command invocation.  The
-``<name>`` corresponds to the logical target name and must be globally
+  The options are:
+
+  ``WIN32``
+    Set the :prop_tgt:`WIN32_EXECUTABLE` target property automatically.
+    See documentation of that target property for details.
+
+  ``MACOSX_BUNDLE``
+    Set the :prop_tgt:`MACOSX_BUNDLE` target property automatically.
+    See documentation of that target property for details.
+
+  ``EXCLUDE_FROM_ALL``
+    Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically.
+    See documentation of that target property for details.
+
+The ``<name>`` corresponds to the logical target name and must be globally
 unique within a project.  The actual file name of the executable built is
 constructed based on conventions of the native platform (such as
 ``<name>.exe`` or just ``<name>``).
@@ -39,18 +52,6 @@
 location.  See documentation of the :prop_tgt:`OUTPUT_NAME` target property
 to change the ``<name>`` part of the final file name.
 
-If ``WIN32`` is given the property :prop_tgt:`WIN32_EXECUTABLE` will be
-set on the target created.  See documentation of that target property for
-details.
-
-If ``MACOSX_BUNDLE`` is given the corresponding property will be set on
-the created target.  See documentation of the :prop_tgt:`MACOSX_BUNDLE`
-target property for details.
-
-If ``EXCLUDE_FROM_ALL`` is given the corresponding property will be set on
-the created target.  See documentation of the :prop_tgt:`EXCLUDE_FROM_ALL`
-target property for details.
-
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 buildsystem properties.
 
@@ -61,17 +62,25 @@
 Imported Executables
 ^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
+.. signature::
   add_executable(<name> IMPORTED [GLOBAL])
+  :target: IMPORTED
 
-An :ref:`IMPORTED executable target <Imported Targets>` references an
-executable file located outside the project.  No rules are generated to
-build it, and the :prop_tgt:`IMPORTED` target property is ``True``.  The
-target name has scope in the directory in which it is created and below, but
-the ``GLOBAL`` option extends visibility.  It may be referenced like any
-target built within the project.  ``IMPORTED`` executables are useful
-for convenient reference from commands like :command:`add_custom_command`.
+  Add an :ref:`IMPORTED executable target <Imported Targets>` to reference
+  an executable file located outside the project.  The target name may be
+  referenced like any target built within the project, except that by
+  default it is visible only in the directory in which it is created,
+  and below.
+
+  The options are:
+
+  ``GLOBAL``
+    Make the target name globally visible.
+
+No rules are generated to build imported targets, and the :prop_tgt:`IMPORTED`
+target property is ``True``.  Imported executables are useful for convenient
+reference from commands like :command:`add_custom_command`.
+
 Details about the imported executable are specified by setting properties
 whose names begin in ``IMPORTED_``.  The most important such property is
 :prop_tgt:`IMPORTED_LOCATION` (and its per-configuration version
@@ -82,14 +91,14 @@
 Alias Executables
 ^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
+.. signature::
   add_executable(<name> ALIAS <target>)
+  :target: ALIAS
 
-Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can
-be used to refer to ``<target>`` in subsequent commands.  The ``<name>``
-does not appear in the generated buildsystem as a make target.  The
-``<target>`` may not be an ``ALIAS``.
+  Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can
+  be used to refer to ``<target>`` in subsequent commands.  The ``<name>``
+  does not appear in the generated buildsystem as a make target.  The
+  ``<target>`` may not be an ``ALIAS``.
 
 .. versionadded:: 3.11
   An ``ALIAS`` can target a ``GLOBAL`` :ref:`Imported Target <Imported Targets>`
diff --git a/Help/command/add_library.rst b/Help/command/add_library.rst
index 07c8bab..5b22cb1 100644
--- a/Help/command/add_library.rst
+++ b/Help/command/add_library.rst
@@ -10,18 +10,39 @@
 Normal Libraries
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
+.. signature::
+  add_library(<name> [<type>] [EXCLUDE_FROM_ALL] <sources>...)
+  :target: normal
 
-  add_library(<name> [STATIC | SHARED | MODULE]
-              [EXCLUDE_FROM_ALL]
-              [<source>...])
+  Add a library target called ``<name>`` to be built from the source files
+  listed in the command invocation.
 
-Adds a library target called ``<name>`` to be built from the source files
-listed in the command invocation.  The ``<name>``
-corresponds to the logical target name and must be globally unique within
-a project.  The actual file name of the library built is constructed based
-on conventions of the native platform (such as ``lib<name>.a`` or
-``<name>.lib``).
+  The optional ``<type>`` specifies the type of library to be created:
+
+  ``STATIC``
+    An archive of object files for use when linking other targets.
+
+  ``SHARED``
+    A dynamic library that may be linked by other targets and loaded
+    at runtime.
+
+  ``MODULE``
+    A plugin that may not be linked by other targets, but may be
+    dynamically loaded at runtime using dlopen-like functionality.
+
+  If no ``<type>`` is given the default is ``STATIC`` or ``SHARED``
+  based on the value of the :variable:`BUILD_SHARED_LIBS` variable.
+
+  The options are:
+
+  ``EXCLUDE_FROM_ALL``
+    Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically.
+    See documentation of that target property for details.
+
+The ``<name>`` corresponds to the logical target name and must be globally
+unique within a project.  The actual file name of the library built is
+constructed based on conventions of the native platform (such as
+``lib<name>.a`` or ``<name>.lib``).
 
 .. versionadded:: 3.1
   Source arguments to ``add_library`` may use "generator expressions" with
@@ -32,15 +53,8 @@
   The source files can be omitted if they are added later using
   :command:`target_sources`.
 
-``STATIC``, ``SHARED``, or ``MODULE`` may be given to specify the type of
-library to be created.  ``STATIC`` libraries are archives of object files
-for use when linking other targets.  ``SHARED`` libraries are linked
-dynamically and loaded at runtime.  ``MODULE`` libraries are plugins that
-are not linked into other targets but may be loaded dynamically at runtime
-using dlopen-like functionality.  If no type is given explicitly the
-type is ``STATIC`` or ``SHARED`` based on whether the current value of the
-variable :variable:`BUILD_SHARED_LIBS` is ``ON``.  For ``SHARED`` and
-``MODULE`` libraries the :prop_tgt:`POSITION_INDEPENDENT_CODE` target
+For ``SHARED`` and ``MODULE`` libraries the
+:prop_tgt:`POSITION_INDEPENDENT_CODE` target
 property is set to ``ON`` automatically.
 A ``SHARED`` library may be marked with the :prop_tgt:`FRAMEWORK`
 target property to create an macOS Framework.
@@ -63,10 +77,6 @@
 location.  See documentation of the :prop_tgt:`OUTPUT_NAME` target
 property to change the ``<name>`` part of the final file name.
 
-If ``EXCLUDE_FROM_ALL`` is given the corresponding property will be set on
-the created target.  See documentation of the :prop_tgt:`EXCLUDE_FROM_ALL`
-target property for details.
-
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 buildsystem properties.
 
@@ -77,14 +87,15 @@
 Object Libraries
 ^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
+.. signature::
+  add_library(<name> OBJECT <sources>...)
+  :target: OBJECT
 
-  add_library(<name> OBJECT [<source>...])
+  Add an :ref:`Object Library <Object Libraries>` to compile source files
+  without archiving or linking their object files into a library.
 
-Creates an :ref:`Object Library <Object Libraries>`.  An object library
-compiles source files but does not archive or link their object files into a
-library.  Instead other targets created by ``add_library`` or
-:command:`add_executable` may reference the objects using an expression of the
+Other targets created by ``add_library`` or :command:`add_executable`
+may reference the objects using an expression of the
 form :genex:`$\<TARGET_OBJECTS:objlib\> <TARGET_OBJECTS>` as a source, where
 ``objlib`` is the object library name.  For example:
 
@@ -109,46 +120,48 @@
 Interface Libraries
 ^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
+.. signature::
   add_library(<name> INTERFACE)
+  :target: INTERFACE
 
-Creates an :ref:`Interface Library <Interface Libraries>`.
-An ``INTERFACE`` library target does not compile sources and does
-not produce a library artifact on disk.  However, it may have
-properties set on it and it may be installed and exported.
-Typically, ``INTERFACE_*`` properties are populated on an interface
-target using the commands:
+  Add an :ref:`Interface Library <Interface Libraries>` target that may
+  specify usage requirements for dependents but does not compile sources
+  and does not produce a library artifact on disk.
 
-* :command:`set_property`,
-* :command:`target_link_libraries(INTERFACE)`,
-* :command:`target_link_options(INTERFACE)`,
-* :command:`target_include_directories(INTERFACE)`,
-* :command:`target_compile_options(INTERFACE)`,
-* :command:`target_compile_definitions(INTERFACE)`, and
-* :command:`target_sources(INTERFACE)`,
+  An interface library with no source files is not included as a target
+  in the generated buildsystem.  However, it may have
+  properties set on it and it may be installed and exported.
+  Typically, ``INTERFACE_*`` properties are populated on an interface
+  target using the commands:
 
-and then it is used as an argument to :command:`target_link_libraries`
-like any other target.
+  * :command:`set_property`,
+  * :command:`target_link_libraries(INTERFACE)`,
+  * :command:`target_link_options(INTERFACE)`,
+  * :command:`target_include_directories(INTERFACE)`,
+  * :command:`target_compile_options(INTERFACE)`,
+  * :command:`target_compile_definitions(INTERFACE)`, and
+  * :command:`target_sources(INTERFACE)`,
 
-An interface library created with the above signature has no source files
-itself and is not included as a target in the generated buildsystem.
+  and then it is used as an argument to :command:`target_link_libraries`
+  like any other target.
 
-.. versionadded:: 3.15
-  An interface library can have :prop_tgt:`PUBLIC_HEADER` and
-  :prop_tgt:`PRIVATE_HEADER` properties.  The headers specified by those
-  properties can be installed using the :command:`install(TARGETS)` command.
+  .. versionadded:: 3.15
+    An interface library can have :prop_tgt:`PUBLIC_HEADER` and
+    :prop_tgt:`PRIVATE_HEADER` properties.  The headers specified by those
+    properties can be installed using the :command:`install(TARGETS)` command.
 
-.. versionadded:: 3.19
-  An interface library target may be created with source files:
+.. signature::
+  add_library(<name> INTERFACE [EXCLUDE_FROM_ALL] <sources>...)
+  :target: INTERFACE-with-sources
 
-  .. code-block:: cmake
+  .. versionadded:: 3.19
 
-    add_library(<name> INTERFACE [<source>...] [EXCLUDE_FROM_ALL])
-
-  Source files may be listed directly in the ``add_library`` call or added
-  later by calls to :command:`target_sources` with the ``PRIVATE`` or
-  ``PUBLIC`` keywords.
+  Add an :ref:`Interface Library <Interface Libraries>` target with
+  source files (in addition to usage requirements and properties as
+  documented by the :command:`above signature <add_library(INTERFACE)>`).
+  Source files may be listed directly in the ``add_library`` call
+  or added later by calls to :command:`target_sources` with the
+  ``PRIVATE`` or ``PUBLIC`` keywords.
 
   If an interface library has source files (i.e. the :prop_tgt:`SOURCES`
   target property is set), or header sets (i.e. the :prop_tgt:`HEADER_SETS`
@@ -158,92 +171,106 @@
   but does contain build rules for custom commands created by the
   :command:`add_custom_command` command.
 
-.. note::
-  In most command signatures where the ``INTERFACE`` keyword appears,
-  the items listed after it only become part of that target's usage
-  requirements and are not part of the target's own settings.  However,
-  in this signature of ``add_library``, the ``INTERFACE`` keyword refers
-  to the library type only.  Sources listed after it in the ``add_library``
-  call are ``PRIVATE`` to the interface library and do not appear in its
-  :prop_tgt:`INTERFACE_SOURCES` target property.
+  The options are:
+
+  ``EXCLUDE_FROM_ALL``
+    Set the :prop_tgt:`EXCLUDE_FROM_ALL` target property automatically.
+    See documentation of that target property for details.
+
+  .. note::
+    In most command signatures where the ``INTERFACE`` keyword appears,
+    the items listed after it only become part of that target's usage
+    requirements and are not part of the target's own settings.  However,
+    in this signature of ``add_library``, the ``INTERFACE`` keyword refers
+    to the library type only.  Sources listed after it in the ``add_library``
+    call are ``PRIVATE`` to the interface library and do not appear in its
+    :prop_tgt:`INTERFACE_SOURCES` target property.
 
 .. _`add_library imported libraries`:
 
 Imported Libraries
 ^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
+.. signature::
   add_library(<name> <type> IMPORTED [GLOBAL])
+  :target: IMPORTED
 
-Creates an :ref:`IMPORTED library target <Imported Targets>` called ``<name>``.
-No rules are generated to build it, and the :prop_tgt:`IMPORTED` target
-property is ``True``.  The target name has scope in the directory in which
-it is created and below, but the ``GLOBAL`` option extends visibility.
-It may be referenced like any target built within the project.
-``IMPORTED`` libraries are useful for convenient reference from commands
-like :command:`target_link_libraries`.  Details about the imported library
-are specified by setting properties whose names begin in ``IMPORTED_`` and
-``INTERFACE_``.
+  Add an :ref:`IMPORTED library target <Imported Targets>` called ``<name>``.
+  The target name may be referenced like any target built within the project,
+  except that by default it is visible only in the directory in which it is
+  created, and below.
 
-The ``<type>`` must be one of:
+  The ``<type>`` must be one of:
 
-``STATIC``, ``SHARED``, ``MODULE``, ``UNKNOWN``
-  References a library file located outside the project.  The
-  :prop_tgt:`IMPORTED_LOCATION` target property (or its per-configuration
-  variant :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`) specifies the
-  location of the main library file on disk:
+  ``STATIC``, ``SHARED``, ``MODULE``, ``UNKNOWN``
+    References a library file located outside the project.  The
+    :prop_tgt:`IMPORTED_LOCATION` target property (or its per-configuration
+    variant :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`) specifies the
+    location of the main library file on disk:
 
-  * For a ``SHARED`` library on most non-Windows platforms, the main library
-    file is the ``.so`` or ``.dylib`` file used by both linkers and dynamic
-    loaders.  If the referenced library file has a ``SONAME`` (or on macOS,
-    has a ``LC_ID_DYLIB`` starting in ``@rpath/``), the value of that field
-    should be set in the :prop_tgt:`IMPORTED_SONAME` target property.
-    If the referenced library file does not have a ``SONAME``, but the
-    platform supports it, then  the :prop_tgt:`IMPORTED_NO_SONAME` target
-    property should be set.
+    * For a ``SHARED`` library on most non-Windows platforms, the main library
+      file is the ``.so`` or ``.dylib`` file used by both linkers and dynamic
+      loaders.  If the referenced library file has a ``SONAME`` (or on macOS,
+      has a ``LC_ID_DYLIB`` starting in ``@rpath/``), the value of that field
+      should be set in the :prop_tgt:`IMPORTED_SONAME` target property.
+      If the referenced library file does not have a ``SONAME``, but the
+      platform supports it, then  the :prop_tgt:`IMPORTED_NO_SONAME` target
+      property should be set.
 
-  * For a ``SHARED`` library on Windows, the :prop_tgt:`IMPORTED_IMPLIB`
-    target property (or its per-configuration variant
-    :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>`) specifies the location of the
-    DLL import library file (``.lib`` or ``.dll.a``) on disk, and the
-    ``IMPORTED_LOCATION`` is the location of the ``.dll`` runtime
-    library (and is optional, but needed by the :genex:`TARGET_RUNTIME_DLLS`
-    generator expression).
+    * For a ``SHARED`` library on Windows, the :prop_tgt:`IMPORTED_IMPLIB`
+      target property (or its per-configuration variant
+      :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>`) specifies the location of the
+      DLL import library file (``.lib`` or ``.dll.a``) on disk, and the
+      ``IMPORTED_LOCATION`` is the location of the ``.dll`` runtime
+      library (and is optional, but needed by the :genex:`TARGET_RUNTIME_DLLS`
+      generator expression).
 
-  Additional usage requirements may be specified in ``INTERFACE_*`` properties.
+    Additional usage requirements may be specified in ``INTERFACE_*``
+    properties.
 
-  An ``UNKNOWN`` library type is typically only used in the implementation of
-  :ref:`Find Modules`.  It allows the path to an imported library (often found
-  using the :command:`find_library` command) to be used without having to know
-  what type of library it is.  This is especially useful on Windows where a
-  static library and a DLL's import library both have the same file extension.
+    An ``UNKNOWN`` library type is typically only used in the implementation
+    of :ref:`Find Modules`.  It allows the path to an imported library
+    (often found using the :command:`find_library` command) to be used
+    without having to know what type of library it is.  This is especially
+    useful on Windows where a static library and a DLL's import library
+    both have the same file extension.
 
-``OBJECT``
-  References a set of object files located outside the project.
-  The :prop_tgt:`IMPORTED_OBJECTS` target property (or its per-configuration
-  variant :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>`) specifies the locations of
-  object files on disk.
-  Additional usage requirements may be specified in ``INTERFACE_*`` properties.
+  ``OBJECT``
+    References a set of object files located outside the project.
+    The :prop_tgt:`IMPORTED_OBJECTS` target property (or its per-configuration
+    variant :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>`) specifies the locations of
+    object files on disk.
+    Additional usage requirements may be specified in ``INTERFACE_*``
+    properties.
 
-``INTERFACE``
-  Does not reference any library or object files on disk, but may
-  specify usage requirements in ``INTERFACE_*`` properties.
+  ``INTERFACE``
+    Does not reference any library or object files on disk, but may
+    specify usage requirements in ``INTERFACE_*`` properties.
 
-See documentation of the ``IMPORTED_*`` and ``INTERFACE_*`` properties
-for more information.
+  The options are:
+
+  ``GLOBAL``
+    Make the target name globally visible.
+
+No rules are generated to build imported targets, and the :prop_tgt:`IMPORTED`
+target property is ``True``.  Imported libraries are useful for convenient
+reference from commands like :command:`target_link_libraries`.
+
+Details about the imported library are specified by setting properties whose
+names begin in ``IMPORTED_`` and ``INTERFACE_``.  See documentation of
+such properties for more information.
 
 Alias Libraries
 ^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
+.. signature::
   add_library(<name> ALIAS <target>)
+  :target: ALIAS
 
-Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can be
-used to refer to ``<target>`` in subsequent commands.  The ``<name>`` does
-not appear in the generated buildsystem as a make target.  The ``<target>``
-may not be an ``ALIAS``.
+  Creates an :ref:`Alias Target <Alias Targets>`, such that ``<name>`` can be
+  used to refer to ``<target>`` in subsequent commands.  The ``<name>`` does
+  not appear in the generated buildsystem as a make target.  The ``<target>``
+  may not be an ``ALIAS``.
 
 .. versionadded:: 3.11
   An ``ALIAS`` can target a ``GLOBAL`` :ref:`Imported Target <Imported Targets>`
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst
index 02dd3986..557c858 100644
--- a/Help/command/add_test.rst
+++ b/Help/command/add_test.rst
@@ -27,9 +27,37 @@
 ``add_test`` options are:
 
 ``COMMAND``
-  Specify the test command-line.  If ``<command>`` specifies an executable
-  target created by :command:`add_executable`, it will automatically be
-  replaced by the location of the executable created at build time.
+  Specify the test command-line.
+
+  If ``<command>`` specifies an executable target created by
+  :command:`add_executable`:
+
+  * It will automatically be replaced by the location of the executable
+    created at build time.
+
+  * .. versionadded:: 3.3
+
+      The target's :prop_tgt:`CROSSCOMPILING_EMULATOR`, if set, will be
+      used to run the command on the host::
+
+        <emulator> <command>
+
+      .. versionchanged:: 3.29
+
+        The emulator is used only when
+        :variable:`cross-compiling <CMAKE_CROSSCOMPILING>`.
+        See policy :policy:`CMP0158`.
+
+  * .. versionadded:: 3.29
+
+      The target's :prop_tgt:`TEST_LAUNCHER`, if set, will be
+      used to launch the command::
+
+        <launcher> <command>
+
+      If the :prop_tgt:`CROSSCOMPILING_EMULATOR` is also set, both are used::
+
+        <launcher> <emulator> <command>
 
   The command may be specified using
   :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst
index dad0833..0d2f75e 100644
--- a/Help/command/cmake_host_system_information.rst
+++ b/Help/command/cmake_host_system_information.rst
@@ -265,7 +265,7 @@
 
 .. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes.
 
-.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html
+.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/latest/os-release.html
 .. _various distribution-specific files: http://linuxmafia.com/faq/Admin/release-files.html
 
 .. _Query Windows registry:
diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst
index 3af6b9c..4944f5c 100644
--- a/Help/command/cmake_language.rst
+++ b/Help/command/cmake_language.rst
@@ -15,6 +15,7 @@
   cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...])
   cmake_language(`SET_DEPENDENCY_PROVIDER`_ <command> SUPPORTED_METHODS <methods>...)
   cmake_language(`GET_MESSAGE_LOG_LEVEL`_ <out-var>)
+  cmake_language(`EXIT`_ <exit-code>)
 
 Introduction
 ^^^^^^^^^^^^
@@ -317,7 +318,7 @@
   implementation as part of its processing, it can do so by including the
   ``BYPASS_PROVIDER`` keyword as one of the arguments.
 
-``FETCHCONTENT_MAKEAVAILABE_SERIAL``
+``FETCHCONTENT_MAKEAVAILABLE_SERIAL``
   The ``<method-specific-args>`` will be everything passed to the
   :command:`FetchContent_Declare` call that corresponds to the requested
   dependency, with the following exceptions:
@@ -506,3 +507,20 @@
   If both the command line option and the variable are set, the command line
   option takes precedence. If neither are set, the default logging level
   is returned.
+
+Terminating Scripts
+^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.29
+
+.. signature::
+  cmake_language(EXIT <exit-code>)
+
+  Terminate the current :option:`cmake -P` script and exit with ``<exit-code>``.
+
+  This command works only in :ref:`script mode <Script Processing Mode>`.
+
+  The ``<exit-code>`` should be non-negative.
+  If ``<exit-code>`` is negative then the behavior
+  is unspecified (e.g., on Windows the error code -1
+  becomes ``0xffffffff``, and on Linux it becomes ``255``).
diff --git a/Help/command/configure_file.rst b/Help/command/configure_file.rst
index 07dc2e1..7200c24 100644
--- a/Help/command/configure_file.rst
+++ b/Help/command/configure_file.rst
@@ -1,6 +1,10 @@
 configure_file
 --------------
 
+.. only:: html
+
+   .. contents::
+
 Copy a file to another location and modify its contents.
 
 .. code-block:: cmake
@@ -11,11 +15,79 @@
                  [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
                  [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
 
-Copies an ``<input>`` file to an ``<output>`` file and substitutes
-variable values referenced as ``@VAR@``, ``${VAR}``, ``$CACHE{VAR}`` or
-``$ENV{VAR}`` in the input file content.  Each variable reference will be
-replaced with the current value of the variable, or the empty string if
-the variable is not defined.  Furthermore, input lines of the form
+Copies an ``<input>`` file to an ``<output>`` file while performing
+`transformations`_ of the input file content.
+
+If the input file is modified the build system will re-run CMake to
+re-configure the file and generate the build system again.
+The generated file is modified and its timestamp updated on subsequent
+cmake runs only if its content is changed.
+
+Options
+^^^^^^^
+
+The options are:
+
+``<input>``
+  Path to the input file.  A relative path is treated with respect to
+  the value of :variable:`CMAKE_CURRENT_SOURCE_DIR`.  The input path
+  must be a file, not a directory.
+
+``<output>``
+  Path to the output file or directory.  A relative path is treated
+  with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
+  If the path names an existing directory the output file is placed
+  in that directory with the same file name as the input file.
+  If the path contains non-existent directories, they are created.
+
+``NO_SOURCE_PERMISSIONS``
+  .. versionadded:: 3.19
+
+  Do not transfer the permissions of the input file to the output file.
+  The copied file permissions default to the standard 644 value
+  (-rw-r--r--).
+
+``USE_SOURCE_PERMISSIONS``
+  .. versionadded:: 3.20
+
+  Transfer the permissions of the input file to the output file.
+  This is already the default behavior if none of the three permissions-related
+  keywords are given (``NO_SOURCE_PERMISSIONS``, ``USE_SOURCE_PERMISSIONS``
+  or ``FILE_PERMISSIONS``).  The ``USE_SOURCE_PERMISSIONS`` keyword mostly
+  serves as a way of making the intended behavior clearer at the call site.
+
+``FILE_PERMISSIONS <permissions>...``
+  .. versionadded:: 3.20
+
+  Ignore the input file's permissions and use the specified ``<permissions>``
+  for the output file instead.
+
+``COPYONLY``
+  Copy the file without replacing any variable references or other
+  content.  This option may not be used with ``NEWLINE_STYLE``.
+
+``ESCAPE_QUOTES``
+  Escape any substituted quotes with backslashes (C-style).
+
+``@ONLY``
+  Restrict variable replacement to references of the form ``@VAR@``.
+  This is useful for configuring scripts that use ``${VAR}`` syntax.
+
+``NEWLINE_STYLE <style>``
+  Specify the newline style for the output file.  Specify
+  ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
+  ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
+  This option may not be used with ``COPYONLY``.
+
+Transformations
+^^^^^^^^^^^^^^^
+
+:ref:`Variables <CMake Language Variables>` referenced in the input
+file content as ``@VAR@``, ``${VAR}``, ``$CACHE{VAR}``, and
+:ref:`environment variables <CMake Language Environment Variables>`
+referenced as ``$ENV{VAR}``, will each be replaced with the current value
+of the variable, or the empty string if the variable is not defined.
+Furthermore, input lines of the form
 
 .. code-block:: c
 
@@ -79,64 +151,6 @@
     #  define VAR
     #  define VAR 1
 
-If the input file is modified the build system will re-run CMake to
-re-configure the file and generate the build system again.
-The generated file is modified and its timestamp updated on subsequent
-cmake runs only if its content is changed.
-
-The arguments are:
-
-``<input>``
-  Path to the input file.  A relative path is treated with respect to
-  the value of :variable:`CMAKE_CURRENT_SOURCE_DIR`.  The input path
-  must be a file, not a directory.
-
-``<output>``
-  Path to the output file or directory.  A relative path is treated
-  with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
-  If the path names an existing directory the output file is placed
-  in that directory with the same file name as the input file.
-  If the path contains non-existent directories, they are created.
-
-``NO_SOURCE_PERMISSIONS``
-  .. versionadded:: 3.19
-
-  Do not transfer the permissions of the input file to the output file.
-  The copied file permissions default to the standard 644 value
-  (-rw-r--r--).
-
-``USE_SOURCE_PERMISSIONS``
-  .. versionadded:: 3.20
-
-  Transfer the permissions of the input file to the output file.
-  This is already the default behavior if none of the three permissions-related
-  keywords are given (``NO_SOURCE_PERMISSIONS``, ``USE_SOURCE_PERMISSIONS``
-  or ``FILE_PERMISSIONS``).  The ``USE_SOURCE_PERMISSIONS`` keyword mostly
-  serves as a way of making the intended behavior clearer at the call site.
-
-``FILE_PERMISSIONS <permissions>...``
-  .. versionadded:: 3.20
-
-  Ignore the input file's permissions and use the specified ``<permissions>``
-  for the output file instead.
-
-``COPYONLY``
-  Copy the file without replacing any variable references or other
-  content.  This option may not be used with ``NEWLINE_STYLE``.
-
-``ESCAPE_QUOTES``
-  Escape any substituted quotes with backslashes (C-style).
-
-``@ONLY``
-  Restrict variable replacement to references of the form ``@VAR@``.
-  This is useful for configuring scripts that use ``${VAR}`` syntax.
-
-``NEWLINE_STYLE <style>``
-  Specify the newline style for the output file.  Specify
-  ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
-  ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
-  This option may not be used with ``COPYONLY``.
-
 Example
 ^^^^^^^
 
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst
index cf20ade..4d56577 100644
--- a/Help/command/ctest_test.rst
+++ b/Help/command/ctest_test.rst
@@ -13,6 +13,8 @@
              [INCLUDE <include-regex>]
              [EXCLUDE_LABEL <label-exclude-regex>]
              [INCLUDE_LABEL <label-include-regex>]
+             [EXCLUDE_FROM_FILE <filename>]
+             [INCLUDE_FROM_FILE <filename>]
              [EXCLUDE_FIXTURE <regex>]
              [EXCLUDE_FIXTURE_SETUP <regex>]
              [EXCLUDE_FIXTURE_CLEANUP <regex>]
@@ -72,6 +74,16 @@
   Specify a regular expression matching test labels to include.
   Tests not matching this expression are excluded.
 
+``EXCLUDE_FROM_FILE <filename>``
+  .. versionadded:: 3.29
+
+  Do NOT run tests listed with their exact name in the given file.
+
+``INCLUDE_FROM_FILE <filename>``
+  .. versionadded:: 3.29
+
+  Only run the tests listed with their exact name in the given file.
+
 ``EXCLUDE_FIXTURE <regex>``
   .. versionadded:: 3.7
 
diff --git a/Help/command/define_property.rst b/Help/command/define_property.rst
index 5278e30..06f2823 100644
--- a/Help/command/define_property.rst
+++ b/Help/command/define_property.rst
@@ -74,6 +74,42 @@
   underscore. It is recommended that the property name have a prefix specific
   to the project.
 
+Property Redefinition
+^^^^^^^^^^^^^^^^^^^^^
+
+Once a property is defined for a particular type of scope, it cannot be
+redefined. Attempts to redefine an existing property by calling
+:command:`define_property` with the same scope type and property name
+will be silently ignored. Defining the same property name for two different
+kinds of scope is valid.
+
+:command:`get_property` can be used to determine whether a property is
+already defined for a particular kind of scope, and if so, to examine its
+definition. For example:
+
+.. code-block:: cmake
+
+  # Initial definition
+  define_property(TARGET PROPERTY MY_NEW_PROP
+    BRIEF_DOCS "My new custom property"
+  )
+
+  # Later examination
+  get_property(my_new_prop_exists
+    TARGET NONE
+    PROPERTY MY_NEW_PROP
+    DEFINED
+  )
+
+  if(my_new_prop_exists)
+    get_property(my_new_prop_docs
+      TARGET NONE
+      PROPERTY MY_NEW_PROP
+      BRIEF_DOCS
+    )
+    # ${my_new_prop_docs} is now set to "My new custom property"
+  endif()
+
 See Also
 ^^^^^^^^
 
diff --git a/Help/command/export.rst b/Help/command/export.rst
index cc927bc..f190df7 100644
--- a/Help/command/export.rst
+++ b/Help/command/export.rst
@@ -15,12 +15,13 @@
   export(`TARGETS`_ <target>... [...])
   export(`EXPORT`_ <export-name> [...])
   export(`PACKAGE`_ <PackageName>)
+  export(`SETUP`_ <export-name> [...])
 
 Exporting Targets
 ^^^^^^^^^^^^^^^^^
 
-.. _`export(TARGETS)`:
-.. _TARGETS:
+.. signature::
+  export(TARGETS <target>... [...])
 
 .. code-block:: cmake
 
@@ -62,7 +63,7 @@
 
 This signature requires all targets to be listed explicitly.  If a library
 target is included in the export, but a target to which it links is not
-included, the behavior is unspecified.  See the `export(EXPORT)`_ signature
+included, the behavior is unspecified.  See the :command:`export(EXPORT)` signature
 to automatically export the same targets from the build tree as
 :command:`install(EXPORT)` would from an install tree.
 
@@ -102,27 +103,33 @@
 Exporting Targets matching install(EXPORT)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. _`export(EXPORT)`:
-.. _EXPORT:
+.. signature::
+  export(EXPORT <export-name> [...])
 
 .. code-block:: cmake
 
   export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>]
-         [CXX_MODULES_DIRECTORY <directory>])
+         [CXX_MODULES_DIRECTORY <directory>] [EXPORT_PACKAGE_DEPENDENCIES])
 
 Creates a file ``<filename>`` that may be included by outside projects to
 import targets from the current project's build tree.  This is the same
-as the `export(TARGETS)`_ signature, except that the targets are not
+as the :command:`export(TARGETS)` signature, except that the targets are not
 explicitly listed.  Instead, it exports the targets associated with
 the installation export ``<export-name>``.  Target installations may be
 associated with the export ``<export-name>`` using the ``EXPORT`` option
 of the :command:`install(TARGETS)` command.
 
+``EXPORT_PACKAGE_DEPENDENCIES``
+  .. versionadded:: 3.29
+
+  Specify that :command:`find_dependency` calls should be exported. See
+  :command:`install(EXPORT)` for details on how this works.
+
 Exporting Packages
 ^^^^^^^^^^^^^^^^^^
 
-.. _`export(PACKAGE)`:
-.. _PACKAGE:
+.. signature::
+  export(PACKAGE <PackageName>)
 
 .. code-block:: cmake
 
@@ -149,3 +156,62 @@
   outside the source and build trees.  Set the
   :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` variable to add build directories
   to the CMake user package registry.
+
+Configuring Exports
+^^^^^^^^^^^^^^^^^^^
+
+.. signature::
+  export(SETUP <export-name> [...])
+
+.. code-block:: cmake
+
+  export(SETUP <export-name>
+         [PACKAGE_DEPENDENCY <dep>
+          [ENABLED (<bool-true>|<bool-false>|AUTO)]
+          [EXTRA_ARGS <args>...]
+         ] [...]
+         [TARGET <target>
+          [XCFRAMEWORK_LOCATION <location>]
+         ] [...]
+         )
+
+.. versionadded:: 3.29
+
+Configure the parameters of an export. The arguments are as follows:
+
+``PACKAGE_DEPENDENCY <dep>``
+  Specify a package dependency to configure. This changes how
+  :command:`find_dependency` calls are written during
+  :command:`export(EXPORT)` and :command:`install(EXPORT)`. ``<dep>`` is the
+  name of a package to export. This argument accepts the following additional
+  arguments:
+
+  ``ENABLED``
+    Manually control whether or not the dependency is exported. This accepts
+    the following values:
+
+    ``<bool-true>``
+      Any value that CMake recognizes as "true". Always export the dependency,
+      even if no exported targets depend on it. This can be used to manually
+      add :command:`find_dependency` calls to the export.
+
+    ``<bool-false>``
+      Any value that CMake recognizes as "false". Never export the dependency,
+      even if an exported target depends on it.
+
+    ``AUTO``
+      Only export the dependency if an exported target depends on it.
+
+  ``EXTRA_ARGS <args>``
+    Specify additional arguments to pass to :command:`find_dependency` after
+    the ``REQUIRED`` argument.
+
+``TARGET <target>``
+  Specify a target to configure in this export. This argument accepts the
+  following additional arguments:
+
+  ``XCFRAMEWORK_LOCATION``
+    Specify the location of an ``.xcframework`` which contains the library from
+    this target. If specified, the generated code will check to see if the
+    ``.xcframework`` exists, and if it does, it will use the ``.xcframework``
+    as its imported location instead of the installed library.
diff --git a/Help/command/file.rst b/Help/command/file.rst
index f9d1a79..4acdc7e 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -32,14 +32,14 @@
 
   `Writing`_
     file({`WRITE`_ | `APPEND`_} <filename> <content>...)
-    file({`TOUCH`_ | `TOUCH_NOCREATE`_} [<file>...])
+    file({`TOUCH`_ | `TOUCH_NOCREATE`_} <file>...)
     file(`GENERATE`_ OUTPUT <output-file> [...])
     file(`CONFIGURE`_ OUTPUT <output-file> CONTENT <content> [...])
 
   `Filesystem`_
-    file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] [<globbing-expr>...])
-    file(`MAKE_DIRECTORY`_ [<dir>...])
-    file({`REMOVE`_ | `REMOVE_RECURSE`_ } [<files>...])
+    file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] <globbing-expr>...)
+    file(`MAKE_DIRECTORY`_ <directories>...)
+    file({`REMOVE`_ | `REMOVE_RECURSE`_ } <files>...)
     file(`RENAME`_ <oldname> <newname> [...])
     file(`COPY_FILE`_ <oldname> <newname> [...])
     file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...])
@@ -80,7 +80,7 @@
   (``a`` through ``f``) are in lowercase.
 
 .. signature::
-  file(STRINGS <filename> <variable> [<options>...])
+  file(STRINGS <filename> <variable> <options>...)
 
   Parse a list of ASCII strings from ``<filename>`` and store it in
   ``<variable>``.  Binary data in the file are ignored.  Carriage return
@@ -113,6 +113,11 @@
       Consider only strings that match the given regular expression,
       as described under :ref:`string(REGEX) <Regex Specification>`.
 
+      .. versionchanged:: 3.29
+        Capture groups from the last match in the file are stored in
+        :variable:`CMAKE_MATCH_<n>`, similar to
+        :command:`string(REGEX MATCHALL)`.  See policy :policy:`CMP0159`.
+
     ``ENCODING <encoding-type>``
       .. versionadded:: 3.1
 
@@ -165,17 +170,17 @@
       [RESOLVED_DEPENDENCIES_VAR <deps_var>]
       [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
       [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>]
-      [EXECUTABLES [<executable_files>...]]
-      [LIBRARIES [<library_files>...]]
-      [MODULES [<module_files>...]]
-      [DIRECTORIES [<directories>...]]
+      [EXECUTABLES <executable_files>...]
+      [LIBRARIES <library_files>...]
+      [MODULES <module_files>...]
+      [DIRECTORIES <directories>...]
       [BUNDLE_EXECUTABLE <bundle_executable_file>]
-      [PRE_INCLUDE_REGEXES [<regexes>...]]
-      [PRE_EXCLUDE_REGEXES [<regexes>...]]
-      [POST_INCLUDE_REGEXES [<regexes>...]]
-      [POST_EXCLUDE_REGEXES [<regexes>...]]
-      [POST_INCLUDE_FILES [<files>...]]
-      [POST_EXCLUDE_FILES [<files>...]]
+      [PRE_INCLUDE_REGEXES <regexes>...]
+      [PRE_EXCLUDE_REGEXES <regexes>...]
+      [POST_INCLUDE_REGEXES <regexes>...]
+      [POST_EXCLUDE_REGEXES <regexes>...]
+      [POST_INCLUDE_FILES <files>...]
+      [POST_EXCLUDE_FILES <files>...]
       )
 
   Please note that this sub-command is not intended to be used in project mode.
@@ -210,7 +215,7 @@
       of paths that were found for that filename are stored in
       ``<conflicting_deps_prefix>_<filename>``.
 
-    ``EXECUTABLES <executable_files>``
+    ``EXECUTABLES <executable_files>...``
       List of executable files to read for dependencies. These are executables
       that are typically created with :command:`add_executable`, but they do
       not have to be created by CMake. On Apple platforms, the paths to these
@@ -218,14 +223,14 @@
       resolving the libraries. Specifying any kind of library (``STATIC``,
       ``MODULE``, or ``SHARED``) here will result in undefined behavior.
 
-    ``LIBRARIES <library_files>``
+    ``LIBRARIES <library_files>...``
       List of library files to read for dependencies. These are libraries that
       are typically created with :command:`add_library(SHARED)`, but they do
       not have to be created by CMake. Specifying ``STATIC`` libraries,
       ``MODULE`` libraries, or executables here will result in undefined
       behavior.
 
-    ``MODULES <module_files>``
+    ``MODULES <module_files>...``
       List of loadable module files to read for dependencies. These are modules
       that are typically created with :command:`add_library(MODULE)`, but they
       do not have to be created by CMake. They are typically used by calling
@@ -233,7 +238,7 @@
       Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables
       here will result in undefined behavior.
 
-    ``DIRECTORIES <directories>``
+    ``DIRECTORIES <directories>...``
       List of additional directories to search for dependencies. On Linux
       platforms, these directories are searched if the dependency is not found
       in any of the other usual paths. If it is found in such a directory, a
@@ -256,30 +261,30 @@
   The following arguments specify filters for including or excluding libraries
   to be resolved. See below for a full description of how they work.
 
-    ``PRE_INCLUDE_REGEXES <regexes>``
+    ``PRE_INCLUDE_REGEXES <regexes>...``
       List of pre-include regexes through which to filter the names of
       not-yet-resolved dependencies.
 
-    ``PRE_EXCLUDE_REGEXES <regexes>``
+    ``PRE_EXCLUDE_REGEXES <regexes>...``
       List of pre-exclude regexes through which to filter the names of
       not-yet-resolved dependencies.
 
-    ``POST_INCLUDE_REGEXES <regexes>``
+    ``POST_INCLUDE_REGEXES <regexes>...``
       List of post-include regexes through which to filter the names of
       resolved dependencies.
 
-    ``POST_EXCLUDE_REGEXES <regexes>``
+    ``POST_EXCLUDE_REGEXES <regexes>...``
       List of post-exclude regexes through which to filter the names of
       resolved dependencies.
 
-    ``POST_INCLUDE_FILES <files>``
+    ``POST_INCLUDE_FILES <files>...``
       .. versionadded:: 3.21
 
       List of post-include filenames through which to filter the names of
       resolved dependencies. Symlinks are resolved when attempting to match
       these filenames.
 
-    ``POST_EXCLUDE_FILES <files>``
+    ``POST_EXCLUDE_FILES <files>...``
       .. versionadded:: 3.21
 
       List of post-exclude filenames through which to filter the names of
@@ -486,8 +491,8 @@
   to update the file only when its content changes.
 
 .. signature::
-  file(TOUCH [<files>...])
-  file(TOUCH_NOCREATE [<files>...])
+  file(TOUCH <files>...)
+  file(TOUCH_NOCREATE <files>...)
 
   .. versionadded:: 3.12
 
@@ -638,10 +643,10 @@
 .. signature::
   file(GLOB <variable>
        [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
-       [<globbing-expressions>...])
+       <globbing-expressions>...)
   file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
        [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
-       [<globbing-expressions>...])
+       <globbing-expressions>...)
 
   Generate a list of files that match the ``<globbing-expressions>`` and
   store it into the ``<variable>``.  Globbing expressions are similar to
@@ -703,13 +708,13 @@
   ============== ======================================================
 
 .. signature::
-  file(MAKE_DIRECTORY [<directories>...])
+  file(MAKE_DIRECTORY <directories>...)
 
   Create the given directories and their parents as needed.
 
 .. signature::
-  file(REMOVE [<files>...])
-  file(REMOVE_RECURSE [<files>...])
+  file(REMOVE <files>...)
+  file(REMOVE_RECURSE <files>...)
 
   Remove the given files.  The ``REMOVE_RECURSE`` mode will remove the given
   files and directories, including non-empty directories. No error is emitted
@@ -1012,8 +1017,8 @@
 ^^^^^^^^
 
 .. signature::
-  file(DOWNLOAD <url> [<file>] [<options>...])
-  file(UPLOAD <file> <url> [<options>...])
+  file(DOWNLOAD <url> [<file>] <options>...)
+  file(UPLOAD <file> <url> <options>...)
 
   The ``DOWNLOAD`` subcommand downloads the given ``<url>`` to a local
   ``<file>``.  The ``UPLOAD`` mode uploads a local ``<file>`` to a given
diff --git a/Help/command/if.rst b/Help/command/if.rst
index 5d85a1f..a4ccd97 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -178,6 +178,46 @@
 
   False if the given path is an empty string.
 
+  .. note::
+    Prefer ``if(IS_READABLE)`` to check file readability.  ``if(EXISTS)``
+    may be changed in the future to to only check file existence.
+
+.. signature:: if(IS_READABLE <path-to-file-or-directory>)
+
+  .. versionadded:: 3.29
+
+  True if the named file or directory is readable.  Behavior
+  is well-defined only for explicit full paths (a leading ``~/`` is not
+  expanded as a home directory and is considered a relative path).
+  Resolves symbolic links, i.e. if the named file or directory is a
+  symbolic link, returns true if the target of the symbolic link is readable.
+
+  False if the given path is an empty string.
+
+.. signature:: if(IS_WRITABLE <path-to-file-or-directory>)
+
+  .. versionadded:: 3.29
+
+  True if the named file or directory is writable.  Behavior
+  is well-defined only for explicit full paths (a leading ``~/`` is not
+  expanded as a home directory and is considered a relative path).
+  Resolves symbolic links, i.e. if the named file or directory is a
+  symbolic link, returns true if the target of the symbolic link is writable.
+
+  False if the given path is an empty string.
+
+.. signature:: if(IS_EXECUTABLE <path-to-file-or-directory>)
+
+  .. versionadded:: 3.29
+
+  True if the named file or directory is executable.  Behavior
+  is well-defined only for explicit full paths (a leading ``~/`` is not
+  expanded as a home directory and is considered a relative path).
+  Resolves symbolic links, i.e. if the named file or directory is a
+  symbolic link, returns true if the target of the symbolic link is executable.
+
+  False if the given path is an empty string.
+
 .. signature:: if(<file1> IS_NEWER_THAN <file2>)
   :target: IS_NEWER_THAN
 
diff --git a/Help/command/install.rst b/Help/command/install.rst
index b0698dd..674b770 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -47,23 +47,26 @@
 
 ``DESTINATION <dir>``
   Specify the directory on disk to which a file will be installed.
-  Arguments can be relative or absolute paths.
+  ``<dir>`` should be a relative path.  An absolute path is allowed,
+  but not recommended.
 
-  If a relative path is given it is interpreted relative to the value
+  When a relative path is given it is interpreted relative to the value
   of the :variable:`CMAKE_INSTALL_PREFIX` variable.
   The prefix can be relocated at install time using the ``DESTDIR``
   mechanism explained in the :variable:`CMAKE_INSTALL_PREFIX` variable
   documentation.
 
-  If an absolute path (with a leading slash or drive letter) is given
-  it is used verbatim.
-
-  As absolute paths are not supported by :manual:`cpack <cpack(1)>` installer
-  generators, it is preferable to use relative paths throughout.
+  As absolute paths do not work with the ``cmake --install`` command's
+  :option:`--prefix <cmake--install --prefix>` option, or with the
+  :manual:`cpack <cpack(1)>` installer generators, it is strongly recommended
+  to use relative paths throughout for best support by package maintainers.
   In particular, there is no need to make paths absolute by prepending
   :variable:`CMAKE_INSTALL_PREFIX`; this prefix is used by default if
   the DESTINATION is a relative path.
 
+  If an absolute path (with a leading slash or drive letter) is given
+  it is used verbatim.
+
 ``PERMISSIONS <permission>...``
   Specify permissions for installed files.  Valid permissions are
   ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``, ``GROUP_READ``,
@@ -280,8 +283,8 @@
   instead of being able to rely on the above (see next example below).
 
   To make packages compliant with distribution filesystem layout policies, if
-  projects must specify a ``DESTINATION``, it is recommended that they use a
-  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  projects must specify a ``DESTINATION``, it is strongly recommended that they use
+  a path that begins with the appropriate relative :module:`GNUInstallDirs` variable.
   This allows package maintainers to control the install destination by setting
   the appropriate cache variables.  The following example shows a static library
   being installed to the default destination provided by
@@ -572,8 +575,8 @@
   ``DATA`` instead.
 
   To make packages compliant with distribution filesystem layout policies, if
-  projects must specify a ``DESTINATION``, it is recommended that they use a
-  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  projects must specify a ``DESTINATION``, it is strongly recommended that they use
+  a path that begins with the appropriate relative :module:`GNUInstallDirs` variable.
   This allows package maintainers to control the install destination by setting
   the appropriate cache variables.  The following example shows how to follow
   this advice while installing an image to a project-specific documentation
@@ -719,8 +722,8 @@
   ``DATA`` instead.
 
   To make packages compliant with distribution filesystem layout policies, if
-  projects must specify a ``DESTINATION``, it is recommended that they use a
-  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  projects must specify a ``DESTINATION``, it is strongly recommended that they use
+  a path that begins with the appropriate relative :module:`GNUInstallDirs` variable.
   This allows package maintainers to control the install destination by setting
   the appropriate cache variables.
 
@@ -784,7 +787,8 @@
             [CXX_MODULES_DIRECTORY <directory>]
             [EXPORT_LINK_INTERFACE_LIBRARIES]
             [COMPONENT <component>]
-            [EXCLUDE_FROM_ALL])
+            [EXCLUDE_FROM_ALL]
+            [EXPORT_PACKAGE_DEPENDENCIES])
     install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])
 
   The ``EXPORT`` form generates and installs a CMake file containing code to
@@ -848,6 +852,34 @@
     without this information, none of the C++ modules which are part of the
     targets in the export set will support being imported in consuming targets.
 
+  ``EXPORT_PACKAGE_DEPENDENCIES``
+    .. versionadded:: 3.29
+
+    Specify that :command:`find_dependency` calls should be exported. If this
+    argument is specified, CMake examines all targets in the export set and
+    gathers their ``INTERFACE`` link targets. If any such targets either were
+    found with :command:`find_package` or have the
+    :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` property set, and such package
+    dependency was not disabled by passing ``ENABLED OFF`` to
+    :command:`export(SETUP)`, then a :command:`find_dependency` call is
+    written with the target's corresponding package name, a ``REQUIRED``
+    argument, and any additional arguments specified by the ``EXTRA_ARGS``
+    argument of :command:`export(SETUP)`. Any package dependencies that were
+    manually specified by passing ``ENABLED ON`` to :command:`export(SETUP)`
+    are also added, even if the exported targets don't depend on any targets
+    from them.
+
+    The :command:`find_dependency` calls are written in the following order:
+
+    1. Any package dependencies that were listed in :command:`export(SETUP)`
+       are written in the order they were first specified, regardless of
+       whether or not they contain ``INTERFACE`` dependencies of the
+       exported targets.
+    2. Any package dependencies that contain ``INTERFACE`` link dependencies
+       of the exported targets and that were never specified in
+       :command:`export(SETUP)` are written in the order they were first
+       found.
+
   The ``EXPORT`` form is useful to help outside projects use targets built
   and installed by the current project.  For example, the code
 
diff --git a/Help/command/qt_wrap_cpp.rst b/Help/command/qt_wrap_cpp.rst
index ce11c2d..02f7232 100644
--- a/Help/command/qt_wrap_cpp.rst
+++ b/Help/command/qt_wrap_cpp.rst
@@ -8,7 +8,7 @@
   :module:`FindQt4` module provides the ``qt4_wrap_cpp()`` macro, which
   should be used instead for Qt 4 projects.  For projects using Qt 5 or
   later, use the equivalent macro provided by Qt itself (e.g. Qt 5 provides
-  ``qt5_wrap_cpp()``).
+  `qt5_wrap_cpp() <https://doc.qt.io/qt-5/qtcore-cmake-qt5-wrap-cpp.html>`_).
 
 Manually create Qt Wrappers.
 
diff --git a/Help/command/return.rst b/Help/command/return.rst
index bb6d87d..799d3e8 100644
--- a/Help/command/return.rst
+++ b/Help/command/return.rst
@@ -40,7 +40,7 @@
   .. code-block:: cmake
     :caption: CMakeLists.txt
 
-    cmake_version_required(VERSION 3.25)
+    cmake_minimum_required(VERSION 3.25)
     project(example)
 
     set(var1 "top-value")
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst
index 0255b4d9..f44a4e8 100644
--- a/Help/command/try_compile.rst
+++ b/Help/command/try_compile.rst
@@ -77,6 +77,7 @@
               [COMPILE_DEFINITIONS <defs>...]
               [LINK_OPTIONS <options>...]
               [LINK_LIBRARIES <libs>...]
+              [LINKER_LANGUAGE <lang>]
               [OUTPUT_VARIABLE <var>]
               [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
               [<LANG>_STANDARD <std>]
@@ -177,6 +178,9 @@
   If this option is specified, any ``-DLINK_LIBRARIES=...`` value
   given to the ``CMAKE_FLAGS`` option will be ignored.
 
+  .. versionadded:: 3.29
+    Alias targets to imported libraries are also supported.
+
 ``LINK_OPTIONS <options>...``
   .. versionadded:: 3.14
 
@@ -184,6 +188,14 @@
   set the :prop_tgt:`STATIC_LIBRARY_OPTIONS` target property in the generated
   project, depending on the :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` variable.
 
+``LINKER_LANGUAGE <lang>```
+  .. versionadded:: 3.29
+
+  Specify the :prop_tgt:`LINKER_LANGUAGE` target property of the generated
+  project.  When using multiple source files with different languages, set
+  this to the language of the source file containing the program entry point,
+  e.g., ``main``.
+
 ``LOG_DESCRIPTION <text>``
   .. versionadded:: 3.26
 
diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst
index 1b5087d..c466a81 100644
--- a/Help/command/try_run.rst
+++ b/Help/command/try_run.rst
@@ -67,6 +67,7 @@
           [COMPILE_DEFINITIONS <defs>...]
           [LINK_OPTIONS <options>...]
           [LINK_LIBRARIES <libs>...]
+          [LINKER_LANGUAGE <lang>]
           [COMPILE_OUTPUT_VARIABLE <var>]
           [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
           [<LANG>_STANDARD <std>]
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst
index 705ec9c..23436de 100644
--- a/Help/cpack_gen/deb.rst
+++ b/Help/cpack_gen/deb.rst
@@ -69,28 +69,32 @@
  :Mandatory: Yes
  :Default: ``<CPACK_PACKAGE_FILE_NAME>[-<component>].deb``
 
- This may be set to ``DEB-DEFAULT`` to allow the CPack DEB generator to generate
- package file name by itself in deb format::
+ This may be set to:
 
-   <PackageName>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb
+ ``DEB-DEFAULT``
+   Tell CPack to automatically generate the package file name in deb format::
 
- Alternatively provided package file name must end
- with either ``.deb`` or ``.ipk`` suffix.
+     <PackageName>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb
 
- .. versionadded:: 3.10
-  ``.ipk`` suffix used by OPKG packaging system.
+   This setting recommended as the preferred behavior, but for backward
+   compatibility with the CPack DEB generator in CMake prior to version 3.6,
+   this is not the default.   Without this, duplicate names may occur.
+   Duplicate files get overwritten and it is up to the packager to set
+   the variables in a manner that will prevent such errors.
 
- .. note::
+ ``<file-name>[.deb]``
+   Use the given file name.
 
-   Preferred setting of this variable is ``DEB-DEFAULT`` but for backward
-   compatibility with the CPack DEB generator in CMake prior to version 3.6 this
-   feature is disabled by default.
+   .. versionchanged:: 3.29
 
- .. note::
+     The ``.deb`` suffix will be automatically added if the file name does
+     not end in ``.deb`` or ``.ipk``.  Previously the suffix was required.
 
-   By using non default filenames duplicate names may occur. Duplicate files
-   get overwritten and it is up to the packager to set the variables in a
-   manner that will prevent such errors.
+ ``<file-name>.ipk``
+   .. versionadded:: 3.10
+
+   Use the given file name.
+   The ``.ipk`` suffix is used by the OPKG packaging system.
 
 .. variable:: CPACK_DEBIAN_PACKAGE_EPOCH
 
diff --git a/Help/cpack_gen/productbuild.rst b/Help/cpack_gen/productbuild.rst
index 48a9b44..ee8a03f 100644
--- a/Help/cpack_gen/productbuild.rst
+++ b/Help/cpack_gen/productbuild.rst
@@ -91,8 +91,9 @@
  .. versionadded:: 3.23
 
  This option enables more granular control over where the product may be
- installed. When it is set to true, a ``domains`` element of the following
- form will be added to the productbuild Distribution XML:
+ installed. When it is set to true (see policy :policy:`CMP0161`), a
+ ``domains`` element of the following form will be added to the
+ productbuild Distribution XML:
 
  .. code-block:: xml
 
diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst
index 7b91261..4a2ce5f 100644
--- a/Help/cpack_gen/rpm.rst
+++ b/Help/cpack_gen/rpm.rst
@@ -84,9 +84,18 @@
  :Default: ``<CPACK_PACKAGE_FILE_NAME>[-<component>].rpm`` with spaces
                replaced by '-'
 
- This may be set to ``RPM-DEFAULT`` to allow ``rpmbuild`` tool to generate package
- file name by itself.
- Alternatively provided package file name must end with ``.rpm`` suffix.
+ This may be set to:
+
+ ``RPM-DEFAULT``
+    Tell ``rpmbuild`` to automatically generate the package file name.
+
+ ``<file-name>[.rpm]``
+   Use the given file name.
+
+   .. versionchanged:: 3.29
+
+     The ``.rpm`` suffix will be automatically added if missing.
+     Previously the suffix was required.
 
  .. note::
 
diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst
index af01252..cb56c9d 100644
--- a/Help/cpack_gen/wix.rst
+++ b/Help/cpack_gen/wix.rst
@@ -119,7 +119,8 @@
 
  If this variable is set, the specified template will be used to generate
  the WiX wxs file.  This should be used if further customization of the
- output is required.
+ output is required. The template contents will override the effect of most
+ ``CPACK_WIX_`` variables.
 
  If this variable is not set, the default MSI template included with CMake
  will be used.
@@ -337,3 +338,31 @@
  of the installer. May for example be set to ``x64`` or ``arm64``.
 
  When unspecified, CPack will default to ``x64`` or ``x86``.
+
+.. variable:: CPACK_WIX_INSTALL_SCOPE
+
+ .. versionadded:: 3.29
+
+ This variable can be optionally set to specify the ``InstallScope``
+ of the installer:
+
+ ``perMachine`` (default)
+   Create an installer that installs for all users and requires
+   administrative privileges.  Start menu entries created by the
+   installer are visible to all users.
+
+ ``perUser``
+   Not yet supported. This is reserved for future use.
+
+ ``NONE``
+   Create an installer without any ``InstallScope`` attribute.
+
+   .. deprecated:: 3.29
+
+     This value is only for compatibility with the inconsistent behavior used
+     by CPack 3.28 and older.  The resulting installer requires administrative
+     privileges and installs into the system-wide ``ProgramFiles`` directory,
+     but the start menu entry and uninstaller registration are created only
+     for the current user.
+
+ See https://wixtoolset.org/docs/v3/xsd/wix/package/
diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst
index 81e0e6f..c904673 100644
--- a/Help/dev/maint.rst
+++ b/Help/dev/maint.rst
@@ -8,6 +8,13 @@
 
 .. contents:: Maintainer Processes:
 
+Governance
+==========
+
+CMake has no formal governance body.  Maintainers expect one another to
+cooperate constructively and make decisions in good faith.  In cases of
+disagreement, the chief maintainer retains final authority.
+
 Review a Merge Request
 ======================
 
@@ -332,15 +339,16 @@
   away from setting policies to OLD.
 
 Update the ``cmake_policy`` version range generated by ``install(EXPORT)``
-in ``cmExportFileGenerator::GeneratePolicyHeaderCode`` to end at the
+in ``cmExportFileGenerator::GeneratePolicyHeaderCode`` and
+``install_jar_exports`` in ``javaTargets.cmake.in`` to end at the
 previous release.  We use one release back since we now know all the
 policies added for that version.  Commit with a message such as::
 
   export: Increase maximum policy version in exported files to $prev
 
-  The files generated by `install(EXPORT)` and `export()` commands
-  are known to work with policies as of CMake $prev, so enable them
-  in sufficiently new CMake versions.
+  The files generated by `install(EXPORT)`, `export()`, and
+  `install_jar_exports()` commands are known to work with policies
+  as of CMake $prev, so enable them in sufficiently new CMake versions.
 
 Update the ``cmake_minimum_required`` version range in CMake itself:
 
diff --git a/Help/dev/try_compile-linker-language.rst b/Help/dev/try_compile-linker-language.rst
new file mode 100644
index 0000000..8482dee
--- /dev/null
+++ b/Help/dev/try_compile-linker-language.rst
@@ -0,0 +1,6 @@
+try_compile-linker-language
+---------------------------
+
+* The :command:`try_compile` and :command:`try_run` commands gained a
+  ``LINKER_LANGUAGE`` option to specify the :prop_tgt:`LINKER_LANGUAGE`
+  target property in the generated test project.
diff --git a/Help/envvar/CMAKE_BUILD_PARALLEL_LEVEL.rst b/Help/envvar/CMAKE_BUILD_PARALLEL_LEVEL.rst
index dbc589a..a50797c 100644
--- a/Help/envvar/CMAKE_BUILD_PARALLEL_LEVEL.rst
+++ b/Help/envvar/CMAKE_BUILD_PARALLEL_LEVEL.rst
@@ -8,6 +8,10 @@
 Specifies the maximum number of concurrent processes to use when building
 using the ``cmake --build`` command line
 :ref:`Build Tool Mode <Build Tool Mode>`.
+For example, if ``CMAKE_BUILD_PARALLEL_LEVEL`` is set to 8, the
+underlying build tool will execute up to 8 jobs concurrently as if
+``cmake --build`` were invoked with the
+:option:`--parallel 8 <cmake--build --parallel>` option.
 
 If this variable is defined empty the native build tool's default number is
 used.
diff --git a/Help/envvar/CMAKE_INSTALL_PREFIX.rst b/Help/envvar/CMAKE_INSTALL_PREFIX.rst
new file mode 100644
index 0000000..5c3e055
--- /dev/null
+++ b/Help/envvar/CMAKE_INSTALL_PREFIX.rst
@@ -0,0 +1,11 @@
+CMAKE_INSTALL_PREFIX
+--------------------
+
+.. versionadded:: 3.29
+
+.. include:: ENV_VAR.txt
+
+The ``CMAKE_INSTALL_PREFIX`` environment variable specifies a custom default
+value for the :variable:`CMAKE_INSTALL_PREFIX` variable in place of the
+default values specified by CMake itself. The value specified must be an
+absolute path to a directory.
diff --git a/Help/envvar/CMAKE_TEST_LAUNCHER.rst b/Help/envvar/CMAKE_TEST_LAUNCHER.rst
new file mode 100644
index 0000000..d620ce5
--- /dev/null
+++ b/Help/envvar/CMAKE_TEST_LAUNCHER.rst
@@ -0,0 +1,11 @@
+CMAKE_TEST_LAUNCHER
+-------------------
+
+.. versionadded:: 3.29
+
+.. include:: ENV_VAR.txt
+
+The default value for the :variable:`CMAKE_TEST_LAUNCHER` variable when there
+is no explicit configuration given on the first run while creating a new
+build tree.  On later runs in an existing build tree the value persists in
+the cache as :variable:`CMAKE_TEST_LAUNCHER`.
diff --git a/Help/envvar/CTEST_PARALLEL_LEVEL.rst b/Help/envvar/CTEST_PARALLEL_LEVEL.rst
index fd4936e..126bdb3 100644
--- a/Help/envvar/CTEST_PARALLEL_LEVEL.rst
+++ b/Help/envvar/CTEST_PARALLEL_LEVEL.rst
@@ -3,5 +3,9 @@
 
 .. include:: ENV_VAR.txt
 
-Specify the number of tests for CTest to run in parallel. See :manual:`ctest(1)`
-for more information on parallel test execution.
+Specify the number of tests for CTest to run in parallel.
+For example, if ``CTEST_PARALLEL_LEVEL`` is set to 8, CTest will run
+up to 8 tests concurrently as if ``ctest`` were invoked with the
+:option:`--parallel 8 <ctest --parallel>` option.
+
+See :manual:`ctest(1)` for more information on parallel test execution.
diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst
index 910eacb..d2dddf7 100644
--- a/Help/guide/tutorial/Adding Generator Expressions.rst
+++ b/Help/guide/tutorial/Adding Generator Expressions.rst
@@ -24,7 +24,7 @@
 
 Logical expressions are used to create conditional output. The basic
 expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the
-empty string, and ``<1:...>`` results in the content of ``...``.  They can also
+empty string, and ``$<1:...>`` results in the content of ``...``.  They can also
 be nested.
 
 Exercise 1 - Adding Compiler Warning Flags with Generator Expressions
diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst
index 18ced97..cfcfc67 100644
--- a/Help/guide/tutorial/Adding a Library.rst
+++ b/Help/guide/tutorial/Adding a Library.rst
@@ -145,10 +145,10 @@
 
   </details>
 
-Finally we need to specify the library's header file location. Modify
-:command:`target_include_directories` to add the ``MathFunctions`` subdirectory
-as an include directory so that the ``MathFunctions.h`` header file can be
-found.
+Finally we need to specify the library's header file location.
+Modify the existing :command:`target_include_directories` call
+to add the ``MathFunctions`` subdirectory as an include directory
+so that the ``MathFunctions.h`` header file can be found.
 
 .. raw:: html
 
diff --git a/Help/guide/tutorial/Step12/CMakeLists.txt b/Help/guide/tutorial/Step12/CMakeLists.txt
index 1d8b5a6..1ba4e31 100644
--- a/Help/guide/tutorial/Step12/CMakeLists.txt
+++ b/Help/guide/tutorial/Step12/CMakeLists.txt
@@ -97,7 +97,7 @@
 # generate the config file that includes the exports
 configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
   "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
-  INSTALL_DESTINATION "lib/cmake/example"
+  INSTALL_DESTINATION "lib/cmake/MathFunctions"
   NO_SET_AND_CHECK_MACRO
   NO_CHECK_REQUIRED_COMPONENTS_MACRO
   )
diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
index ffb2f35..74c553f 100644
--- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
@@ -14,9 +14,9 @@
               mysqrt.cxx
               )
 
-  # TODO 7: Link SqrtLibrary to tutorial_compiler_flags
+  # TODO 6: Link SqrtLibrary to tutorial_compiler_flags
 
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
-# TODO 6: Link MathFunctions to tutorial_compiler_flags
+# TODO 7: Link MathFunctions to tutorial_compiler_flags
diff --git a/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make b/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make
new file mode 100644
index 0000000..a17673a
--- /dev/null
+++ b/Help/manual/CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make
@@ -0,0 +1,2 @@
+test:
+	+ctest -j 8
diff --git a/Help/manual/cmake-compile-features.7.rst b/Help/manual/cmake-compile-features.7.rst
index 1e87ec6..fb93222 100644
--- a/Help/manual/cmake-compile-features.7.rst
+++ b/Help/manual/cmake-compile-features.7.rst
@@ -266,6 +266,7 @@
 * ``PGI``: PGI version 12.10+.
 * ``NVHPC``: NVIDIA HPC compilers version 11.0+.
 * ``TI``: Texas Instruments compiler.
+* ``TIClang``: Texas Instruments Clang-based compilers.
 * ``XL``: IBM XL version 10.1+.
 
 CMake is currently aware of the :prop_tgt:`C standards <C_STANDARD>` and
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index 55f07b7..3ab5935 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -50,12 +50,14 @@
    /envvar/CMAKE_GENERATOR_PLATFORM
    /envvar/CMAKE_GENERATOR_TOOLSET
    /envvar/CMAKE_INSTALL_MODE
+   /envvar/CMAKE_INSTALL_PREFIX
    /envvar/CMAKE_LANG_COMPILER_LAUNCHER
    /envvar/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES_EXCLUDE
    /envvar/CMAKE_LANG_LINKER_LAUNCHER
    /envvar/CMAKE_MSVCIDE_RUN_PATH
    /envvar/CMAKE_NO_VERBOSE
    /envvar/CMAKE_OSX_ARCHITECTURES
+   /envvar/CMAKE_TEST_LAUNCHER
    /envvar/CMAKE_TOOLCHAIN_FILE
    /envvar/DESTDIR
    /envvar/LDFLAGS
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index 88a7bab..c249ee2 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -431,7 +431,7 @@
 
   {
     "kind": "codemodel",
-    "version": { "major": 2, "minor": 6 },
+    "version": { "major": 2, "minor": 7 },
     "paths": {
       "source": "/path/to/top-level-source-dir",
       "build": "/path/to/top-level-build-dir"
@@ -998,6 +998,36 @@
       destination is available.  The value is an unsigned integer 0-based
       index into the ``backtraceGraph`` member's ``nodes`` array.
 
+``launchers``
+  Optional member that is present on executable targets that have
+  at least one launcher specified by the project.  The value is a
+  JSON array of entries corresponding to the specified launchers.
+  Each entry is a JSON object with members:
+
+  ``command``
+    A string specifying the path to the launcher on disk, represented
+    with forward slashes. If the file is inside the top-level source
+    directory then the path is specified relative to that directory.
+
+  ``arguments``
+    Optional member that is present when the launcher command has
+    arguments preceding the executable to be launched.  The value
+    is a JSON array of strings representing the arguments.
+
+  ``type``
+    A string specifying the type of launcher.  The value is one of
+    the following:
+
+    ``emulator``
+      An emulator for the target platform when cross-compiling.
+      See the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property.
+
+    ``test``
+      A start program for the execution of tests.
+      See the :prop_tgt:`TEST_LAUNCHER` target property.
+
+  This field was added in codemodel version 2.7.
+
 ``link``
   Optional member that is present for executables and shared library
   targets that link into a runtime binary.  The value is a JSON object
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index d4a43de..0f09218 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1053,6 +1053,10 @@
   ``1`` if CMake's compiler id of the C compiler matches any one
   of the entries in ``compiler_ids``, otherwise ``0``.
 
+  .. versionchanged:: 3.15
+    Multiple ``compiler_ids`` can be specified.
+    CMake 3.14 and earlier only accepted a single compiler ID.
+
 .. genex:: $<CXX_COMPILER_ID>
 
   CMake's compiler id of the CXX compiler used.
@@ -1063,6 +1067,10 @@
   ``1`` if CMake's compiler id of the CXX compiler matches any one
   of the entries in ``compiler_ids``, otherwise ``0``.
 
+  .. versionchanged:: 3.15
+    Multiple ``compiler_ids`` can be specified.
+    CMake 3.14 and earlier only accepted a single compiler ID.
+
 .. genex:: $<CUDA_COMPILER_ID>
 
   .. versionadded:: 3.15
@@ -1115,6 +1123,10 @@
   ``1`` if CMake's compiler id of the Fortran compiler matches any one
   of the entries in ``compiler_ids``, otherwise ``0``.
 
+  .. versionchanged:: 3.15
+    Multiple ``compiler_ids`` can be specified.
+    CMake 3.14 and earlier only accepted a single compiler ID.
+
 .. genex:: $<HIP_COMPILER_ID>
 
   .. versionadded:: 3.21
@@ -2321,10 +2333,13 @@
 
   Content of the install prefix when the target is exported via
   :command:`install(EXPORT)`, or when evaluated in the
-  :prop_tgt:`INSTALL_NAME_DIR` property, the ``INSTALL_NAME_DIR`` argument of
-  :command:`install(RUNTIME_DEPENDENCY_SET)`, the code argument of
-  :command:`install(CODE)`, or the file argument of :command:`install(SCRIPT)`,
-  and empty otherwise.
+  :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of
+  :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise.
+
+  .. versionchanged:: 3.27
+    Evaluates to the content of the install prefix
+    in the code argument of :command:`install(CODE)` or
+    the file argument of :command:`install(SCRIPT)`.
 
 Multi-level Expression Evaluation
 ---------------------------------
@@ -2395,6 +2410,13 @@
 
   A literal ``;``. Used to prevent list expansion on an argument with ``;``.
 
+.. genex:: $<QUOTE>
+
+  .. versionadded:: 3.30
+
+  A literal ``"``. Used to allow string literal quotes inside a generator expression.
+
+
 Deprecated Expressions
 ----------------------
 
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 46707ff..bb6cfd4 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,19 @@
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.29
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0161: CPACK_PRODUCTBUILD_DOMAINS defaults to true. </policy/CMP0161>
+   CMP0160: More read-only target properties now error when trying to set them. </policy/CMP0160>
+   CMP0159: file(STRINGS) with REGEX updates CMAKE_MATCH_<n>. </policy/CMP0159>
+   CMP0158: add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when cross-compiling. </policy/CMP0158>
+   CMP0157: Swift compilation mode is selected by an abstraction. </policy/CMP0157>
+   CMP0156: De-duplicate libraries on link lines based on linker capabilities. </policy/CMP0156>
+
 Policies Introduced by CMake 3.28
 =================================
 
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 0ff2f2a..0d1046a 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -129,7 +129,9 @@
    /prop_tgt/ARCHIVE_OUTPUT_DIRECTORY_CONFIG
    /prop_tgt/ARCHIVE_OUTPUT_NAME
    /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG
+   /prop_tgt/AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
    /prop_tgt/AUTOGEN_BUILD_DIR
+   /prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX
    /prop_tgt/AUTOGEN_ORIGIN_DEPENDS
    /prop_tgt/AUTOGEN_PARALLEL
    /prop_tgt/AUTOGEN_TARGET_DEPENDS
@@ -211,6 +213,7 @@
    /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD
    /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG
    /prop_tgt/EXPORT_COMPILE_COMMANDS
+   /prop_tgt/EXPORT_FIND_PACKAGE_NAME
    /prop_tgt/EXPORT_NAME
    /prop_tgt/EXPORT_NO_SYSTEM
    /prop_tgt/EXPORT_PROPERTIES
@@ -334,6 +337,7 @@
    /prop_tgt/LINK_SEARCH_START_STATIC
    /prop_tgt/LINK_WHAT_YOU_USE
    /prop_tgt/LINKER_LANGUAGE
+   /prop_tgt/LINKER_TYPE
    /prop_tgt/LOCATION
    /prop_tgt/LOCATION_CONFIG
    /prop_tgt/MACHO_COMPATIBILITY_VERSION
@@ -389,11 +393,13 @@
    /prop_tgt/STATIC_LIBRARY_FLAGS_CONFIG
    /prop_tgt/STATIC_LIBRARY_OPTIONS
    /prop_tgt/SUFFIX
+   /prop_tgt/Swift_COMPILATION_MODE
    /prop_tgt/Swift_DEPENDENCIES_FILE
    /prop_tgt/Swift_LANGUAGE_VERSION
    /prop_tgt/Swift_MODULE_DIRECTORY
    /prop_tgt/Swift_MODULE_NAME
    /prop_tgt/SYSTEM
+   /prop_tgt/TEST_LAUNCHER
    /prop_tgt/TYPE
    /prop_tgt/UNITY_BUILD
    /prop_tgt/UNITY_BUILD_BATCH_SIZE
diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst
index e5a39f5..27230c8 100644
--- a/Help/manual/cmake-qt.7.rst
+++ b/Help/manual/cmake-qt.7.rst
@@ -10,34 +10,39 @@
 Introduction
 ============
 
-CMake can find and use Qt 4 and Qt 5 libraries.  The Qt 4 libraries are found
-by the :module:`FindQt4` find-module shipped with CMake, whereas the
-Qt 5 libraries are found using "Config-file Packages" shipped with Qt 5. See
-:manual:`cmake-packages(7)` for more information about CMake packages, and
-see `the Qt cmake manual <https://doc.qt.io/qt-5/cmake-manual.html>`_
-for your Qt version.
+CMake can find and use Qt 4, Qt 5 and Qt 6 libraries. The Qt 4 libraries are
+found by the :module:`FindQt4` find-module shipped with CMake, whereas the
+Qt 5 and Qt 6 libraries are found using "Config-file Packages" shipped with
+Qt 5 and Qt 6. See :manual:`cmake-packages(7)` for more information about CMake
+packages, and see `the Qt cmake manual`_ for your Qt version.
 
-Qt 4 and Qt 5 may be used together in the same
+.. _`the Qt cmake manual`: https://doc.qt.io/qt-6/cmake-manual.html
+
+Qt 4, Qt 5 and Qt 6 may be used together in the same
 :manual:`CMake buildsystem <cmake-buildsystem(7)>`:
 
 .. code-block:: cmake
 
-  cmake_minimum_required(VERSION 3.8.0 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
 
-  project(Qt4And5)
+  project(Qt4_5_6)
 
   set(CMAKE_AUTOMOC ON)
 
-  find_package(Qt5 COMPONENTS Widgets DBus REQUIRED)
+  find_package(Qt6 COMPONENTS Widgets DBus REQUIRED)
   add_executable(publisher publisher.cpp)
-  target_link_libraries(publisher Qt5::Widgets Qt5::DBus)
+  target_link_libraries(publisher Qt6::Widgets Qt6::DBus)
+
+  find_package(Qt5 COMPONENTS Gui DBus REQUIRED)
+  add_executable(subscriber1 subscriber1.cpp)
+  target_link_libraries(subscriber1 Qt5::Gui Qt5::DBus)
 
   find_package(Qt4 REQUIRED)
-  add_executable(subscriber subscriber.cpp)
-  target_link_libraries(subscriber Qt4::QtGui Qt4::QtDBus)
+  add_executable(subscriber2 subscriber2.cpp)
+  target_link_libraries(subscriber2 Qt4::QtGui Qt4::QtDBus)
 
-A CMake target may not link to both Qt 4 and Qt 5.  A diagnostic is issued if
-this is attempted or results from transitive target dependency evaluation.
+A CMake target may not link to more than one Qt version.  A diagnostic is issued
+if this is attempted or results from transitive target dependency evaluation.
 
 Qt Build Tools
 ==============
@@ -46,7 +51,7 @@
 meta-object code generation, ``uic`` for widget layout and population,
 and ``rcc`` for virtual file system content generation.  These tools may be
 automatically invoked by :manual:`cmake(1)` if the appropriate conditions
-are met.  The automatic tool invocation may be used with both Qt 4 and Qt 5.
+are met.  The automatic tool invocation may be used with Qt version 4 to 6.
 
 .. _`Qt AUTOMOC`:
 
@@ -158,7 +163,7 @@
 .. code-block:: cmake
 
   add_library(KI18n klocalizedstring.cpp)
-  target_link_libraries(KI18n Qt5::Core)
+  target_link_libraries(KI18n Qt6::Core)
 
   # KI18n uses the tr2i18n() function instead of tr().  That function is
   # declared in the klocalizedstring.h header.
@@ -213,25 +218,44 @@
 Source files can be excluded from :prop_tgt:`AUTORCC` processing by
 enabling :prop_sf:`SKIP_AUTORCC` or the broader :prop_sf:`SKIP_AUTOGEN`.
 
+.. _`<ORIGIN>_autogen`:
+
 The ``<ORIGIN>_autogen`` target
 ===============================
 
 The ``moc`` and ``uic`` tools are executed as part of a synthesized
-``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>` generated by
-CMake.  By default that ``<ORIGIN>_autogen`` target inherits the dependencies
+:ref:`<ORIGIN>_autogen` :command:`custom target <add_custom_target>` generated by
+CMake.  By default that :ref:`<ORIGIN>_autogen` target inherits the dependencies
 of the ``<ORIGIN>`` target (see :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`).
-Target dependencies may be added to the ``<ORIGIN>_autogen`` target by adding
+Target dependencies may be added to the :ref:`<ORIGIN>_autogen` target by adding
 them to the :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property.
 
+.. note::
+  If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or
+  :ref:`Makefile Generators`, see :ref:`<ORIGIN>_autogen_timestamp_deps`.
+
+.. _`<ORIGIN>_autogen_timestamp_deps`:
+
+The ``<ORIGIN>_autogen_timestamp_deps`` target
+==============================================
+
+If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or
+:ref:`Makefile Generators`, the ``<ORIGIN>_autogen_timestamp_deps`` target is
+also created in addition to the :ref:`<ORIGIN>_autogen` target.  This target
+does not have any sources or commands to execute, but it has dependencies that
+were previously inherited by the pre-Qt 5.15 :ref:`<ORIGIN>_autogen` target.
+These dependencies will serve as a list of order-only dependencies for the
+custom command, without forcing the custom command to re-execute.
+
 Visual Studio Generators
 ========================
 
 When using the :manual:`Visual Studio generators <cmake-generators(7)>`, CMake
 generates a ``PRE_BUILD`` :command:`custom command <add_custom_command>`
-instead of the ``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>`
-(for :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`).
-This isn't always possible though and
-an ``<ORIGIN>_autogen`` :command:`custom target <add_custom_target>` is used,
+instead of the :ref:`<ORIGIN>_autogen`
+:command:`custom target <add_custom_target>` (for :prop_tgt:`AUTOMOC` and
+:prop_tgt:`AUTOUIC`).  This isn't always possible though and an
+:ref:`<ORIGIN>_autogen` :command:`custom target <add_custom_target>` is used,
 when either
 
 - the ``<ORIGIN>`` target depends on :prop_sf:`GENERATED` files which aren't
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index d9df773..8a5ab30 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -55,7 +55,6 @@
    /variable/CMAKE_EDIT_COMMAND
    /variable/CMAKE_EXECUTABLE_SUFFIX
    /variable/CMAKE_EXECUTABLE_SUFFIX_LANG
-   /variable/CMAKE_EXTRA_GENERATOR
    /variable/CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES
    /variable/CMAKE_FIND_DEBUG_MODE
    /variable/CMAKE_FIND_PACKAGE_NAME
@@ -74,6 +73,10 @@
    /variable/CMAKE_JOB_POOLS
    /variable/CMAKE_LANG_COMPILER_AR
    /variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT
+   /variable/CMAKE_LANG_COMPILER_LINKER
+   /variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT
+   /variable/CMAKE_LANG_COMPILER_LINKER_ID
+   /variable/CMAKE_LANG_COMPILER_LINKER_VERSION
    /variable/CMAKE_LANG_COMPILER_RANLIB
    /variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX
    /variable/CMAKE_LINK_LIBRARY_SUFFIX
@@ -111,8 +114,10 @@
    /variable/CMAKE_SOURCE_DIR
    /variable/CMAKE_STATIC_LIBRARY_PREFIX
    /variable/CMAKE_STATIC_LIBRARY_SUFFIX
+   /variable/CMAKE_Swift_COMPILATION_MODE
    /variable/CMAKE_Swift_MODULE_DIRECTORY
    /variable/CMAKE_Swift_NUM_THREADS
+   /variable/CMAKE_TEST_LAUNCHER
    /variable/CMAKE_TOOLCHAIN_FILE
    /variable/CMAKE_TWEAK_VERSION
    /variable/CMAKE_VERBOSE_MAKEFILE
@@ -126,6 +131,7 @@
    /variable/CMAKE_VS_PLATFORM_TOOLSET
    /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA
    /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR
+   /variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN
    /variable/CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE
    /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION
    /variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
@@ -169,8 +175,6 @@
    /variable/CMAKE_ABSOLUTE_DESTINATION_FILES
    /variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
    /variable/CMAKE_APPBUNDLE_PATH
-   /variable/CMAKE_AUTOMOC_RELAXED_MODE
-   /variable/CMAKE_BACKWARDS_COMPATIBILITY
    /variable/CMAKE_BUILD_TYPE
    /variable/CMAKE_CLANG_VFS_OVERLAY
    /variable/CMAKE_CODEBLOCKS_COMPILER_ID
@@ -198,8 +202,6 @@
    /variable/CMAKE_FIND_LIBRARY_PREFIXES
    /variable/CMAKE_FIND_LIBRARY_SUFFIXES
    /variable/CMAKE_FIND_NO_INSTALL_PREFIX
-   /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
-   /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
    /variable/CMAKE_FIND_PACKAGE_PREFER_CONFIG
    /variable/CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS
    /variable/CMAKE_FIND_PACKAGE_TARGETS_GLOBAL
@@ -251,6 +253,7 @@
    /variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES
    /variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName
    /variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
+   /variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY
    /variable/CMAKE_STAGING_PREFIX
    /variable/CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS
    /variable/CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE
@@ -336,15 +339,6 @@
    /variable/LINUX
    /variable/MINGW
    /variable/MSVC
-   /variable/MSVC10
-   /variable/MSVC11
-   /variable/MSVC12
-   /variable/MSVC14
-   /variable/MSVC60
-   /variable/MSVC70
-   /variable/MSVC71
-   /variable/MSVC80
-   /variable/MSVC90
    /variable/MSVC_IDE
    /variable/MSVC_TOOLSET_VERSION
    /variable/MSVC_VERSION
@@ -395,6 +389,8 @@
    /variable/CMAKE_APPLE_SILICON_PROCESSOR
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG
+   /variable/CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
+   /variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX
    /variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS
    /variable/CMAKE_AUTOGEN_PARALLEL
    /variable/CMAKE_AUTOGEN_USE_SYSTEM_INCLUDE
@@ -439,6 +435,7 @@
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_EXE_LINKER_FLAGS_INIT
+   /variable/CMAKE_EXPORT_FIND_PACKAGE_NAME
    /variable/CMAKE_FOLDER
    /variable/CMAKE_Fortran_FORMAT
    /variable/CMAKE_Fortran_MODULE_DIRECTORY
@@ -459,7 +456,6 @@
    /variable/CMAKE_INSTALL_RPATH_USE_LINK_PATH
    /variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION
    /variable/CMAKE_INTERPROCEDURAL_OPTIMIZATION_CONFIG
-   /variable/CMAKE_IOS_INSTALL_COMBINED
    /variable/CMAKE_LANG_CLANG_TIDY
    /variable/CMAKE_LANG_CLANG_TIDY_EXPORT_FIXES_DIR
    /variable/CMAKE_LANG_COMPILER_LAUNCHER
@@ -474,6 +470,8 @@
    /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
    /variable/CMAKE_LANG_LINKER_LAUNCHER
+   /variable/CMAKE_LANG_USING_LINKER_MODE
+   /variable/CMAKE_LANG_USING_LINKER_TYPE
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -490,6 +488,7 @@
    /variable/CMAKE_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_WHAT_YOU_USE
    /variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
+   /variable/CMAKE_LINKER_TYPE
    /variable/CMAKE_MACOSX_BUNDLE
    /variable/CMAKE_MACOSX_RPATH
    /variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG
@@ -534,7 +533,6 @@
    /variable/CMAKE_UNITY_BUILD
    /variable/CMAKE_UNITY_BUILD_BATCH_SIZE
    /variable/CMAKE_UNITY_BUILD_UNIQUE_ID
-   /variable/CMAKE_USE_RELATIVE_PATHS
    /variable/CMAKE_VERIFY_INTERFACE_HEADER_SETS
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VS_DEBUGGER_COMMAND
@@ -571,9 +569,6 @@
    /variable/CMAKE_C_EXTENSIONS
    /variable/CMAKE_C_STANDARD
    /variable/CMAKE_C_STANDARD_REQUIRED
-   /variable/CMAKE_COMPILER_IS_GNUCC
-   /variable/CMAKE_COMPILER_IS_GNUCXX
-   /variable/CMAKE_COMPILER_IS_GNUG77
    /variable/CMAKE_CUDA_ARCHITECTURES
    /variable/CMAKE_CUDA_COMPILE_FEATURES
    /variable/CMAKE_CUDA_EXTENSIONS
@@ -693,7 +688,6 @@
    /variable/CTEST_CUSTOM_TESTS_IGNORE
    /variable/CTEST_CUSTOM_WARNING_EXCEPTION
    /variable/CTEST_CUSTOM_WARNING_MATCH
-   /variable/CTEST_CVS_CHECKOUT
    /variable/CTEST_CVS_COMMAND
    /variable/CTEST_CVS_UPDATE_OPTIONS
    /variable/CTEST_DROP_LOCATION
@@ -722,7 +716,6 @@
    /variable/CTEST_P4_UPDATE_OPTIONS
    /variable/CTEST_RESOURCE_SPEC_FILE
    /variable/CTEST_RUN_CURRENT_SCRIPT
-   /variable/CTEST_SCP_COMMAND
    /variable/CTEST_SCRIPT_DIRECTORY
    /variable/CTEST_SITE
    /variable/CTEST_SOURCE_DIRECTORY
@@ -733,7 +726,6 @@
    /variable/CTEST_SVN_UPDATE_OPTIONS
    /variable/CTEST_TEST_LOAD
    /variable/CTEST_TEST_TIMEOUT
-   /variable/CTEST_TRIGGER_SITE
    /variable/CTEST_UPDATE_COMMAND
    /variable/CTEST_UPDATE_OPTIONS
    /variable/CTEST_UPDATE_VERSION_ONLY
@@ -786,3 +778,67 @@
    /variable/CMAKE_LANG_PLATFORM_ID
    /variable/CMAKE_NOT_USING_CONFIG_FLAGS
    /variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION
+
+Deprecated Variables that Provide Information
+=============================================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/CMAKE_EXTRA_GENERATOR
+
+Deprecated Variables that Change Behavior
+=========================================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/CMAKE_AUTOMOC_RELAXED_MODE
+   /variable/CMAKE_BACKWARDS_COMPATIBILITY
+   /variable/CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
+   /variable/CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
+
+Deprecated Variables that Describe the System
+=============================================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/MSVC10
+   /variable/MSVC11
+   /variable/MSVC12
+   /variable/MSVC14
+   /variable/MSVC60
+   /variable/MSVC70
+   /variable/MSVC71
+   /variable/MSVC80
+   /variable/MSVC90
+
+Deprecated Variables that Control the Build
+===========================================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/CMAKE_IOS_INSTALL_COMBINED
+   /variable/CMAKE_USE_RELATIVE_PATHS
+
+Deprecated Variables for Languages
+==================================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/CMAKE_COMPILER_IS_GNUCC
+   /variable/CMAKE_COMPILER_IS_GNUCXX
+   /variable/CMAKE_COMPILER_IS_GNUG77
+
+Deprecated Variables for CTest
+==============================
+
+.. toctree::
+   :maxdepth: 1
+
+   /variable/CTEST_CVS_CHECKOUT
+   /variable/CTEST_SCP_COMMAND
+   /variable/CTEST_TRIGGER_SITE
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 5223acb..621c005 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -891,6 +891,10 @@
     ``-`` will result in an error. Use ``--`` to indicate the end of options, in
     case a file starts with ``-``.
 
+  .. versionadded:: 3.29
+
+    ``cat`` can now print the standard input by passing the ``-`` argument.
+
 .. program:: cmake-E
 
 .. option:: chdir <dir> <cmd> [<arg>...]
@@ -1311,6 +1315,7 @@
 
   Write Windows registry value.
 
+.. _`Find-Package Tool Mode`:
 
 Run the Find-Package Tool
 =========================
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 5512c61..602a82d 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -234,6 +234,30 @@
  of the test's labels (i.e. the multiple ``-LE`` labels form an ``AND``
  relationship).  See `Label Matching`_.
 
+.. option:: --tests-from-file <filename>
+
+ .. versionadded:: 3.29
+
+ Run tests listed in the given file.
+
+ This option tells CTest to run the tests which are listed in the given
+ file. The file must contain one exact test name per line.
+ Lines can be commented out using a ``#``.
+ This option can be combined with the other options like
+ ``-R``, ``-E``, ``-L`` or ``-LE``.
+
+.. option:: --exclude-from-file <filename>
+
+ .. versionadded:: 3.29
+
+ Exclude tests listed in the given file.
+
+ This option tells CTest to NOT run the tests which are listed in the given
+ file. The file must contain one exact test name per line.
+ Lines can be commented out using a ``#``.
+ This option can be combined with the other options like
+ ``-R``, ``-E``, ``-L`` or ``-LE``.
+
 .. option:: -FA <regex>, --fixture-exclude-any <regex>
 
  Exclude fixtures matching ``<regex>`` from automatically adding any tests to
@@ -354,6 +378,8 @@
 
 .. option:: --test-dir <dir>
 
+ .. versionadded:: 3.20
+
  Specify the directory in which to look for tests, typically a CMake project
  build directory. If not specified, the current directory is used.
 
@@ -752,6 +778,16 @@
 
  This option will submit extra files to the dashboard.
 
+.. option:: --http-header <header>
+
+ .. versionadded:: 3.29
+
+ Append HTTP header when submitting to the dashboard.
+
+ This option will cause CTest to append the specified header
+ when submitting to the dashboard.
+ This option may be specified more than once.
+
 .. option:: --http1.0
 
  Submit using `HTTP 1.0`.
@@ -1847,6 +1883,31 @@
 not be specified with the ``--resource-spec-file`` argument or the
 :variable:`CTEST_RESOURCE_SPEC_FILE` variable.
 
+.. _`ctest-job-server-integration`:
+
+Job Server Integration
+======================
+
+.. versionadded:: 3.29
+
+On POSIX systems, when running under the context of a `Job Server`_,
+CTest shares its job slots.  This is independent of the :prop_test:`PROCESSORS`
+test property, which still counts against CTest's :option:`-j <ctest -j>`
+parallel level.  CTest acquires exactly one token from the job server before
+running each test, and returns it when the test finishes.
+
+For example, consider the ``Makefile``:
+
+.. literalinclude:: CTEST_EXAMPLE_MAKEFILE_JOB_SERVER.make
+  :language: make
+
+When invoked via ``make -j 2 test``, ``ctest`` connects to the job server,
+acquires a token for each test, and runs at most 2 tests concurrently.
+
+On Windows systems, job server integration is not yet implemented.
+
+.. _`Job Server`: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html
+
 See Also
 ========
 
diff --git a/Help/policy/CMP0001.rst b/Help/policy/CMP0001.rst
index 6fa64d9..e06f2a1 100644
--- a/Help/policy/CMP0001.rst
+++ b/Help/policy/CMP0001.rst
@@ -14,8 +14,8 @@
 ``CMAKE_BACKWARDS_COMPATIBILITY`` for projects written for CMake 2.4 and
 below.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0002.rst b/Help/policy/CMP0002.rst
index dc68d51..f50ddd5 100644
--- a/Help/policy/CMP0002.rst
+++ b/Help/policy/CMP0002.rst
@@ -21,8 +21,8 @@
 must simply have globally unique names (unless one uses the global
 property :prop_gbl:`ALLOW_DUPLICATE_CUSTOM_TARGETS` with a Makefiles generator).
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0003.rst b/Help/policy/CMP0003.rst
index dd90883..0a6b778 100644
--- a/Help/policy/CMP0003.rst
+++ b/Help/policy/CMP0003.rst
@@ -97,8 +97,8 @@
 target.  This avoids flooding users with messages for every target
 when setting the policy once will probably fix all targets.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0004.rst b/Help/policy/CMP0004.rst
index be6d307..74e3e66 100644
--- a/Help/policy/CMP0004.rst
+++ b/Help/policy/CMP0004.rst
@@ -19,8 +19,8 @@
 target is created by an :command:`add_executable` or :command:`add_library`
 command.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0005.rst b/Help/policy/CMP0005.rst
index 59567d5..3037a12 100644
--- a/Help/policy/CMP0005.rst
+++ b/Help/policy/CMP0005.rst
@@ -19,8 +19,8 @@
 See documentation of the ``COMPILE_DEFINITIONS`` target property for
 limitations of the escaping implementation.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0006.rst b/Help/policy/CMP0006.rst
index 181958b..31efebd 100644
--- a/Help/policy/CMP0006.rst
+++ b/Help/policy/CMP0006.rst
@@ -17,8 +17,8 @@
 behavior for this policy is to produce an error if a bundle target is installed
 without a ``BUNDLE DESTINATION``.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0007.rst b/Help/policy/CMP0007.rst
index 1006ed3..b95f36c 100644
--- a/Help/policy/CMP0007.rst
+++ b/Help/policy/CMP0007.rst
@@ -10,8 +10,8 @@
 elements.  The ``NEW`` behavior for this policy is to correctly count
 empty elements in a list.
 
-This policy was introduced in CMake version 2.6.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0008.rst b/Help/policy/CMP0008.rst
index 18ede82..bcb10d5 100644
--- a/Help/policy/CMP0008.rst
+++ b/Help/policy/CMP0008.rst
@@ -28,8 +28,8 @@
 it.  The ``NEW`` behavior for this policy is to trust the given path and
 pass it directly to the native build tool unchanged.
 
-This policy was introduced in CMake version 2.6.1.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0009.rst b/Help/policy/CMP0009.rst
index 27cfde0..c554522 100644
--- a/Help/policy/CMP0009.rst
+++ b/Help/policy/CMP0009.rst
@@ -14,8 +14,8 @@
 to follow the symlinks by default, but only if ``FOLLOW_SYMLINKS`` is given
 as an additional argument to the ``FILE`` command.
 
-This policy was introduced in CMake version 2.6.2.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.2
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0010.rst b/Help/policy/CMP0010.rst
index cfae498..442553d 100644
--- a/Help/policy/CMP0010.rst
+++ b/Help/policy/CMP0010.rst
@@ -13,8 +13,8 @@
 If :policy:`CMP0053` is set to ``NEW``, this policy has no effect
 and is treated as always being ``NEW``.
 
-This policy was introduced in CMake version 2.6.3.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0011.rst b/Help/policy/CMP0011.rst
index 257415c..976b3c3 100644
--- a/Help/policy/CMP0011.rst
+++ b/Help/policy/CMP0011.rst
@@ -18,8 +18,8 @@
 The ``NEW`` behavior for this policy is to allow the commands to do
 their default cmake_policy ``PUSH`` and ``POP``.
 
-This policy was introduced in CMake version 2.6.3.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.6.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0012.rst b/Help/policy/CMP0012.rst
index 17ec8d3..dd889d2 100644
--- a/Help/policy/CMP0012.rst
+++ b/Help/policy/CMP0012.rst
@@ -21,8 +21,8 @@
 for this policy is to recognize numbers and boolean constants without
 dereferencing variables with such names.
 
-This policy was introduced in CMake version 2.8.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0013.rst b/Help/policy/CMP0013.rst
index dbd67a1..4ee19cc 100644
--- a/Help/policy/CMP0013.rst
+++ b/Help/policy/CMP0013.rst
@@ -14,8 +14,8 @@
 behavior for this policy is to disallow duplicate binary directories
 with an error.
 
-This policy was introduced in CMake version 2.8.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0014.rst b/Help/policy/CMP0014.rst
index 331dde5..1487bc4 100644
--- a/Help/policy/CMP0014.rst
+++ b/Help/policy/CMP0014.rst
@@ -10,8 +10,8 @@
 The ``OLD`` behavior for this policy is to silently ignore the problem.
 The ``NEW`` behavior for this policy is to report an error.
 
-This policy was introduced in CMake version 2.8.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0015.rst b/Help/policy/CMP0015.rst
index 90d5203..cd65c3f 100644
--- a/Help/policy/CMP0015.rst
+++ b/Help/policy/CMP0015.rst
@@ -12,8 +12,8 @@
 ``NEW`` behavior for this policy is to convert relative paths to absolute
 paths by appending the relative path to ``CMAKE_CURRENT_SOURCE_DIR``.
 
-This policy was introduced in CMake version 2.8.1.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0016.rst b/Help/policy/CMP0016.rst
index 026d02a..960361d 100644
--- a/Help/policy/CMP0016.rst
+++ b/Help/policy/CMP0016.rst
@@ -9,8 +9,8 @@
 wasn't a valid target.  In CMake 2.8.3 and above it reports an error
 in this case.
 
-This policy was introduced in CMake version 2.8.3.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0017.rst b/Help/policy/CMP0017.rst
index ca4664e..d06ad13 100644
--- a/Help/policy/CMP0017.rst
+++ b/Help/policy/CMP0017.rst
@@ -14,8 +14,8 @@
 behavior is to always prefer files from CMAKE_MODULE_PATH over files
 from the CMake modules directory.
 
-This policy was introduced in CMake version 2.8.4.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.4
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0018.rst b/Help/policy/CMP0018.rst
index 6248406..e4cd184 100644
--- a/Help/policy/CMP0018.rst
+++ b/Help/policy/CMP0018.rst
@@ -27,9 +27,8 @@
 ``CMAKE_SHARED_LIBRARY_<Lang>_FLAGS`` whether it is modified or not and
 honor the :prop_tgt:`POSITION_INDEPENDENT_CODE` target property.
 
-This policy was introduced in CMake version 2.8.9.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.9
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0019.rst b/Help/policy/CMP0019.rst
index 682dcdf..f3fa46a 100644
--- a/Help/policy/CMP0019.rst
+++ b/Help/policy/CMP0019.rst
@@ -15,8 +15,8 @@
 strict compatibility.  The ``NEW`` behavior for this policy is to leave
 the values untouched.
 
-This policy was introduced in CMake version 2.8.11.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.11
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0020.rst b/Help/policy/CMP0020.rst
index 6d27684..0a4de5c 100644
--- a/Help/policy/CMP0020.rst
+++ b/Help/policy/CMP0020.rst
@@ -20,8 +20,8 @@
 The ``NEW`` behavior for this policy is to link executables to ``qtmain.lib``
 automatically when they link to QtCore ``IMPORTED`` target.
 
-This policy was introduced in CMake version 2.8.11.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.11
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0021.rst b/Help/policy/CMP0021.rst
index 937b106..2c49c33 100644
--- a/Help/policy/CMP0021.rst
+++ b/Help/policy/CMP0021.rst
@@ -14,8 +14,8 @@
 policy is to issue a ``FATAL_ERROR`` if ``INCLUDE_DIRECTORIES`` contains a
 relative path.
 
-This policy was introduced in CMake version 2.8.12.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0022.rst b/Help/policy/CMP0022.rst
index be60e37..c82a768 100644
--- a/Help/policy/CMP0022.rst
+++ b/Help/policy/CMP0022.rst
@@ -32,8 +32,8 @@
 property for in-build targets, and ignore the old properties matching
 ``(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?``.
 
-This policy was introduced in CMake version 2.8.12.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0023.rst b/Help/policy/CMP0023.rst
index 3c72c81..c863a91 100644
--- a/Help/policy/CMP0023.rst
+++ b/Help/policy/CMP0023.rst
@@ -28,8 +28,8 @@
 this policy is to not to allow mixing of the keyword and plain
 signatures.
 
-This policy was introduced in CMake version 2.8.12.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 2.8.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0024.rst b/Help/policy/CMP0024.rst
index 6e24b04..c9fd472 100644
--- a/Help/policy/CMP0024.rst
+++ b/Help/policy/CMP0024.rst
@@ -17,9 +17,8 @@
 an :command:`export` command.  The ``NEW`` behavior for this policy is not to
 allow including the result of an :command:`export` command.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0025.rst b/Help/policy/CMP0025.rst
index ba5e1e9..cfbed2a 100644
--- a/Help/policy/CMP0025.rst
+++ b/Help/policy/CMP0025.rst
@@ -18,10 +18,10 @@
 The ``OLD`` behavior for this policy is to use compiler id ``Clang``.  The
 ``NEW`` behavior for this policy is to use compiler id ``AppleClang``.
 
-This policy was introduced in CMake version 3.0.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly.  Unlike most policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0025 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0026.rst b/Help/policy/CMP0026.rst
index e08fd54..b2a5f25 100644
--- a/Help/policy/CMP0026.rst
+++ b/Help/policy/CMP0026.rst
@@ -22,8 +22,8 @@
 properties from build-targets.  The ``NEW`` behavior for this policy is to
 not to allow reading the :prop_tgt:`LOCATION` properties from build-targets.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0027.rst b/Help/policy/CMP0027.rst
index bf7b6a9..78c01f7 100644
--- a/Help/policy/CMP0027.rst
+++ b/Help/policy/CMP0027.rst
@@ -20,8 +20,8 @@
 the :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` of a generator-expression
 conditionally linked ``IMPORTED`` target does not exist.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0028.rst b/Help/policy/CMP0028.rst
index dcd39d8..18b17ee 100644
--- a/Help/policy/CMP0028.rst
+++ b/Help/policy/CMP0028.rst
@@ -20,8 +20,8 @@
 for this policy is to issue a ``FATAL_ERROR`` if a link dependency contains
 double-colons but is not an ``IMPORTED`` target or an ``ALIAS`` target.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0037.rst b/Help/policy/CMP0037.rst
index 9895fb0..f912a22 100644
--- a/Help/policy/CMP0037.rst
+++ b/Help/policy/CMP0037.rst
@@ -27,8 +27,8 @@
 The ``NEW`` behavior for this policy is to report an error
 if an add_* command is used with an invalid target name.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0038.rst b/Help/policy/CMP0038.rst
index 7fb2209..b0de8ca 100644
--- a/Help/policy/CMP0038.rst
+++ b/Help/policy/CMP0038.rst
@@ -11,8 +11,8 @@
 in their own link implementation.  The ``NEW`` behavior for this policy is to
 report an error if a target attempts to link to itself.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0039.rst b/Help/policy/CMP0039.rst
index 4b14e21..558c5c5 100644
--- a/Help/policy/CMP0039.rst
+++ b/Help/policy/CMP0039.rst
@@ -12,8 +12,8 @@
 report an error if an attempt is made to set the link libraries of a
 utility target.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0040.rst b/Help/policy/CMP0040.rst
index 0afe589..b6777ef 100644
--- a/Help/policy/CMP0040.rst
+++ b/Help/policy/CMP0040.rst
@@ -13,9 +13,8 @@
 an error if the target referenced in :command:`add_custom_command` is
 unknown or was defined outside the current directory.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or
-``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0041.rst b/Help/policy/CMP0041.rst
index 3b4df36..bf9e8a5 100644
--- a/Help/policy/CMP0041.rst
+++ b/Help/policy/CMP0041.rst
@@ -20,8 +20,8 @@
 an error if a generator expression appears in another location and the path is
 relative.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0042.rst b/Help/policy/CMP0042.rst
index 0877564..8fb8cb2 100644
--- a/Help/policy/CMP0042.rst
+++ b/Help/policy/CMP0042.rst
@@ -14,8 +14,8 @@
 the :prop_tgt:`INSTALL_NAME_DIR` and :variable:`CMAKE_INSTALL_NAME_DIR`
 variables.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0043.rst b/Help/policy/CMP0043.rst
index 05210ac..decd228 100644
--- a/Help/policy/CMP0043.rst
+++ b/Help/policy/CMP0043.rst
@@ -40,8 +40,8 @@
 compilation command. The ``NEW`` behavior for this policy is to ignore the content
 of the :prop_tgt:`COMPILE_DEFINITIONS_<CONFIG>` target property .
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0044.rst b/Help/policy/CMP0044.rst
index 6a4d040..36c2839 100644
--- a/Help/policy/CMP0044.rst
+++ b/Help/policy/CMP0044.rst
@@ -14,8 +14,8 @@
 for this policy is to perform a case-sensitive comparison with the value in
 the ``<LANG>_COMPILER_ID`` expression.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0045.rst b/Help/policy/CMP0045.rst
index 80e217b..257abfa 100644
--- a/Help/policy/CMP0045.rst
+++ b/Help/policy/CMP0045.rst
@@ -12,8 +12,8 @@
 for this policy is to issue a ``FATAL_ERROR`` if the command is called with a
 non-existent target.
 
-This policy was introduced in CMake version 3.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0046.rst b/Help/policy/CMP0046.rst
index bf78584..6e9bc72 100644
--- a/Help/policy/CMP0046.rst
+++ b/Help/policy/CMP0046.rst
@@ -11,9 +11,8 @@
 if non-existent dependencies are listed in the :command:`add_dependencies`
 command.
 
-This policy was introduced in CMake version 3.0.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it
-to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0047.rst b/Help/policy/CMP0047.rst
index 9588edd..e82d72c 100644
--- a/Help/policy/CMP0047.rst
+++ b/Help/policy/CMP0047.rst
@@ -19,10 +19,10 @@
 for the qcc and QCC compiler drivers. The ``NEW`` behavior for this policy
 is to use the ``QCC`` compiler id for those drivers.
 
-This policy was introduced in CMake version 3.0.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly.  Unlike most policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0047 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0048.rst b/Help/policy/CMP0048.rst
index e63ec01..6192136 100644
--- a/Help/policy/CMP0048.rst
+++ b/Help/policy/CMP0048.rst
@@ -16,9 +16,8 @@
 The ``NEW`` behavior for this policy is to set ``VERSION`` as documented by the
 :command:`project` command.
 
-This policy was introduced in CMake version 3.0.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0049.rst b/Help/policy/CMP0049.rst
index 49b20be..d12ef88 100644
--- a/Help/policy/CMP0049.rst
+++ b/Help/policy/CMP0049.rst
@@ -17,9 +17,8 @@
 the target sources.  The ``NEW`` behavior for this policy is to issue an error
 if such variables need to be expanded.
 
-This policy was introduced in CMake version 3.0.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0050.rst b/Help/policy/CMP0050.rst
index 27e7b1d..1b93773 100644
--- a/Help/policy/CMP0050.rst
+++ b/Help/policy/CMP0050.rst
@@ -12,9 +12,8 @@
 :command:`add_custom_command` SOURCE signatures.  The ``NEW`` behavior for this
 policy is to issue an error if such a signature is used.
 
-This policy was introduced in CMake version 3.0.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it to ``OLD`` or
-``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0051.rst b/Help/policy/CMP0051.rst
index 3558909..e049bba 100644
--- a/Help/policy/CMP0051.rst
+++ b/Help/policy/CMP0051.rst
@@ -20,9 +20,8 @@
 behavior for this policy is to include ``TARGET_OBJECTS`` expressions
 in the output.
 
-This policy was introduced in CMake version 3.1.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it
-to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0052.rst b/Help/policy/CMP0052.rst
index c75f9ab..c710262 100644
--- a/Help/policy/CMP0052.rst
+++ b/Help/policy/CMP0052.rst
@@ -21,9 +21,8 @@
 directory.  The ``NEW`` behavior for this
 policy is to issue an error if such a directory is used.
 
-This policy was introduced in CMake version 3.1.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it
-to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0053.rst b/Help/policy/CMP0053.rst
index 58500d6..5a6f5cf 100644
--- a/Help/policy/CMP0053.rst
+++ b/Help/policy/CMP0053.rst
@@ -44,9 +44,8 @@
 variable references and escape sequences.  The ``NEW`` behavior is to
 use the simpler variable expansion and escape sequence evaluation rules.
 
-This policy was introduced in CMake version 3.1.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0054.rst b/Help/policy/CMP0054.rst
index c7ae019..d22e8d9 100644
--- a/Help/policy/CMP0054.rst
+++ b/Help/policy/CMP0054.rst
@@ -46,9 +46,8 @@
 
   if("E" STREQUAL "")
 
-This policy was introduced in CMake version 3.1.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0055.rst b/Help/policy/CMP0055.rst
index 47cac8e..dc83863 100644
--- a/Help/policy/CMP0055.rst
+++ b/Help/policy/CMP0055.rst
@@ -13,9 +13,8 @@
 outside of loop contexts and ignores any arguments.  The ``NEW`` behavior for this
 policy is to issue an error if a misplaced break or any arguments are found.
 
-This policy was introduced in CMake version 3.2.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it to ``OLD`` or
-``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.2
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0056.rst b/Help/policy/CMP0056.rst
index 628a6a1..ca238e2 100644
--- a/Help/policy/CMP0056.rst
+++ b/Help/policy/CMP0056.rst
@@ -27,9 +27,11 @@
 :variable:`CMAKE_POLICY_DEFAULT_CMP0056 <CMAKE_POLICY_DEFAULT_CMP<NNNN>>`
 variable in the cache.
 
-This policy was introduced in CMake version 3.2.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.2
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0056 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0057.rst b/Help/policy/CMP0057.rst
index 76aebfb..07bc969 100644
--- a/Help/policy/CMP0057.rst
+++ b/Help/policy/CMP0057.rst
@@ -10,9 +10,8 @@
 The ``OLD`` behavior for this policy is to ignore the IN_LIST operator.
 The ``NEW`` behavior is to interpret the IN_LIST operator.
 
-This policy was introduced in CMake version 3.3.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0058.rst b/Help/policy/CMP0058.rst
index faab1cb..2b729e0 100644
--- a/Help/policy/CMP0058.rst
+++ b/Help/policy/CMP0058.rst
@@ -104,12 +104,12 @@
 behavior for this policy is to not generate these and instead
 require projects to specify custom command ``BYPRODUCTS`` explicitly.
 
-This policy was introduced in CMake version 3.3.
-CMake version |release| warns when it sees unknown dependencies in
-out-of-source build trees if the policy is not set and then uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-the policy to ``OLD`` or ``NEW`` explicitly.  The policy setting
-must be in scope at the end of the top-level ``CMakeLists.txt``
-file of the project and has global effect.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace::
+   warns when it sees unknown dependencies in out-of-source build trees
+.. include:: STANDARD_ADVICE.txt
+
+The policy setting must be in scope at the end of the top-level
+``CMakeLists.txt`` file of the project and has global effect.
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0059.rst b/Help/policy/CMP0059.rst
index 6317d05..4ac286d 100644
--- a/Help/policy/CMP0059.rst
+++ b/Help/policy/CMP0059.rst
@@ -13,9 +13,8 @@
 so far to the :command:`add_definitions` command.  The ``NEW`` behavior is
 to behave as a normal user-defined directory property.
 
-This policy was introduced in CMake version 3.3.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0060.rst b/Help/policy/CMP0060.rst
index 09257d1..8fff803 100644
--- a/Help/policy/CMP0060.rst
+++ b/Help/policy/CMP0060.rst
@@ -58,9 +58,11 @@
 The ``NEW`` behavior for this policy is to link libraries by full path even
 if they are in implicit link directories.
 
-This policy was introduced in CMake version 3.3.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0060 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0061.rst b/Help/policy/CMP0061.rst
index aca551d..22ec0d0 100644
--- a/Help/policy/CMP0061.rst
+++ b/Help/policy/CMP0061.rst
@@ -21,8 +21,8 @@
 calls in CTest.  The ``NEW`` behavior for this policy is to not
 add ``-i``.
 
-This policy was introduced in CMake version 3.3.  Unlike most policies,
-CMake version |release| does *not* warn when this policy is not set and
-simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0062.rst b/Help/policy/CMP0062.rst
index 01e93f0..e0cf71b 100644
--- a/Help/policy/CMP0062.rst
+++ b/Help/policy/CMP0062.rst
@@ -23,9 +23,8 @@
 an :command:`export()` command.  The ``NEW`` behavior for this policy is
 not to allow installing the result of an :command:`export()` command.
 
-This policy was introduced in CMake version 3.3.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy()` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0063.rst b/Help/policy/CMP0063.rst
index fec3ad8..1e1cbfa 100644
--- a/Help/policy/CMP0063.rst
+++ b/Help/policy/CMP0063.rst
@@ -22,9 +22,8 @@
 The ``NEW`` behavior for this policy is to honor the visibility properties
 for all target types.
 
-This policy was introduced in CMake version 3.3.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy()` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.3
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0064.rst b/Help/policy/CMP0064.rst
index 0c20c13..4fd873f 100644
--- a/Help/policy/CMP0064.rst
+++ b/Help/policy/CMP0064.rst
@@ -11,9 +11,8 @@
 The ``OLD`` behavior for this policy is to ignore the ``TEST`` operator.
 The ``NEW`` behavior is to interpret the ``TEST`` operator.
 
-This policy was introduced in CMake version 3.4.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy()` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.4
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0065.rst b/Help/policy/CMP0065.rst
index 8968b91..54034b1 100644
--- a/Help/policy/CMP0065.rst
+++ b/Help/policy/CMP0065.rst
@@ -20,9 +20,11 @@
 flags when linking executables if the :prop_tgt:`ENABLE_EXPORTS` target
 property is set to ``True``.
 
-This policy was introduced in CMake version 3.4.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.4
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0065 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0066.rst b/Help/policy/CMP0066.rst
index b08430f..fa4bc5c 100644
--- a/Help/policy/CMP0066.rst
+++ b/Help/policy/CMP0066.rst
@@ -20,9 +20,11 @@
 The ``NEW`` behavior of this policy is to honor config-specific flag
 variabldes like :variable:`CMAKE_<LANG>_FLAGS_DEBUG`.
 
-This policy was introduced in CMake version 3.7.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.7
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0066 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0067.rst b/Help/policy/CMP0067.rst
index 8358bb2..0696131 100644
--- a/Help/policy/CMP0067.rst
+++ b/Help/policy/CMP0067.rst
@@ -30,9 +30,11 @@
 The ``NEW`` behavior of this policy is to honor language standard
 setting variables.
 
-This policy was introduced in CMake version 3.8.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.8
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0067 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0068.rst b/Help/policy/CMP0068.rst
index 5d2a4b1..aad8c3a 100644
--- a/Help/policy/CMP0068.rst
+++ b/Help/policy/CMP0068.rst
@@ -29,9 +29,8 @@
 ``install_name`` on macOS.  The ``NEW`` behavior of this policy is to ignore
 the ``RPATH`` settings for ``install_name`` on macOS.
 
-This policy was introduced in CMake version 3.9.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.9
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0069.rst b/Help/policy/CMP0069.rst
index eafac45..97665e6 100644
--- a/Help/policy/CMP0069.rst
+++ b/Help/policy/CMP0069.rst
@@ -26,10 +26,9 @@
 The ``NEW`` behavior for this policy is to add IPO flags for the current
 compiler or produce an error if CMake does not know the flags.
 
-This policy was introduced in CMake version 3.9.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.9
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
 
diff --git a/Help/policy/CMP0070.rst b/Help/policy/CMP0070.rst
index c880d1f..33b5a97 100644
--- a/Help/policy/CMP0070.rst
+++ b/Help/policy/CMP0070.rst
@@ -19,9 +19,8 @@
 interpret relative paths with respect to the current source or binary
 directory of the caller.
 
-This policy was introduced in CMake version 3.10.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.10
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0071.rst b/Help/policy/CMP0071.rst
index 700d3b0..7e118ee 100644
--- a/Help/policy/CMP0071.rst
+++ b/Help/policy/CMP0071.rst
@@ -36,9 +36,8 @@
   set_property(SOURCE /path/to/file3.h PROPERTY SKIP_AUTOGEN ON)
   # ...
 
-This policy was introduced in CMake version 3.10.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.10
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0072.rst b/Help/policy/CMP0072.rst
index 1dcdfef..430cb5a 100644
--- a/Help/policy/CMP0072.rst
+++ b/Help/policy/CMP0072.rst
@@ -20,9 +20,8 @@
 ``LEGACY``.  The ``NEW`` behavior for this policy is to set
 ``OpenGL_GL_PREFERENCE`` to ``GLVND``.
 
-This policy was introduced in CMake version 3.11.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.11
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0073.rst b/Help/policy/CMP0073.rst
index 8f0345c..897092e 100644
--- a/Help/policy/CMP0073.rst
+++ b/Help/policy/CMP0073.rst
@@ -19,9 +19,8 @@
 The ``OLD`` behavior for this policy is to set ``<tgt>_LIB_DEPENDS`` cache
 entries.  The ``NEW`` behavior for this policy is to not set them.
 
-This policy was introduced in CMake version 3.12.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0074.rst b/Help/policy/CMP0074.rst
index 863bbb2..fff843f 100644
--- a/Help/policy/CMP0074.rst
+++ b/Help/policy/CMP0074.rst
@@ -17,9 +17,8 @@
 variables.  The ``NEW`` behavior for this policy is to use
 ``<PackageName>_ROOT`` variables.
 
-This policy was introduced in CMake version 3.12.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0075.rst b/Help/policy/CMP0075.rst
index 4213782..479c629 100644
--- a/Help/policy/CMP0075.rst
+++ b/Help/policy/CMP0075.rst
@@ -20,9 +20,8 @@
 in the include file check macros.  The ``NEW`` behavior of this policy is to
 honor ``CMAKE_REQUIRED_LIBRARIES`` in the include file check macros.
 
-This policy was introduced in CMake version 3.12.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.12
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0076.rst b/Help/policy/CMP0076.rst
index edca742..a30b4c9 100644
--- a/Help/policy/CMP0076.rst
+++ b/Help/policy/CMP0076.rst
@@ -20,9 +20,8 @@
 all relative source file paths unmodified.  The ``NEW`` behavior of this
 policy is to convert relative paths to absolute according to above rules.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0077.rst b/Help/policy/CMP0077.rst
index 482125a..f64f92f 100644
--- a/Help/policy/CMP0077.rst
+++ b/Help/policy/CMP0077.rst
@@ -50,10 +50,11 @@
 command, but note that there are some differences in ``NEW`` behavior
 between the two policies.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly within a project.  Use the :variable:`CMAKE_POLICY_DEFAULT_CMP0077
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+Use the :variable:`CMAKE_POLICY_DEFAULT_CMP0077
 <CMAKE_POLICY_DEFAULT_CMP\<NNNN\>>` variable to set the policy for
 a third-party project in a subdirectory without modifying it.
 
diff --git a/Help/policy/CMP0078.rst b/Help/policy/CMP0078.rst
index 89fdb0b..c4b8a64 100644
--- a/Help/policy/CMP0078.rst
+++ b/Help/policy/CMP0078.rst
@@ -18,9 +18,8 @@
   This is the default if not specified.
 * ``STANDARD``: target name matches specified name.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0079.rst b/Help/policy/CMP0079.rst
index 01aa08d..039ac7b 100644
--- a/Help/policy/CMP0079.rst
+++ b/Help/policy/CMP0079.rst
@@ -34,9 +34,8 @@
 keyword only.  The ``NEW`` behavior of this policy is to allow all such
 calls but use the new scoping rules.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0080.rst b/Help/policy/CMP0080.rst
index 789efea..cface96 100644
--- a/Help/policy/CMP0080.rst
+++ b/Help/policy/CMP0080.rst
@@ -19,9 +19,8 @@
 be included at configure time. The ``NEW`` behavior of this policy is to
 disallow such inclusion.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0081.rst b/Help/policy/CMP0081.rst
index d1573dd..b89a353 100644
--- a/Help/policy/CMP0081.rst
+++ b/Help/policy/CMP0081.rst
@@ -17,8 +17,8 @@
 this policy is to issue a ``FATAL_ERROR`` if :prop_tgt:`LINK_DIRECTORIES`
 contains a relative path.
 
-This policy was introduced in CMake version 3.13.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.  Use
-the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.13
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0082.rst b/Help/policy/CMP0082.rst
index 25c9580..2de0cd9 100644
--- a/Help/policy/CMP0082.rst
+++ b/Help/policy/CMP0082.rst
@@ -19,9 +19,11 @@
 behavior for this policy is to run all install rules in the order they are
 declared.
 
-This policy was introduced in CMake version 3.14.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0082 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0083.rst b/Help/policy/CMP0083.rst
index 7518ee3..2ba9e14 100644
--- a/Help/policy/CMP0083.rst
+++ b/Help/policy/CMP0083.rst
@@ -25,10 +25,9 @@
 :prop_tgt:`POSITION_INDEPENDENT_CODE` target property for executables will be
 honored at link time.
 
-This policy was introduced in CMake version 3.14. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does not warn when this policy is
-not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. Note::
 
diff --git a/Help/policy/CMP0084.rst b/Help/policy/CMP0084.rst
index 9547701..3b86a73 100644
--- a/Help/policy/CMP0084.rst
+++ b/Help/policy/CMP0084.rst
@@ -20,9 +20,8 @@
 :command:`find_package`. The ``NEW`` behavior is to pretend that it doesn't
 exist for :command:`find_package`.
 
-This policy was introduced in CMake version 3.14.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0085.rst b/Help/policy/CMP0085.rst
index d90c72f..783c644 100644
--- a/Help/policy/CMP0085.rst
+++ b/Help/policy/CMP0085.rst
@@ -15,9 +15,8 @@
 ``0`` if the first argument is empty. The ``NEW`` behavior is to return ``1``
 if the first argument is empty and the list contains an empty item.
 
-This policy was introduced in CMake version 3.14.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0086.rst b/Help/policy/CMP0086.rst
index 725b502..c1abffe 100644
--- a/Help/policy/CMP0086.rst
+++ b/Help/policy/CMP0086.rst
@@ -14,9 +14,8 @@
 The ``NEW`` behavior is to pass ``-module`` option to ``SWIG`` compiler if
 ``SWIG_MODULE_NAME`` is specified.
 
-This policy was introduced in CMake version 3.14.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0087.rst b/Help/policy/CMP0087.rst
index 4a65506..3ef2c60 100644
--- a/Help/policy/CMP0087.rst
+++ b/Help/policy/CMP0087.rst
@@ -23,9 +23,8 @@
 their own directory scope (e.g. from files brought in via :command:`include()`
 rather than :command:`add_subdirectory()`).
 
-This policy was introduced in CMake version 3.14.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0088.rst b/Help/policy/CMP0088.rst
index 1840a58..e53078c 100644
--- a/Help/policy/CMP0088.rst
+++ b/Help/policy/CMP0088.rst
@@ -23,9 +23,8 @@
 use the current binary directory for the ``WORKING_DIRECTORY`` and where
 to generate implicit files.
 
-This policy was introduced in CMake version 3.14.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.14
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0089.rst b/Help/policy/CMP0089.rst
index e3fc77a..5f548c0 100644
--- a/Help/policy/CMP0089.rst
+++ b/Help/policy/CMP0089.rst
@@ -21,10 +21,10 @@
 The ``OLD`` behavior for this policy is to use compiler id ``XL``.  The
 ``NEW`` behavior for this policy is to use compiler id ``XLClang``.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0089 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0090.rst b/Help/policy/CMP0090.rst
index 58dd999..df7726c 100644
--- a/Help/policy/CMP0090.rst
+++ b/Help/policy/CMP0090.rst
@@ -21,9 +21,8 @@
 The ``NEW`` behavior is for :command:`export(PACKAGE)` command to do nothing
 unless the :variable:`CMAKE_EXPORT_PACKAGE_REGISTRY` is enabled.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0091.rst b/Help/policy/CMP0091.rst
index ffc0ade..110e655 100644
--- a/Help/policy/CMP0091.rst
+++ b/Help/policy/CMP0091.rst
@@ -43,9 +43,8 @@
 The ``NEW`` behavior for this policy is to *not* place MSVC runtime
 library flags in the default cache entries and use the abstraction instead.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0092.rst b/Help/policy/CMP0092.rst
index 2f39830..03a9975 100644
--- a/Help/policy/CMP0092.rst
+++ b/Help/policy/CMP0092.rst
@@ -32,9 +32,8 @@
 for this policy is to *not* place MSVC warning flags in the default cache
 entries.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0093.rst b/Help/policy/CMP0093.rst
index 4a9955f..5f7ae7d 100644
--- a/Help/policy/CMP0093.rst
+++ b/Help/policy/CMP0093.rst
@@ -18,9 +18,8 @@
 policy is for :module:`FindBoost` to report ``Boost_VERSION`` in
 ``x.y.z`` format.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses the ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0094.rst b/Help/policy/CMP0094.rst
index 1b57658..1b88a22 100644
--- a/Help/policy/CMP0094.rst
+++ b/Help/policy/CMP0094.rst
@@ -16,9 +16,8 @@
 ``Python3_FIND_STRATEGY``, ``Python2_FIND_STRATEGY`` and
 ``Python_FIND_STRATEGY``.
 
-This policy was introduced in CMake version 3.15.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses the ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.15
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0095.rst b/Help/policy/CMP0095.rst
index ebdbab6..cbeffc7 100644
--- a/Help/policy/CMP0095.rst
+++ b/Help/policy/CMP0095.rst
@@ -24,9 +24,9 @@
 escape coincidental CMake syntax in ``RPATH`` entries when generating the
 intermediary ``cmake_install.cmake`` script.
 
-This policy was introduced in CMake version 3.16. CMake version |release| warns
-when the policy is not set and detected usage of CMake-like syntax and uses
-``OLD`` behavior. Use the :command:`cmake_policy` command to set it to ``OLD``
-or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.16
+.. |WARNS_OR_DOES_NOT_WARN| replace::
+   warns when it detects use of CMake-like syntax
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0096.rst b/Help/policy/CMP0096.rst
index 8fd6f72..8ea3784 100644
--- a/Help/policy/CMP0096.rst
+++ b/Help/policy/CMP0096.rst
@@ -19,9 +19,8 @@
 of this policy preserves the leading zeros in all components, such that
 version ``1.07.06`` remains unchanged.
 
-This policy was introduced in CMake version 3.16.  Unlike many policies, CMake
-version |release| does *not* warn when this policy is not set and simply uses
-the ``OLD`` behavior.  Use the :command:`cmake_policy` command to set it to
-``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.16
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst
index 24957d0..66a7b96 100644
--- a/Help/policy/CMP0097.rst
+++ b/Help/policy/CMP0097.rst
@@ -21,7 +21,8 @@
 The ``NEW`` behavior for this policy is for ``GIT_SUBMODULES`` when set to
 an empty string to initialize and update no git submodules.
 
-This policy was introduced in CMake version 3.16.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.16
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0098.rst b/Help/policy/CMP0098.rst
index e793380..42c900b 100644
--- a/Help/policy/CMP0098.rst
+++ b/Help/policy/CMP0098.rst
@@ -24,9 +24,8 @@
 use the current binary directory for the ``WORKING_DIRECTORY`` relative to
 which implicit files are generated unless provided as absolute path.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0099.rst b/Help/policy/CMP0099.rst
index 0c64949..c0db99d 100644
--- a/Help/policy/CMP0099.rst
+++ b/Help/policy/CMP0099.rst
@@ -18,9 +18,8 @@
 properties. The ``NEW`` behavior of this policy is to propagate interface link
 properties.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0100.rst b/Help/policy/CMP0100.rst
index 730fa82..c3b782b 100644
--- a/Help/policy/CMP0100.rst
+++ b/Help/policy/CMP0100.rst
@@ -34,9 +34,8 @@
     set_property(SOURCE /path/to/file2.hh PROPERTY SKIP_AUTOUIC ON)
     set_property(SOURCE /path/to/file3.hh PROPERTY SKIP_AUTOGEN ON)
 
-This policy was introduced in CMake version 3.17.0.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0101.rst b/Help/policy/CMP0101.rst
index 6781079..ceefcda 100644
--- a/Help/policy/CMP0101.rst
+++ b/Help/policy/CMP0101.rst
@@ -22,9 +22,8 @@
 The ``NEW`` behavior for this policy is to honor the ``BEFORE`` keyword in
 all cases.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0102.rst b/Help/policy/CMP0102.rst
index 08024bf..8590979 100644
--- a/Help/policy/CMP0102.rst
+++ b/Help/policy/CMP0102.rst
@@ -19,11 +19,11 @@
 The ``NEW`` behavior of this policy is to ignore variables which do not
 already exist in the cache.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.  See
-documentation of the :variable:`CMAKE_POLICY_WARNING_CMP0102
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the :variable:`CMAKE_POLICY_WARNING_CMP0102
 <CMAKE_POLICY_WARNING_CMP<NNNN>>` variable to control the warning.
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0103.rst b/Help/policy/CMP0103.rst
index b5f44d1..50bceba 100644
--- a/Help/policy/CMP0103.rst
+++ b/Help/policy/CMP0103.rst
@@ -16,9 +16,8 @@
 The ``NEW`` behavior of this policy is to raise an error on second call to
 :command:`export` command with same ``FILE`` without ``APPEND``.
 
-This policy was introduced in CMake version 3.18.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.18
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0104.rst b/Help/policy/CMP0104.rst
index b125729..9e8d222 100644
--- a/Help/policy/CMP0104.rst
+++ b/Help/policy/CMP0104.rst
@@ -29,10 +29,9 @@
 flags are passed to the compiler. This is intended to support packagers and
 the rare cases where full control over the passed flags is required.
 
-This policy was introduced in CMake version 3.18.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.18
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
 
diff --git a/Help/policy/CMP0105.rst b/Help/policy/CMP0105.rst
index aadc8d6..8726997 100644
--- a/Help/policy/CMP0105.rst
+++ b/Help/policy/CMP0105.rst
@@ -14,9 +14,8 @@
 The ``NEW`` behavior of this policy is to use the link options during the
 device link step.
 
-This policy was introduced in CMake version 3.18.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.18
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0106.rst b/Help/policy/CMP0106.rst
index 18a6635..5f7e078 100644
--- a/Help/policy/CMP0106.rst
+++ b/Help/policy/CMP0106.rst
@@ -14,8 +14,8 @@
 cache variables and find VTK documentation dependent packages. The ``NEW``
 behavior is to act as an empty module.
 
-This policy was introduced in CMake version 3.18.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.18
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0107.rst b/Help/policy/CMP0107.rst
index da5ae06..ece0b23 100644
--- a/Help/policy/CMP0107.rst
+++ b/Help/policy/CMP0107.rst
@@ -13,9 +13,8 @@
 
 The ``NEW`` behavior of this policy is to prevent target overwriting.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0108.rst b/Help/policy/CMP0108.rst
index 4d1091d..fbb72b3 100644
--- a/Help/policy/CMP0108.rst
+++ b/Help/policy/CMP0108.rst
@@ -13,9 +13,8 @@
 The ``NEW`` behavior of this policy is to prevent a target to link to itself
 through an ``ALIAS`` target.
 
-This policy was introduced in CMake version 3.17.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.17
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0109.rst b/Help/policy/CMP0109.rst
index f86287f..2073d72 100644
--- a/Help/policy/CMP0109.rst
+++ b/Help/policy/CMP0109.rst
@@ -17,8 +17,8 @@
 The ``NEW`` behavior for this policy is for ``find_program`` to require
 execute permission but not read permission.
 
-This policy was introduced in CMake version 3.19.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0110.rst b/Help/policy/CMP0110.rst
index 6977d41..ad9e096 100644
--- a/Help/policy/CMP0110.rst
+++ b/Help/policy/CMP0110.rst
@@ -21,6 +21,8 @@
 mentioned workaround).  The ``NEW`` behavior on the other hand allows names
 with whitespace and special characters for tests created by ``add_test``.
 
-This policy was introduced in CMake version 3.19.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0111.rst b/Help/policy/CMP0111.rst
index e327583..33a925b 100644
--- a/Help/policy/CMP0111.rst
+++ b/Help/policy/CMP0111.rst
@@ -20,8 +20,8 @@
 set.
 The ``NEW`` behavior is to raise an error.
 
-This policy was introduced in CMake version 3.19.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0112.rst b/Help/policy/CMP0112.rst
index 25c3896..2d21d96 100644
--- a/Help/policy/CMP0112.rst
+++ b/Help/policy/CMP0112.rst
@@ -34,9 +34,11 @@
 this policy is to not add a dependency on the evaluated target for the
 above generator expressions.
 
-This policy was introduced in CMake version 3.19.  Unlike many policies,
-CMake version |release| does *not* warn by default when this policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0112 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
diff --git a/Help/policy/CMP0113.rst b/Help/policy/CMP0113.rst
index 6f56902..1908727 100644
--- a/Help/policy/CMP0113.rst
+++ b/Help/policy/CMP0113.rst
@@ -36,8 +36,8 @@
 dependent targets.  The ``NEW`` behavior of this policy is to not duplicate
 custom commands in dependent targets.
 
-This policy was introduced in CMake version 3.19.  Unlike many policies,
-CMake version |release| does *not* warn when this policy is not set and
-simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0114.rst b/Help/policy/CMP0114.rst
index ae48478..f4ca7ef 100644
--- a/Help/policy/CMP0114.rst
+++ b/Help/policy/CMP0114.rst
@@ -79,7 +79,8 @@
 behavior for this policy is to use the above-documented behavior preferred
 by 3.19 and above.
 
-This policy was introduced in CMake version 3.19.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.19
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0115.rst b/Help/policy/CMP0115.rst
index 7f82c43..b11f97d 100644
--- a/Help/policy/CMP0115.rst
+++ b/Help/policy/CMP0115.rst
@@ -27,8 +27,8 @@
 to source files if they can't be found. The ``NEW`` behavior of this policy is
 to not append known extensions and require them to be explicit.
 
-This policy was introduced in CMake version 3.20.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0116.rst b/Help/policy/CMP0116.rst
index 18e5a96..b0fc896 100644
--- a/Help/policy/CMP0116.rst
+++ b/Help/policy/CMP0116.rst
@@ -33,9 +33,13 @@
 same directory with different values for ``CMP0116`` by setting the policy
 before each custom command.
 
-This policy was introduced in CMake version 3.20.  Unlike most policies,
-CMake version |release| does *not* warn by default when this policy is not set
-(unless ``DEPFILE`` is used in a subdirectory) and simply uses ``OLD``
-behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace::
+   does *not* warn by default (unless ``DEPFILE`` is used in a subdirectory)
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0116 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0117.rst b/Help/policy/CMP0117.rst
index 0c4dd30..e1a0ee9 100644
--- a/Help/policy/CMP0117.rst
+++ b/Help/policy/CMP0117.rst
@@ -35,9 +35,8 @@
 ``NEW`` behavior for this policy is to *not* place the MSVC ``/GR`` flag in
 the default cache entry.
 
-This policy was introduced in CMake version 3.20.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0118.rst b/Help/policy/CMP0118.rst
index aa7e0f7..00afadf 100644
--- a/Help/policy/CMP0118.rst
+++ b/Help/policy/CMP0118.rst
@@ -17,9 +17,11 @@
 visible from the directory scope for which it was set.  The ``NEW``
 behavior on the other hand allows it to be visible from any scope.
 
-This policy was introduced in CMake version 3.20.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior with regard
-to visibility of the ``GENERATED`` property.  However, CMake does warn
-about setting the ``GENERATED`` property to a non-boolean value.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace::
+   does *not* warn with regard to visibility of the ``GENERATED``
+   property, but does warn about setting the ``GENERATED`` property
+   to a non-boolean value,
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0119.rst b/Help/policy/CMP0119.rst
index 61c8bdc..78e9894 100644
--- a/Help/policy/CMP0119.rst
+++ b/Help/policy/CMP0119.rst
@@ -28,9 +28,8 @@
 The ``NEW`` behavior for this policy is to interpret the ``LANGUAGE <LANG>``
 property using its documented meaning to "compile as a ``<LANG>`` source".
 
-This policy was introduced in CMake version 3.20.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0120.rst b/Help/policy/CMP0120.rst
index 9d2f6c9..0ffeb0e 100644
--- a/Help/policy/CMP0120.rst
+++ b/Help/policy/CMP0120.rst
@@ -38,9 +38,9 @@
 :module:`WriteCompilerDetectionHeader` module to work.  The ``NEW``
 behavior is for inclusion of the module to fail as if it does not exist.
 
-This policy was introduced in CMake version 3.20.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.20
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
 
diff --git a/Help/policy/CMP0121.rst b/Help/policy/CMP0121.rst
index 326e7d5..6e08cb5 100644
--- a/Help/policy/CMP0121.rst
+++ b/Help/policy/CMP0121.rst
@@ -14,8 +14,8 @@
 ``2good4you`` is a ``2`` and ``not_an_integer`` is a ``0``. The ``NEW``
 behavior is for invalid indices to trigger an error.
 
-This policy was introduced in CMake version 3.21.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0122.rst b/Help/policy/CMP0122.rst
index 1ff8c48..0b2da6c 100644
--- a/Help/policy/CMP0122.rst
+++ b/Help/policy/CMP0122.rst
@@ -9,9 +9,8 @@
 default naming conventions. This policy provides compatibility with projects
 that expect the legacy behavior.
 
-This policy was introduced in CMake version 3.21.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0123.rst b/Help/policy/CMP0123.rst
index e09b5ec..85f23a4 100644
--- a/Help/policy/CMP0123.rst
+++ b/Help/policy/CMP0123.rst
@@ -25,8 +25,8 @@
 does not add compile or link options, and projects are responsible for
 setting correct options.
 
-This policy was introduced in CMake version 3.21.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0124.rst b/Help/policy/CMP0124.rst
index d5cde64..2c19cac 100644
--- a/Help/policy/CMP0124.rst
+++ b/Help/policy/CMP0124.rst
@@ -42,9 +42,8 @@
 Under the ``OLD`` behavior, this code prints ``var1: value`` and ``var2:``.
 Under the ``NEW`` behavior, this code prints only ``var1: value``.
 
-This policy was introduced in CMake version 3.21. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn when the policy
-is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0125.rst b/Help/policy/CMP0125.rst
index 0b1704e..8c5e23a 100644
--- a/Help/policy/CMP0125.rst
+++ b/Help/policy/CMP0125.rst
@@ -34,9 +34,8 @@
   the result variable, except where a relative path provided by a cache or
   non-cache variable cannot be resolved to an existing path.
 
-This policy was introduced in CMake version 3.21. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn when the policy
-is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0126.rst b/Help/policy/CMP0126.rst
index a389512..ca40709 100644
--- a/Help/policy/CMP0126.rst
+++ b/Help/policy/CMP0126.rst
@@ -24,14 +24,16 @@
 will *not* set the cache variable if a non-cache variable of the same name
 already exists and :policy:`CMP0077` is set to ``NEW``.
 
-Policy ``CMP0126`` was introduced in CMake version 3.21. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly
-within a project.  Use the :variable:`CMAKE_POLICY_DEFAULT_CMP0126
-<CMAKE_POLICY_DEFAULT_CMP\<NNNN\>>` variable to set the policy for
-a third-party project in a subdirectory without modifying it.
-Unlike many policies, CMake version |release| does *not* warn when the policy
-is not set and simply uses ``OLD`` behavior.  See documentation of the
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.21
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
+See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0126 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
 
+The :variable:`CMAKE_POLICY_DEFAULT_CMP0126 <CMAKE_POLICY_DEFAULT_CMP\<NNNN\>>`
+variable may be used to set the policy for a third-party project in a
+subdirectory without modifying it.
+
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0127.rst b/Help/policy/CMP0127.rst
index 2106110..3de4f5e 100644
--- a/Help/policy/CMP0127.rst
+++ b/Help/policy/CMP0127.rst
@@ -26,9 +26,8 @@
 Policy ``CMP0127`` provides compatibility for projects that have not
 been updated to expect the new behavior.
 
-This policy was introduced in CMake version 3.22.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.22
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0128.rst b/Help/policy/CMP0128.rst
index 604a146..7d2c537 100644
--- a/Help/policy/CMP0128.rst
+++ b/Help/policy/CMP0128.rst
@@ -61,9 +61,10 @@
 default :variable:`standard <CMAKE_<LANG>_STANDARD_DEFAULT>` and
 :variable:`extensions <CMAKE_<LANG>_EXTENSIONS_DEFAULT>`.
 
-Unlike many policies, CMake version |release| does *not* warn when the policy
-is not set and simply uses the ``OLD`` behavior. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.22
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0128 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0129.rst b/Help/policy/CMP0129.rst
index 31a26e5..08ef8d1 100644
--- a/Help/policy/CMP0129.rst
+++ b/Help/policy/CMP0129.rst
@@ -23,10 +23,10 @@
 :variable:`CMAKE_<LANG>_SIMULATE_ID` to ``GNU``, and
 :variable:`CMAKE_<LANG>_SIMULATE_VERSION` to the supported GNU compiler version.
 
-This policy was introduced in CMake version 3.23.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW`` explicitly.
-Unlike most policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.23
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0129 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0130.rst b/Help/policy/CMP0130.rst
index 0dd5623..79f1ebb 100644
--- a/Help/policy/CMP0130.rst
+++ b/Help/policy/CMP0130.rst
@@ -25,8 +25,8 @@
 :command:`while` conditions.  The ``NEW`` behavior for this
 policy is to diagnose errors in :command:`while` conditions.
 
-This policy was introduced in CMake version 3.24.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0131.rst b/Help/policy/CMP0131.rst
index d5430e4..8899943 100644
--- a/Help/policy/CMP0131.rst
+++ b/Help/policy/CMP0131.rst
@@ -23,9 +23,8 @@
 usage requirements.  The ``NEW`` behavior for this policy is to use
 the guarded content only for link dependencies.
 
-This policy was introduced in CMake version 3.24.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly.  Unlike many policies, CMake version |release| does *not*
-warn when this policy is not set, and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0132.rst b/Help/policy/CMP0132.rst
index fadbbdc..c2a90c2 100644
--- a/Help/policy/CMP0132.rst
+++ b/Help/policy/CMP0132.rst
@@ -18,9 +18,8 @@
 on the first run when a language is enabled. The ``NEW`` behavior for this
 policy does not set any such environment variables.
 
-This policy was introduced in CMake version 3.24. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0133.rst b/Help/policy/CMP0133.rst
index c19bcf9..3bccd15 100644
--- a/Help/policy/CMP0133.rst
+++ b/Help/policy/CMP0133.rst
@@ -21,10 +21,10 @@
 :variable:`CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE` by default.
 The ``NEW`` behavior for this policy is to not enable it by default.
 
-This policy was introduced in CMake version 3.24.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly. Unlike many policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn by default
+.. include:: STANDARD_ADVICE.txt
+
 See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0133 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
 variable to control the warning.
diff --git a/Help/policy/CMP0134.rst b/Help/policy/CMP0134.rst
index a94012c..986d58e 100644
--- a/Help/policy/CMP0134.rst
+++ b/Help/policy/CMP0134.rst
@@ -28,9 +28,8 @@
 The ``NEW`` behavior for this policy is to use registry views ``TARGET`` and
 ``BOTH`` as default.
 
-This policy was introduced in CMake version 3.24.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly. Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0135.rst b/Help/policy/CMP0135.rst
index 1c0c134..c8d9205 100644
--- a/Help/policy/CMP0135.rst
+++ b/Help/policy/CMP0135.rst
@@ -22,8 +22,8 @@
 this policy is to restore the timestamps from the archive. The ``NEW``
 behavior sets the timestamps of extracted contents to the time of extraction.
 
-This policy was introduced in CMake version 3.24.  CMake version |release|
-warns when the policy is not set and uses ``OLD`` behavior.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0136.rst b/Help/policy/CMP0136.rst
index 5414278..0980c87 100644
--- a/Help/policy/CMP0136.rst
+++ b/Help/policy/CMP0136.rst
@@ -42,9 +42,8 @@
 The ``NEW`` behavior for this policy is to *not* place Watcom runtime
 library flags in the default cache entries and use the abstraction instead.
 
-This policy was introduced in CMake version 3.24.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0137.rst b/Help/policy/CMP0137.rst
index ba3cb9c..3d879d1 100644
--- a/Help/policy/CMP0137.rst
+++ b/Help/policy/CMP0137.rst
@@ -25,9 +25,8 @@
 :variable:`CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES` variable may be set
 to suppress passing the platform variables through either signature.
 
-This policy was introduced in CMake version 3.24.  Use the
-:command:`cmake_policy` command to set this policy to ``OLD`` or ``NEW``
-explicitly. Unlike many policies, CMake version |release| does *not* warn
-by default when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0138.rst b/Help/policy/CMP0138.rst
index a86849d..f933b90 100644
--- a/Help/policy/CMP0138.rst
+++ b/Help/policy/CMP0138.rst
@@ -23,9 +23,8 @@
 for this policy is to use the values of those variables as
 compiler flags in the test project.
 
-This policy was introduced in CMake version 3.24. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0139.rst b/Help/policy/CMP0139.rst
index 5a0f4f7..640055d 100644
--- a/Help/policy/CMP0139.rst
+++ b/Help/policy/CMP0139.rst
@@ -9,9 +9,8 @@
 The ``OLD`` behavior for this policy is to ignore the ``PATH_EQUAL`` operator.
 The ``NEW`` behavior is to interpret the ``PATH_EQUAL`` operator.
 
-This policy was introduced in CMake version 3.24.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.24
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0140.rst b/Help/policy/CMP0140.rst
index dea8989..4236017 100644
--- a/Help/policy/CMP0140.rst
+++ b/Help/policy/CMP0140.rst
@@ -9,9 +9,8 @@
 command.
 The ``NEW`` behavior is to check the validity of the parameters.
 
-This policy was introduced in CMake version 3.25.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set
-it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.25
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0141.rst b/Help/policy/CMP0141.rst
index 970e41d..1aef848 100644
--- a/Help/policy/CMP0141.rst
+++ b/Help/policy/CMP0141.rst
@@ -47,9 +47,8 @@
 debug information format flags in the default cache entries and use
 the abstraction instead.
 
-This policy was introduced in CMake version 3.25.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.25
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0142.rst b/Help/policy/CMP0142.rst
index 1f928f0..e4b1440 100644
--- a/Help/policy/CMP0142.rst
+++ b/Help/policy/CMP0142.rst
@@ -19,9 +19,8 @@
 ``$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)`` to all library search paths.
 The ``NEW`` behavior is to not modify library search paths.
 
-This policy was introduced in CMake version 3.25.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.25
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0143.rst b/Help/policy/CMP0143.rst
index 24fdc27..b3bbb28 100644
--- a/Help/policy/CMP0143.rst
+++ b/Help/policy/CMP0143.rst
@@ -22,9 +22,10 @@
 
 This policy provides compatibility with projects that have not been updated
 to expect enabling of folders.  Enabling folders causes projects to appear
-differently in IDEs.  The policy was introduced in CMake version 3.26.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+differently in IDEs.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.26
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0144.rst b/Help/policy/CMP0144.rst
index 3959d96..81e622a 100644
--- a/Help/policy/CMP0144.rst
+++ b/Help/policy/CMP0144.rst
@@ -18,9 +18,8 @@
 The ``NEW`` behavior for this policy is to use ``<PACKAGENAME>_ROOT``
 variables.
 
-This policy was introduced in CMake version 3.27.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0145.rst b/Help/policy/CMP0145.rst
index bb1c02e..9b0d43f 100644
--- a/Help/policy/CMP0145.rst
+++ b/Help/policy/CMP0145.rst
@@ -22,9 +22,8 @@
 ``find_package(Dart)`` to load the deprecated modules.  The ``NEW``
 behavior is for uses of the modules to fail as if they do not exist.
 
-This policy was introduced in CMake version 3.27.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0146.rst b/Help/policy/CMP0146.rst
index c7cac22..7d6ba19 100644
--- a/Help/policy/CMP0146.rst
+++ b/Help/policy/CMP0146.rst
@@ -21,9 +21,8 @@
 load the deprecated module.  The ``NEW`` behavior is for uses of the
 module to fail as if it does not exist.
 
-This policy was introduced in CMake version 3.27.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0147.rst b/Help/policy/CMP0147.rst
index 0f25096..fd5dc5f 100644
--- a/Help/policy/CMP0147.rst
+++ b/Help/policy/CMP0147.rst
@@ -16,9 +16,8 @@
 The ``NEW`` behavior for this policy is to add ``BuildInParallel`` for
 VS 15.8 and newer.
 
-This policy was introduced in CMake version 3.27.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0148.rst b/Help/policy/CMP0148.rst
index 2f5c43d..c522c6a 100644
--- a/Help/policy/CMP0148.rst
+++ b/Help/policy/CMP0148.rst
@@ -21,9 +21,8 @@
 and ``find_package(PythonLibs)`` to load the deprecated modules.  The ``NEW``
 behavior is for uses of the modules to fail as if they do not exist.
 
-This policy was introduced in CMake version 3.27.  CMake version
-|release| warns when the policy is not set and uses ``OLD`` behavior.
-Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0149.rst b/Help/policy/CMP0149.rst
index a84149f..11e935d 100644
--- a/Help/policy/CMP0149.rst
+++ b/Help/policy/CMP0149.rst
@@ -46,9 +46,8 @@
 :variable:`CMAKE_SYSTEM_VERSION` if possible.  The ``NEW`` behavior
 for this policy is to ignore it.
 
-This policy was introduced in CMake version 3.27.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0150.rst b/Help/policy/CMP0150.rst
index fe646d9..e8c58ef 100644
--- a/Help/policy/CMP0150.rst
+++ b/Help/policy/CMP0150.rst
@@ -31,9 +31,9 @@
 If an appropriate remote cannot be determined from the above, a fatal error
 will be raised.
 
-This policy was introduced in CMake version 3.27.  CMake version |release|
-warns when a relative path is encountered and the policy is not set,
-falling back to using ``OLD`` behavior.  Use the :command:`cmake_policy`
-command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace::
+   warns when a relative path is encountered
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0151.rst b/Help/policy/CMP0151.rst
index c12f595..49b0811 100644
--- a/Help/policy/CMP0151.rst
+++ b/Help/policy/CMP0151.rst
@@ -20,9 +20,8 @@
 The ``NEW`` behavior for this policy is to add autogen include directory to
 the target's system include directories.
 
-This policy was introduced in CMake version 3.27.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.27
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0152.rst b/Help/policy/CMP0152.rst
index d7e8692..94109e3 100644
--- a/Help/policy/CMP0152.rst
+++ b/Help/policy/CMP0152.rst
@@ -14,7 +14,8 @@
 The ``NEW`` behavior for this policy is to resolve all symlinks before
 collapsing ``../`` components.
 
-This policy was introduced in CMake version 3.28.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.28
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0154.rst b/Help/policy/CMP0154.rst
index bf398af..89a4f67 100644
--- a/Help/policy/CMP0154.rst
+++ b/Help/policy/CMP0154.rst
@@ -51,9 +51,8 @@
 policy is to assume generated files are private in targets using file sets,
 and for :ref:`Ninja Generators` to produce more efficient build graphs.
 
-This policy was introduced in CMake version 3.28.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.28
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0155.rst b/Help/policy/CMP0155.rst
index 8b741cb..5397c0d 100644
--- a/Help/policy/CMP0155.rst
+++ b/Help/policy/CMP0155.rst
@@ -20,9 +20,8 @@
 assume that C++ 20 and newer files may import modules if the compiler
 understands how to scan for their dependencies, and need to be scanned.
 
-This policy was introduced in CMake version 3.28.  Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
-Unlike many policies, CMake version |release| does *not* warn
-when this policy is not set and simply uses ``OLD`` behavior.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.28
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0156.rst b/Help/policy/CMP0156.rst
new file mode 100644
index 0000000..6fa0f04
--- /dev/null
+++ b/Help/policy/CMP0156.rst
@@ -0,0 +1,46 @@
+CMP0156
+-------
+
+.. versionadded:: 3.29
+
+De-duplicate libraries on link lines based on linker capabilities.
+
+Traditional linkers maintain a set of undefined symbols during linking. The
+linker processes each file in the order in which it appears on the command
+line, until the set of undefined symbols becomes empty. An object file is
+linked into the output object when it is encountered, with its undefined
+symbols added to the set. Upon encountering an archive file a traditional
+linker searches the objects contained therein, and processes those that satisfy
+symbols in the unresolved set.
+
+Handling mutually dependent archives may be awkward when using a traditional
+linker. Archive files may have to be specified multiple times.
+
+Some linkers (for instance Apple or Windows linkers, as well as ``LLVM LLD``)
+records all symbols found in objects and archives as it iterates over command
+line arguments. When one of these linkers encounters an undefined symbol that
+can be resolved by an object file contained in a previously processed archive
+file, it immediately extracts and links it into the output object.
+
+CMake 3.28 and below may generate link lines that repeat static libraries as
+a traditional linker would need, even when using a linker does not need it.
+They may also de-duplicate shared libraries by keeping their last occurrence,
+which on Windows platforms can change DLL load order.
+
+CMake 3.29 and above prefer to apply different strategies based on linker
+capabilities.  So, when targeting Apple and Windows platforms, all
+libraries are de-duplicated.  Moreover, on Windows platforms, libraries
+are de-duplicated by keeping their fist occurrence, thus respecting the
+project-specified order.  This policy provides compatibility with projects
+that have not been updated to expect the latter behavior.
+
+The ``OLD`` behavior for this policy is to always repeat static libraries
+as if using a traditional linker, and always de-duplicate shared libraries
+by keeping the last occurrence of each.  The ``NEW`` behavior for this policy
+is to apply different strategies based on linker capabilities.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0157.rst b/Help/policy/CMP0157.rst
new file mode 100644
index 0000000..c607203
--- /dev/null
+++ b/Help/policy/CMP0157.rst
@@ -0,0 +1,47 @@
+CMP0157
+-------
+
+.. versionadded:: 3.29
+
+Swift compilation mode is selected by an abstraction.
+
+The Swift compiler can compile modules in different modes. The desired build
+mode depends whether the developer is iterating and wants to incrementally make
+changes, or if they are building a release for distribution and want more
+optimizations applied to the resulting binary.
+
+CMake versions 3.26 through 3.28 build Swift binaries with whole-module
+optimizations enabled when configured in a non-debug build type.
+For CMake versions earlier than 3.26, the developer needs to specify
+the necessary flag manually for the :ref:`Ninja Generators`, and cannot
+not specify whole-module optimizations to the :generator:`Xcode` generator.
+
+CMake versions 3.29 and above prefer to set the compilation mode using
+the :prop_tgt:`Swift_COMPILATION_MODE` target property, which can be
+initialized by the :variable:`CMAKE_Swift_COMPILATION_MODE` variable.
+
+This policy provides compatibility for projects that have not been updated.
+The policy setting takes effect as of the first :command:`project` or
+:command:`enable_language` command that enables the ``Swift`` language.
+
+.. note::
+
+  Once the policy has taken effect at the top of a project, that choice
+  must be used throughout the tree. In projects that have nested projects
+  in subdirectories, be sure to convert everything together.
+
+The ``OLD`` behavior for this policy builds all Swift targets in
+``wholemodule`` mode for non-debug configurations.  :ref:`Ninja Generators`
+prepend the ``-wmo`` flag to the default set of Swift flags.
+The :generator:`Xcode` generator sets the ``SWIFT_COMPILATION_MODE``
+attribute to ``wholemodule`` in the generated Xcode project file.
+
+The ``NEW`` behavior for this policy is to apply the compilation mode specified
+in the :prop_tgt:`Swift_COMPILATION_MODE` target property, initialized as each
+target is created by the :variable:`CMAKE_Swift_COMPILATION_MODE` variable.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0158.rst b/Help/policy/CMP0158.rst
new file mode 100644
index 0000000..66e5a62
--- /dev/null
+++ b/Help/policy/CMP0158.rst
@@ -0,0 +1,28 @@
+CMP0158
+-------
+
+.. versionadded:: 3.29
+
+:command:`add_test` honors :variable:`CMAKE_CROSSCOMPILING_EMULATOR` only
+when :variable:`cross-compiling <CMAKE_CROSSCOMPILING>`.
+
+In CMake 3.28 and below, :command:`add_test` unconditionally used the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` target property (initialized by the
+:variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable) to run test commands
+naming executable targets.  CMake 3.29 and above prefer to use the emulator
+only when the :variable:`CMAKE_CROSSCOMPILING` variable is enabled.  The
+:variable:`CMAKE_TEST_LAUNCHER` variable may be used instead when not
+cross-compiling.  This policy provides compatibility for projects that
+have not been updated.
+
+The ``OLD`` behavior for this policy is for :command:`add_test` to use
+the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property unconditionally.
+The ``NEW`` behavior for this policy is for :command:`add_test` to use
+the :prop_tgt:`CROSSCOMPILING_EMULATOR` target property only when
+:variable:`cross-compiling <CMAKE_CROSSCOMPILING>`.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0159.rst b/Help/policy/CMP0159.rst
new file mode 100644
index 0000000..908ef0f
--- /dev/null
+++ b/Help/policy/CMP0159.rst
@@ -0,0 +1,23 @@
+CMP0159
+-------
+
+.. versionadded:: 3.29
+
+:command:`file(STRINGS)` with ``REGEX`` updates :variable:`CMAKE_MATCH_<n>`.
+
+In CMake 3.28 and below the :command:`file(STRINGS)` command's ``REGEX``
+option does not affect :variable:`CMAKE_MATCH_<n>` variables.  CMake 3.29
+and above prefer to update the :variable:`CMAKE_MATCH_<n>` variables using
+captures from the last match in the file, similar to the
+:command:`string(REGEX MATCHALL)` command.  This policy provides
+compatibility for projects that have not been updated to expect the behavior.
+
+The ``OLD`` behavior for this policy is for :command:`file(STRINGS)` with
+``REGEX`` to not store capture groups in :variable:`CMAKE_MATCH_<n>`
+variables.  The ``NEW`` behavior is to store the capture groups.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0160.rst b/Help/policy/CMP0160.rst
new file mode 100644
index 0000000..46318ab
--- /dev/null
+++ b/Help/policy/CMP0160.rst
@@ -0,0 +1,39 @@
+CMP0160
+-------
+
+.. versionadded:: 3.29
+
+More read-only target properties now error when trying to set them.
+
+The :command:`set_target_properties` and :command:`set_property` commands
+are intended to error out on all read-only properties. However, CMake 3.28 and
+below only did this for the following properties:
+
+* :prop_tgt:`HEADER_SETS`
+* :prop_tgt:`INTERFACE_HEADER_SETS`
+* :prop_tgt:`IMPORTED_GLOBAL`
+* :prop_tgt:`MANUALLY_ADDED_DEPENDENCIES`
+* :prop_tgt:`NAME`
+* :prop_tgt:`TYPE`
+
+This policy enforces the read-only nature of the following target properties:
+
+* :prop_tgt:`ALIAS_GLOBAL`
+* :prop_tgt:`BINARY_DIR`
+* :prop_tgt:`CXX_MODULE_SETS`
+* :prop_tgt:`IMPORTED`
+* :prop_tgt:`INTERFACE_CXX_MODULE_SETS`
+* :prop_tgt:`LOCATION`
+* :prop_tgt:`LOCATION_<CONFIG>`
+* :prop_tgt:`SOURCE_DIR`
+
+The ``OLD`` behavior for this policy is to only error out for the properties
+:prop_tgt:`MANUALLY_ADDED_DEPENDENCIES`, :prop_tgt:`NAME`, and :prop_tgt:`TYPE`.
+The ``NEW`` behavior for this policy is to error out on all target properties
+that are documented as read-only.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0161.rst b/Help/policy/CMP0161.rst
new file mode 100644
index 0000000..34bb64c
--- /dev/null
+++ b/Help/policy/CMP0161.rst
@@ -0,0 +1,36 @@
+CMP0161
+-------
+
+.. versionadded:: 3.29
+
+The :variable:`CPACK_PRODUCTBUILD_DOMAINS` variable defaults to true.
+
+Before CMake 3.29, the :variable:`CPACK_PRODUCTBUILD_DOMAINS` variable is
+unset by default.  When using the :cpack_gen:`CPack productbuild Generator`,
+this disables the use of the ``domains`` attribute in the productbuild
+Distribution XML, and falls back to the ``auth`` attribute instead.
+These attributes control where a productbuild package is allowed to be
+installed.  But the ``auth`` attribute has been deprecated by Apple,
+so projects should migrate to using ``domains`` instead.
+
+CMake 3.29 and above prefer to use a default value of true for
+:variable:`CPACK_PRODUCTBUILD_DOMAINS`, which means ``domains`` will be used
+by default unless the project explicitly sets
+:variable:`CPACK_PRODUCTBUILD_DOMAINS` to false.
+This policy provides compatibility with projects that enabled the
+:cpack_gen:`CPack productbuild Generator`, but did not explicitly set
+:variable:`CPACK_PRODUCTBUILD_DOMAINS`.
+
+The ``OLD`` behavior for this policy is to leave
+:variable:`CPACK_PRODUCTBUILD_DOMAINS` unset if it hasn't been set.
+The ``NEW`` behavior for this policy is to use a default value of true for
+:variable:`CPACK_PRODUCTBUILD_DOMAINS`.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.29
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+Note that a warning will only be emitted if the
+:variable:`CPACK_BINARY_PRODUCTBUILD <CPACK_BINARY_<GENNAME>>` variable is
+set to true and the project is being built for an Apple platform.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/DISALLOWED_COMMAND.txt b/Help/policy/DISALLOWED_COMMAND.txt
index 6500bb0..506f2bb 100644
--- a/Help/policy/DISALLOWED_COMMAND.txt
+++ b/Help/policy/DISALLOWED_COMMAND.txt
@@ -3,7 +3,6 @@
 The ``NEW`` behavior for this policy is to issue a ``FATAL_ERROR`` when the
 command is called.
 
-This policy was introduced in CMake version |disallowed_version|.
-CMake version |release| warns when the policy is not set and uses
-``OLD`` behavior.  Use the :command:`cmake_policy` command to set it to ``OLD`` or
-``NEW`` explicitly.
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: |disallowed_version|
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
diff --git a/Help/policy/STANDARD_ADVICE.txt b/Help/policy/STANDARD_ADVICE.txt
new file mode 100644
index 0000000..925c174
--- /dev/null
+++ b/Help/policy/STANDARD_ADVICE.txt
@@ -0,0 +1,3 @@
+This policy was introduced in CMake version |INTRODUCED_IN_CMAKE_VERSION|.
+It may be set by :command:`cmake_policy` or :command:`cmake_minimum_required`.
+If it is not set, CMake |WARNS_OR_DOES_NOT_WARN|, and uses ``OLD`` behavior.
diff --git a/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst b/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst
index 1ec4517..e94856d 100644
--- a/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst
+++ b/Help/prop_test/FAIL_REGULAR_EXPRESSION.rst
@@ -1,15 +1,22 @@
 FAIL_REGULAR_EXPRESSION
 -----------------------
 
-If the output matches this regular expression the test will fail,
-regardless of the process exit code.
+If the test output (stdout or stderr) matches this regular expression the test
+will fail, regardless of the process exit code. Tests that exceed the timeout
+specified by :prop_test:`TIMEOUT` fail regardless of
+``FAIL_REGULAR_EXPRESSION``. Any non-zero return code or system-level test
+failures including segmentation faults, signal abort, or heap errors fail the
+test even if the regular expression does not match.
 
-If set, if the output matches one of specified regular expressions,
-the test will fail.  Example:
+If set, if the output matches one of specified regular expressions, the test
+will fail.  Example:
 
 .. code-block:: cmake
 
-  set_tests_properties(mytest PROPERTIES
+  # test would pass, except for FAIL_REGULAR_EXPRESSION
+  add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Failed")
+
+  set_property(TEST mytest PROPERTY
     FAIL_REGULAR_EXPRESSION "[^a-z]Error;ERROR;Failed"
   )
 
diff --git a/Help/prop_test/PASS_REGULAR_EXPRESSION.rst b/Help/prop_test/PASS_REGULAR_EXPRESSION.rst
index 96468c0..b19e637 100644
--- a/Help/prop_test/PASS_REGULAR_EXPRESSION.rst
+++ b/Help/prop_test/PASS_REGULAR_EXPRESSION.rst
@@ -1,20 +1,50 @@
 PASS_REGULAR_EXPRESSION
 -----------------------
 
-The output must match this regular expression for the test to pass.
-The process exit code is ignored.
+The test output (stdout or stderr) must match this regular expression
+for the test to pass. The process exit code is ignored. Tests that exceed
+the timeout specified by :prop_test:`TIMEOUT` still fail regardless of
+``PASS_REGULAR_EXPRESSION``. System-level test failures including
+segmentation faults, signal abort, or heap errors may fail the test even
+if ``PASS_REGULAR_EXPRESSION`` is matched.
 
-If set, the test output will be checked against the specified regular
-expressions and at least one of the regular expressions has to match,
-otherwise the test will fail.  Example:
+Example:
 
 .. code-block:: cmake
 
-  set_tests_properties(mytest PROPERTIES
-    PASS_REGULAR_EXPRESSION "TestPassed;All ok"
+  add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Passed this test")
+
+  set_property(TEST mytest PROPERTY
+    PASS_REGULAR_EXPRESSION "pass;Passed"
   )
 
 ``PASS_REGULAR_EXPRESSION`` expects a list of regular expressions.
 
+To run a test that may have a system-level failure, but still pass if
+``PASS_REGULAR_EXPRESSION`` matches, use a CMake command to wrap the
+executable run. Note that this will prevent automatic handling of the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER`
+target property.
+
+.. code-block:: cmake
+
+    add_executable(main main.c)
+
+    add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>)
+
+    set_property(TEST sigabrt PROPERTY PROPERTY_REGULAR_EXPRESSION "pass;Passed")
+
+.. code-block:: c
+
+    #include <signal.h>
+    #include <stdio.h>
+
+    int main(void){
+        fprintf(stdout, "Passed\n");
+        fflush(stdout);  /* ensure the output buffer is seen */
+        raise(SIGABRT);
+        return 0;
+    }
+
 See also the :prop_test:`FAIL_REGULAR_EXPRESSION` and
 :prop_test:`SKIP_REGULAR_EXPRESSION` test properties.
diff --git a/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst
index 60038e4..8717a0a 100644
--- a/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst
+++ b/Help/prop_test/SKIP_REGULAR_EXPRESSION.rst
@@ -3,19 +3,45 @@
 
 .. versionadded:: 3.16
 
-If the output matches this regular expression the test will be marked as skipped.
+If the test output (stderr or stdout) matches this regular expression the test
+will be marked as skipped, regardless of the process exit code. Tests that
+exceed the timeout specified by :prop_test:`TIMEOUT` still fail regardless of
+``SKIP_REGULAR_EXPRESSION``. System-level test failures including segmentation
+faults, signal abort, or heap errors may fail the test even if the regular
+expression matches.
 
-If set, if the output matches one of specified regular expressions,
-the test will be marked as skipped.  Example:
+Example:
 
 .. code-block:: cmake
 
+  add_test(NAME mytest COMMAND ${CMAKE_COMMAND} -E echo "Skipped this test")
+
   set_property(TEST mytest PROPERTY
     SKIP_REGULAR_EXPRESSION "[^a-z]Skip" "SKIP" "Skipped"
   )
 
 ``SKIP_REGULAR_EXPRESSION`` expects a list of regular expressions.
 
+To run a test that may have a system-level failure, but still skip if
+``SKIP_REGULAR_EXPRESSION`` matches, use a CMake command to wrap the
+executable run. Note that this will prevent automatic handling of the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER`
+target property.
+
+.. code-block:: cmake
+
+    add_executable(main main.c)
+
+    add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>)
+
+    set_property(TEST sigabrt PROPERTY SKIP_REGULAR_EXPRESSION "SIGABRT;[aA]bort")
+
+.. code-block:: c
+
+    #include <signal.h>
+
+    int main(void){ raise(SIGABRT); return 0; }
+
 See also the :prop_test:`SKIP_RETURN_CODE`,
 :prop_test:`PASS_REGULAR_EXPRESSION`, and :prop_test:`FAIL_REGULAR_EXPRESSION`
 test properties.
diff --git a/Help/prop_test/SKIP_RETURN_CODE.rst b/Help/prop_test/SKIP_RETURN_CODE.rst
index 23c4c62..57fc031 100644
--- a/Help/prop_test/SKIP_RETURN_CODE.rst
+++ b/Help/prop_test/SKIP_RETURN_CODE.rst
@@ -9,4 +9,40 @@
 ``Not Run`` if it is encountered. Valid values are in the range of
 0 to 255, inclusive.
 
-See also the :prop_test:`SKIP_REGULAR_EXPRESSION` property.
+Tests that exceed the timeout specified by :prop_test:`TIMEOUT` still fail
+regardless of ``SKIP_RETURN_CODE``.
+System-level test failures including segmentation faults,
+signal abort, or heap errors may fail the test even if the return code matches.
+
+.. code-block:: cmake
+
+    # cmake (1) defines this to return code 1
+    add_test(NAME r1 COMMAND ${CMAKE_COMMAND} -E false)
+
+    set_tests_properties(r1 PROPERTIES SKIP_RETURN_CODE 1)
+
+
+To run a test that may have a system-level failure, but still skip if
+``SKIP_RETURN_CODE`` matches, use a CMake command to wrap the executable run.
+Note that this will prevent automatic handling of the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER` target
+property.
+
+.. code-block:: cmake
+
+    add_executable(main main.c)
+
+    # cmake -E env <command> returns 1 if the command fails in any way
+    add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>)
+
+    set_property(TEST sigabrt PROPERTY SKIP_RETURN_CODE 1)
+
+.. code-block:: c
+
+    #include <signal.h>
+
+    int main(void){ raise(SIGABRT); return 0; }
+
+
+To handle multiple types of cases that may need to be skipped, consider the
+:prop_test:`SKIP_REGULAR_EXPRESSION` property.
diff --git a/Help/prop_test/WILL_FAIL.rst b/Help/prop_test/WILL_FAIL.rst
index 4926f40..9d61ab7 100644
--- a/Help/prop_test/WILL_FAIL.rst
+++ b/Help/prop_test/WILL_FAIL.rst
@@ -1,8 +1,37 @@
 WILL_FAIL
 ---------
 
-If set to true, this will invert the pass/fail flag of the test.
+If ``true``, inverts the pass / fail test criteria. Tests for which
+``WILL_FAIL`` is ``true`` fail with return code 0 and pass with non-zero
+return code. Tests that exceed the timeout specified by :prop_test:`TIMEOUT`
+still fail regardless of ``WILL_FAIL``.
+System-level test failures including segmentation faults,
+signal abort, or heap errors may fail the test even if ``WILL_FAIL`` is true.
 
-This property can be used for tests that are expected to fail and return a
-non-zero return code. Note that system-level test failures such as segmentation
-faults or heap errors will still fail the test even if ``WILL_FALL`` is true.
+Example of a test that would ordinarily pass, but fails because ``WILL_FAIL``
+is ``true``:
+
+.. code-block:: cmake
+
+    add_test(NAME failed COMMAND ${CMAKE_COMMAND} -E true)
+    set_property(TEST failed PROPERTY WILL_FAIL true)
+
+To run a test that may have a system-level failure, but still pass if
+``WILL_FAIL`` is set, use a CMake command to wrap the executable run.
+Note that this will prevent automatic handling of the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` and :prop_tgt:`TEST_LAUNCHER`
+target property.
+
+.. code-block:: cmake
+
+    add_executable(main main.c)
+
+    add_test(NAME sigabrt COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:main>)
+
+    set_property(TEST sigabrt PROPERTY WILL_FAIL TRUE)
+
+.. code-block:: c
+
+    #include <signal.h>
+
+    int main(void){ raise(SIGABRT); return 0; }
diff --git a/Help/prop_tgt/AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst b/Help/prop_tgt/AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst
new file mode 100644
index 0000000..edeb15c
--- /dev/null
+++ b/Help/prop_tgt/AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst
@@ -0,0 +1,22 @@
+AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
+---------------------------------
+
+.. versionadded:: 3.29
+
+``AUTOGEN_BETTER_GRAPH_MULTI_CONFIG`` is a boolean property that can be set
+on a target to have better dependency graph for multi-configuration generators.
+When this property is enabled, ``CMake`` will generate more per-config targets.
+Thus, the dependency graph will be more accurate for multi-configuration
+generators and some recompilations will be avoided.
+
+If the Qt version is 6.8 or newer, this property is enabled by default.
+If the Qt version is older than 6.8, this property is disabled by default.
+Consult the Qt documentation to check if the property can be enabled for older
+Qt versions.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
+
+This property is initialized by the
+:variable:`CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG` variable if it is set when
+a target is created.
diff --git a/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst b/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst
new file mode 100644
index 0000000..f1e51a7
--- /dev/null
+++ b/Help/prop_tgt/AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst
@@ -0,0 +1,18 @@
+AUTOGEN_COMMAND_LINE_LENGTH_MAX
+-------------------------------
+
+.. versionadded:: 3.29
+
+Command line length limit for autogen targets, i.e. ``moc`` or ``uic``,
+that triggers the use of response files on Windows instead of passing all
+arguments to the command line.
+
+- An empty (or unset) value sets the limit to 32000
+- A positive non zero integer value sets the exact command line length
+  limit.
+
+By default ``AUTOGEN_COMMAND_LINE_LENGTH_MAX`` is initialized from
+:variable:`CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX`.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
diff --git a/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst
index 9350a4f..33db8a7 100644
--- a/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst
+++ b/Help/prop_tgt/AUTOGEN_ORIGIN_DEPENDS.rst
@@ -4,20 +4,29 @@
 .. versionadded:: 3.14
 
 Switch for forwarding origin target dependencies to the corresponding
-``_autogen`` target.
+:ref:`<ORIGIN>_autogen` target.
+
+  .. note::
+
+    If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
+    or :ref:`Makefile Generators`, origin target dependencies are forwarded to
+    the :ref:`<ORIGIN>_autogen_timestamp_deps` target instead of
+    :ref:`<ORIGIN>_autogen`
+
 
 Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property
-``ON`` have a corresponding ``_autogen`` target which generates
-``moc`` and ``uic`` files.  As this ``_autogen`` target is created at
+``ON`` have a corresponding :ref:`<ORIGIN>_autogen` target which generates
+``moc`` and ``uic`` files.  As this :ref:`<ORIGIN>_autogen` target is created at
 generate-time, it is not possible to define dependencies of it using
-e.g.  :command:`add_dependencies`.  Instead the
-``AUTOGEN_ORIGIN_DEPENDS`` target property decides whether the origin
-target dependencies should be forwarded to the ``_autogen`` target or not.
+e.g.  :command:`add_dependencies`.  Instead the ``AUTOGEN_ORIGIN_DEPENDS``
+target property decides whether the origin target dependencies should be
+forwarded to the :ref:`<ORIGIN>_autogen` target or not.
 
 By default ``AUTOGEN_ORIGIN_DEPENDS`` is initialized from
 :variable:`CMAKE_AUTOGEN_ORIGIN_DEPENDS` which is ``ON`` by default.
 
-In total the dependencies of the ``_autogen`` target are composed from
+In total the dependencies of the :ref:`<ORIGIN>_autogen` target are composed
+from
 
 - forwarded origin target dependencies
   (enabled by default via ``AUTOGEN_ORIGIN_DEPENDS``)
@@ -26,15 +35,14 @@
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
 
-Note
-^^^^
+.. note::
 
-Disabling ``AUTOGEN_ORIGIN_DEPENDS`` is useful to avoid building of
-origin target dependencies when building the ``_autogen`` target only.
-This is especially interesting when a
-:variable:`global autogen target <CMAKE_GLOBAL_AUTOGEN_TARGET>` is enabled.
+    Disabling ``AUTOGEN_ORIGIN_DEPENDS`` is useful to avoid building of
+    origin target dependencies when building the :ref:`<ORIGIN>_autogen` target
+    only. This is especially interesting when a
+    :variable:`global autogen target <CMAKE_GLOBAL_AUTOGEN_TARGET>` is enabled.
 
-When the ``_autogen`` target doesn't require all the origin target's
-dependencies, and ``AUTOGEN_ORIGIN_DEPENDS`` is disabled, it might be
-necessary to extend :prop_tgt:`AUTOGEN_TARGET_DEPENDS` to add missing
-dependencies.
+    When the :ref:`<ORIGIN>_autogen` target doesn't require all the origin target's
+    dependencies, and ``AUTOGEN_ORIGIN_DEPENDS`` is disabled, it might be
+    necessary to extend :prop_tgt:`AUTOGEN_TARGET_DEPENDS` to add missing
+    dependencies.
diff --git a/Help/prop_tgt/AUTOGEN_PARALLEL.rst b/Help/prop_tgt/AUTOGEN_PARALLEL.rst
index 663b54e..9d34355 100644
--- a/Help/prop_tgt/AUTOGEN_PARALLEL.rst
+++ b/Help/prop_tgt/AUTOGEN_PARALLEL.rst
@@ -6,9 +6,9 @@
 Number of parallel ``moc`` or ``uic`` processes to start when using
 :prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`.
 
-The custom ``<origin>_autogen`` target starts a number of threads of which
+The custom :ref:`<ORIGIN>_autogen` target starts a number of threads of which
 each one parses a source file and on demand starts a ``moc`` or ``uic``
-process.  ``AUTOGEN_PARALLEL`` controls how many parallel threads
+process. ``AUTOGEN_PARALLEL`` controls how many parallel threads
 (and therefore ``moc`` or ``uic`` processes) are started.
 
 - An empty (or unset) value or the string ``AUTO`` sets the number of
diff --git a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst
index 5286d2d..13e2ef7 100644
--- a/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst
+++ b/Help/prop_tgt/AUTOGEN_TARGET_DEPENDS.rst
@@ -1,18 +1,28 @@
 AUTOGEN_TARGET_DEPENDS
 ----------------------
 
-Additional target dependencies of the corresponding ``_autogen`` target.
+Additional target dependencies of the corresponding :ref:`<ORIGIN>_autogen`
+target.
+
+  .. note::
+
+    If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
+    or :ref:`Makefile Generators`, additional target dependencies are added to
+    the :ref:`<ORIGIN>_autogen_timestamp_deps` target instead of the
+    :ref:`<ORIGIN>_autogen` target.
+
 
 Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property
-``ON`` have a corresponding ``_autogen`` target which generates
-``moc`` and ``uic`` files.  As this ``_autogen`` target is created at
-generate-time, it is not possible to define dependencies of it using
-e.g.  :command:`add_dependencies`.  Instead the
-``AUTOGEN_TARGET_DEPENDS`` target property can be set to a
-:ref:`;-list <CMake Language Lists>` of additional dependencies for the
-``_autogen`` target.  Dependencies can be target names or file names.
+``ON`` have a corresponding :ref:`<ORIGIN>_autogen`  target which generates
+``moc`` and ``uic`` files.  As this :ref:`<ORIGIN>_autogen`  target is created
+at generate-time, it is not possible to define dependencies of it using e.g.
+:command:`add_dependencies`.  Instead the ``AUTOGEN_TARGET_DEPENDS`` target
+property can be set to a :ref:`;-list <CMake Language Lists>` of additional
+dependencies for the :ref:`<ORIGIN>_autogen`  target. Dependencies can be target
+names or file names.
 
-In total the dependencies of the ``_autogen`` target are composed from
+In total the dependencies of the :ref:`<ORIGIN>_autogen`  target are composed
+from
 
 - forwarded origin target dependencies
   (enabled by default via :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`)
diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst
index 0feb2e8..d4f2b25 100644
--- a/Help/prop_tgt/AUTOMOC.rst
+++ b/Help/prop_tgt/AUTOMOC.rst
@@ -3,11 +3,13 @@
 
 Should the target be processed with auto-moc (for Qt projects).
 
-``AUTOMOC`` is a boolean specifying whether CMake will handle the Qt
-``moc`` preprocessor automatically, i.e.  without having to use commands like
-:module:`QT4_WRAP_CPP() <FindQt4>`, ``QT5_WRAP_CPP()``, etc.
+``AUTOMOC`` is a boolean specifying whether CMake will handle the Qt ``moc``
+preprocessor automatically, i.e.  without having to use commands like
+:module:`QT4_WRAP_CPP() <FindQt4>`, `qt5_wrap_cpp()`_, etc.
 Currently, Qt versions 4 to 6 are supported.
 
+.. _qt5_wrap_cpp(): https://doc.qt.io/qt-5/qtcore-cmake-qt5-wrap-cpp.html
+
 This property is initialized by the value of the :variable:`CMAKE_AUTOMOC`
 variable if it is set when a target is created.
 
@@ -240,12 +242,16 @@
 
 :variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`:
 A global ``autogen`` target, that depends on all ``AUTOMOC`` or
-:prop_tgt:`AUTOUIC` generated ``<ORIGIN>_autogen`` targets in the project,
+:prop_tgt:`AUTOUIC` generated :ref:`<ORIGIN>_autogen` targets in the project,
 will be generated when this variable is ``ON``.
 
 :prop_tgt:`AUTOGEN_PARALLEL`:
 This target property controls the number of ``moc`` or ``uic`` processes to
 start in parallel during builds.
 
+:prop_tgt:`AUTOGEN_COMMAND_LINE_LENGTH_MAX`:
+This target property controls the limit when to use response files for
+``moc`` or ``uic`` processes on Windows.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
diff --git a/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst b/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst
index c4277d7..d571f53 100644
--- a/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst
+++ b/Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst
@@ -22,14 +22,14 @@
 
 If any of the extracted files is :prop_sf:`GENERATED` or if it is not in the
 target's sources, then it might be necessary to add it to the
-``_autogen`` target  dependencies.
+:ref:`<ORIGIN>_autogen` target  dependencies.
 See :prop_tgt:`AUTOGEN_TARGET_DEPENDS` for reference.
 
 By default ``AUTOMOC_DEPEND_FILTERS`` is initialized from
 :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS`, which is empty by default.
 
-From Qt 5.15.0 on this variable is ignored as moc is able to output the correct
-dependencies.
+From Qt 5.15.0 on this variable is ignored as ``moc`` is able to output the
+correct dependencies.
 
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
diff --git a/Help/prop_tgt/AUTORCC.rst b/Help/prop_tgt/AUTORCC.rst
index 33de352..fea906c 100644
--- a/Help/prop_tgt/AUTORCC.rst
+++ b/Help/prop_tgt/AUTORCC.rst
@@ -5,9 +5,11 @@
 
 ``AUTORCC`` is a boolean specifying whether CMake will handle
 the Qt ``rcc`` code generator automatically, i.e. without having to use
-commands like :module:`QT4_ADD_RESOURCES() <FindQt4>`, ``QT5_ADD_RESOURCES()``,
+commands like :module:`QT4_ADD_RESOURCES() <FindQt4>`, `qt5_add_resources()`_,
 etc.  Currently, Qt versions 4 to 6 are supported.
 
+.. _`qt5_add_resources()`: https://doc.qt.io/qt-5/qtcore-cmake-qt5-add-resources.html
+
 When this property is ``ON``, CMake will handle ``.qrc`` files added
 as target sources at build time and invoke ``rcc`` accordingly.
 This property is initialized by the value of the :variable:`CMAKE_AUTORCC`
diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst
index dc854b2..5010220 100644
--- a/Help/prop_tgt/AUTOUIC.rst
+++ b/Help/prop_tgt/AUTOUIC.rst
@@ -5,9 +5,11 @@
 
 ``AUTOUIC`` is a boolean specifying whether CMake will handle
 the Qt ``uic`` code generator automatically, i.e. without having to use
-commands like :module:`QT4_WRAP_UI() <FindQt4>`, ``QT5_WRAP_UI()``, etc.
+commands like :module:`QT4_WRAP_UI() <FindQt4>`, `qt5_wrap_ui()`_, etc.
 Currently, Qt versions 4 to 6 are supported.
 
+.. _`qt5_wrap_ui()`: https://doc.qt.io/qt-5/qtwidgets-cmake-qt5-wrap-ui.html
+
 This property is initialized by the value of the :variable:`CMAKE_AUTOUIC`
 variable if it is set when a target is created.
 
@@ -74,12 +76,16 @@
 
 :variable:`CMAKE_GLOBAL_AUTOGEN_TARGET`:
 A global ``autogen`` target, that depends on all :prop_tgt:`AUTOMOC` or
-``AUTOUIC`` generated ``<ORIGIN>_autogen`` targets in the project,
+``AUTOUIC`` generated :ref:`<ORIGIN>_autogen` targets in the project,
 will be generated when this variable is ``ON``.
 
 :prop_tgt:`AUTOGEN_PARALLEL`:
 This target property controls the number of ``moc`` or ``uic`` processes to
 start in parallel during builds.
 
+:prop_tgt:`AUTOGEN_COMMAND_LINE_LENGTH_MAX`:
+This target property controls the limit when to use response files for
+``moc`` or ``uic`` processes on Windows.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
diff --git a/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst b/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst
index 8b777e4..d09ff19 100644
--- a/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst
+++ b/Help/prop_tgt/CROSSCOMPILING_EMULATOR.rst
@@ -13,6 +13,10 @@
   Lists>`, then the first value is the command and remaining values are its
   arguments.
 
+.. versionadded:: 3.29
+  Contents of ``CROSSCOMPILING_EMULATOR`` may use
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
 This property is initialized by the value of the
 :variable:`CMAKE_CROSSCOMPILING_EMULATOR` variable if it is set when a target
 is created.
diff --git a/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst
new file mode 100644
index 0000000..b49f005
--- /dev/null
+++ b/Help/prop_tgt/EXPORT_FIND_PACKAGE_NAME.rst
@@ -0,0 +1,12 @@
+EXPORT_FIND_PACKAGE_NAME
+------------------------
+
+.. versionadded:: 3.29
+
+Control the package name associated with a dependency target when exporting a
+:command:`find_dependency` call in :command:`install(EXPORT)` or
+:command:`export(EXPORT)`. This can be used to assign a package name to a
+package that is built by CMake and exported, or to override the package in the
+:command:`find_package` call that created the target.
+
+This property is initialized by :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME`.
diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst
index 2351374..b162253 100644
--- a/Help/prop_tgt/IMPORTED_LOCATION.rst
+++ b/Help/prop_tgt/IMPORTED_LOCATION.rst
@@ -32,7 +32,7 @@
 selected and its :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` value used.
 
 To get the location of an imported target read one of the :prop_tgt:`LOCATION`
-or ``LOCATION_<CONFIG>`` properties.
+or :prop_tgt:`LOCATION_<CONFIG>` properties.
 
 For platforms with import libraries (e.g. Windows, AIX or Apple) see also
 :prop_tgt:`IMPORTED_IMPLIB`.
diff --git a/Help/prop_tgt/LINKER_TYPE.rst b/Help/prop_tgt/LINKER_TYPE.rst
new file mode 100644
index 0000000..90a663d
--- /dev/null
+++ b/Help/prop_tgt/LINKER_TYPE.rst
@@ -0,0 +1,26 @@
+LINKER_TYPE
+-----------
+
+.. versionadded:: 3.29
+
+Specify which linker will be used for the link step. The property value may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+.. code-block:: cmake
+
+  add_library(lib1 SHARED ...)
+  set_property(TARGET lib1 PROPERTY LINKER_TYPE LLD)
+
+This specifies that ``lib1`` should use linker type ``LLD`` for the link step.
+The implementation details will be provided by the variable
+:variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` with ``<TYPE>`` having the value
+``LLD``.
+
+This property is not supported on :generator:`Green Hills MULTI` and
+:generator:`Visual Studio 9 2008` generators.
+
+.. note::
+  It is assumed that the linker specified is fully compatible with the standard
+  one. CMake will not do any options translation.
+
+.. include:: ../variable/LINKER_PREDEFINED_TYPES.txt
diff --git a/Help/prop_tgt/SOVERSION.rst b/Help/prop_tgt/SOVERSION.rst
index b377f22..4f8b1b5 100644
--- a/Help/prop_tgt/SOVERSION.rst
+++ b/Help/prop_tgt/SOVERSION.rst
@@ -1,15 +1,17 @@
 SOVERSION
 ---------
 
-What version number is this target.
+ABI version number of a shared library target.
 
 For shared libraries :prop_tgt:`VERSION` and ``SOVERSION`` can be used to
-specify the build version and API version respectively.  When building or
+specify the build version and ABI version respectively.  When building or
 installing appropriate symlinks are created if the platform supports
 symlinks and the linker supports so-names.  If only one of both is
 specified the missing is assumed to have the same version number.
 ``SOVERSION`` is ignored if :prop_tgt:`NO_SONAME` property is set.
 
+.. include:: VERSION_SOVERSION_EXAMPLE.txt
+
 Windows Versions
 ^^^^^^^^^^^^^^^^
 
diff --git a/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt b/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt
new file mode 100644
index 0000000..b94380a
--- /dev/null
+++ b/Help/prop_tgt/Swift_COMPILATION_MODE-VALUES.txt
@@ -0,0 +1,19 @@
+``incremental``
+  Compiles each Swift source in the module separately, resulting in better
+  parallelism in the build. The compiler emits additional information into
+  the build directory improving rebuild performance when small changes are made
+  to the source between rebuilds. This is the best option to use while
+  iterating on changes in a project.
+
+``wholemodule``
+  Whole-module optimizations are slowest to compile, but results in the most
+  optimized library. The entire context is loaded into once instance of the
+  compiler, so there is no parallelism across source files in the module.
+
+``singlefile``
+  Compiles each source in a Swift modules separately, resulting in better
+  parallelism. Unlike the ``incremental`` build mode, no additional information
+  is emitted by the compiler during the build, so rebuilding after making small
+  changes to the source file will not run faster. This option should be used
+  sparingly, preferring ``incremental`` builds, unless working around a compiler
+  bug.
diff --git a/Help/prop_tgt/Swift_COMPILATION_MODE.rst b/Help/prop_tgt/Swift_COMPILATION_MODE.rst
new file mode 100644
index 0000000..e26474a
--- /dev/null
+++ b/Help/prop_tgt/Swift_COMPILATION_MODE.rst
@@ -0,0 +1,33 @@
+Swift_COMPILATION_MODE
+----------------------
+
+.. versionadded:: 3.29
+
+Specify how Swift compiles a target.
+
+The allowed values are:
+
+.. include:: Swift_COMPILATION_MODE-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to support
+per-configuration specification. For example, the code:
+
+.. code-block:: cmake
+
+  add_library(foo foo.swift)
+  set_property(TARGET foo PROPERTY
+    Swift_COMPILATION_MODE "$<IF:$<CONFIG:Release>,wholemodule,incremental>")
+
+sets the Swift compilation mode to wholemodule mode in the release configuration
+and sets the property to incremental mode in other configurations.
+
+The property is initialized from the value of the
+:variable:`CMAKE_Swift_COMPILATION_MODE` variable, if it is set. If the property
+is not set or is empty, then CMake uses the default value ``incremental`` to
+specify the swift compilation mode.
+
+.. note::
+
+   This property only has effect when policy :policy:`CMP0157` is set to ``NEW``
+   prior to the first :command:`project` or :command:`enable_language` command
+   that enables the Swift language.
diff --git a/Help/prop_tgt/TEST_LAUNCHER.rst b/Help/prop_tgt/TEST_LAUNCHER.rst
new file mode 100644
index 0000000..7eec319
--- /dev/null
+++ b/Help/prop_tgt/TEST_LAUNCHER.rst
@@ -0,0 +1,23 @@
+TEST_LAUNCHER
+-------------
+
+.. versionadded:: 3.29
+
+Use the given launcher to run executables.
+This command will be added as a prefix to :command:`add_test` commands
+for build target system executables and is meant to be run on the host
+machine.
+
+It effectively acts as a run script for tests in a similar way
+to how :variable:`CMAKE_<LANG>_COMPILER_LAUNCHER` works for compilation.
+
+If this property contains a :ref:`semicolon-separated list <CMake Language
+Lists>`, then the first value is the command and remaining values are its
+arguments.
+
+Contents of ``TEST_LAUNCHER`` may use
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is initialized by the value of the
+:variable:`CMAKE_TEST_LAUNCHER` variable if it is set when a target
+is created.
diff --git a/Help/prop_tgt/UNITY_BUILD.rst b/Help/prop_tgt/UNITY_BUILD.rst
index 9d68250..577b0c9 100644
--- a/Help/prop_tgt/UNITY_BUILD.rst
+++ b/Help/prop_tgt/UNITY_BUILD.rst
@@ -30,11 +30,23 @@
 If no explicit :prop_tgt:`UNITY_BUILD_MODE` has been specified, CMake will
 default to ``BATCH``.
 
-Unity builds are not currently supported for all languages.  CMake version
-|release| supports combining ``C`` and ``CXX`` source files.  For targets that
-mix source files from more than one language, CMake will separate the languages
-such that each generated unity source file only contains sources for a single
-language.
+Unity builds are supported for the following languages:
+
+``C``
+  .. versionadded:: 3.16
+
+``CXX``
+  .. versionadded:: 3.16
+
+``OBJC``
+  .. versionadded:: 3.29
+
+``OBJCXX``
+  .. versionadded:: 3.29
+
+For targets that mix source files from more than one language, CMake
+separates the languages such that each generated unity source file only
+contains sources for a single language.
 
 This property is initialized by the value of the :variable:`CMAKE_UNITY_BUILD`
 variable when a target is created.
diff --git a/Help/prop_tgt/VERSION.rst b/Help/prop_tgt/VERSION.rst
index 95db483..f9cb020 100644
--- a/Help/prop_tgt/VERSION.rst
+++ b/Help/prop_tgt/VERSION.rst
@@ -1,10 +1,10 @@
 VERSION
 -------
 
-What version number is this target.
+Version number of a shared library target.
 
 For shared libraries ``VERSION`` and :prop_tgt:`SOVERSION` can be used
-to specify the build version and API version respectively.  When building or
+to specify the build version and ABI version respectively.  When building or
 installing appropriate symlinks are created if the platform supports
 symlinks and the linker supports so-names.  If only one of both is
 specified the missing is assumed to have the same version number.  For
@@ -12,6 +12,8 @@
 building or installing appropriate symlinks are created if the
 platform supports symlinks.
 
+.. include:: VERSION_SOVERSION_EXAMPLE.txt
+
 Windows Versions
 ^^^^^^^^^^^^^^^^
 
diff --git a/Help/prop_tgt/VERSION_SOVERSION_EXAMPLE.txt b/Help/prop_tgt/VERSION_SOVERSION_EXAMPLE.txt
new file mode 100644
index 0000000..ff2a958
--- /dev/null
+++ b/Help/prop_tgt/VERSION_SOVERSION_EXAMPLE.txt
@@ -0,0 +1,9 @@
+A common convention is to specify both ``VERSION`` and ``SOVERSION``
+such that ``SOVERSION`` matches the first component of ``VERSION``:
+
+.. code-block:: cmake
+
+  set_target_properties(mylib PROPERTIES VERSION 1.2.3 SOVERSION 1)
+
+The idea is that breaking changes to the ABI increment both the
+``SOVERSION`` and the major ``VERSION`` number.
diff --git a/Help/prop_tgt/XCODE_EMBED_type.rst b/Help/prop_tgt/XCODE_EMBED_type.rst
index e27d905..0354f97 100644
--- a/Help/prop_tgt/XCODE_EMBED_type.rst
+++ b/Help/prop_tgt/XCODE_EMBED_type.rst
@@ -43,6 +43,28 @@
   The specified items will be added to the ``Embed Resources`` build phase.
   They must be CMake target names or folder paths.
 
+``XPC_SERVICES``
+  .. versionadded:: 3.29
+
+  The specified items will be added to the ``Embed XPC Services`` build phase.
+  They must be CMake target names.
+
+When listing a target as any of the things to embed, Xcode must see that target
+as part of the same Xcode project, or a sub-project of the one defining the
+bundle.  In order to satisfy this constraint, the CMake project must ensure
+at least one of the following:
+
+* The :variable:`CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY` variable is set
+  to true in the top level ``CMakeLists.txt`` file.  This is the simplest and
+  most robust approach.
+* Define the target-to-embed in a subdirectory of the one that defines the
+  target being embedded into.
+* If the target-to-embed and the target being embedded into are in separate,
+  unrelated directories (i.e. they are siblings, not one a parent of the
+  other), ensure they have a common :command:`project` call in a parent
+  directory and no other :command:`project` calls between themselves and that
+  common :command:`project` call.
+
 See also :prop_tgt:`XCODE_EMBED_<type>_PATH`,
 :prop_tgt:`XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY` and
 :prop_tgt:`XCODE_EMBED_<type>_CODE_SIGN_ON_COPY`.
diff --git a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
index ef04d14..255aa68 100644
--- a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
+++ b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
@@ -25,3 +25,6 @@
 
 ``RESOURCES``
   .. versionadded:: 3.28
+
+``XPC_SERVICES``
+  .. versionadded:: 3.29
diff --git a/Help/release/3.14.rst b/Help/release/3.14.rst
index 5fedf7d..ab59ee2 100644
--- a/Help/release/3.14.rst
+++ b/Help/release/3.14.rst
@@ -302,7 +302,7 @@
 * A new :variable:`CMAKE_AUTOGEN_ORIGIN_DEPENDS` variable and
   :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` target property may be set to enable or
   disable forwarding of the origin target dependencies to the corresponding
-  ``_autogen`` target.
+  :ref:`<ORIGIN>_autogen` target.
 
 CTest
 -----
diff --git a/Help/release/3.22.rst b/Help/release/3.22.rst
index eba5d66..2d060ac 100644
--- a/Help/release/3.22.rst
+++ b/Help/release/3.22.rst
@@ -19,7 +19,7 @@
 * The :command:`string(TIMESTAMP)` command now supports the ``%V``
   specifier for ISO 8601 week numbers.
 
-.. _`OS identification variables`: https://www.freedesktop.org/software/systemd/man/os-release.html
+.. _`OS identification variables`: https://www.freedesktop.org/software/systemd/man/latest/os-release.html
 
 Variables
 ---------
diff --git a/Help/release/3.29.rst b/Help/release/3.29.rst
new file mode 100644
index 0000000..659237e
--- /dev/null
+++ b/Help/release/3.29.rst
@@ -0,0 +1,209 @@
+CMake 3.29 Release Notes
+************************
+
+.. only:: html
+
+  .. contents::
+
+Changes made since CMake 3.28 include the following.
+
+New Features
+============
+
+Command-Line
+------------
+
+* :manual:`cmake(1)` :option:`-E cat <cmake-E cat>` can now print the standard
+  input by passing the ``-`` argument.
+
+Generators
+----------
+
+* :ref:`Visual Studio Generators` now support selecting between the
+  Intel oneAPI Fortran compiler (``ifx``) and the Intel classic Fortran
+  compiler (``ifort``) using a ``fortran=`` field in
+  :variable:`CMAKE_GENERATOR_TOOLSET`.
+
+File-Based API
+--------------
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 ``version`` field has
+  been updated to 2.7.
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 "target" object gained
+  a new "launchers" field.
+
+Compilers
+---------
+
+* The LLVM/Clang GNU-like frontend on Windows (``clang++``) may now be used
+  to compile ``CUDA`` language sources.
+
+* Compilers targeting the GNU ABI on Windows (MinGW) may now be used to
+  compile Objective C (``OBJC``) and Objective C++ (``OBJCXX``).  These
+  include GNU compilers (``gcc`` and ``g++``) and the LLVM/Clang GNU-like
+  frontends (``clang`` and ``clang++``).
+
+* TI Clang-based compilers are now supported with
+  :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``TIClang``.
+
+Commands
+--------
+
+* The :ref:`add_custom_command(TARGET) <add_custom_command(TARGET)>`
+  signature now supports adding build events through :ref:`Alias Targets`.
+
+* The :command:`cmake_language(EXIT)` sub-command was added to terminate
+  :option:`cmake -P` scripts with a specified exit code.
+
+* The :command:`export(SETUP)` sub-command was added to configure export sets.
+  Its ``PACKAGE_DEPENDENCY`` option configures how :command:`find_dependency`
+  calls are exported.  Its ``TARGET`` option's ``XCFRAMEWORK_LOCATION``
+  setting specifies the location of a ``.xcframework`` that can be
+  substituted for an installed target.
+
+* The :command:`if` command gained new tests ``IS_READABLE``, ``IS_WRITABLE``
+  and ``IS_EXECUTABLE`` to check file or directory permissions.
+
+* :command:`install(EXPORT)` and :command:`export(EXPORT)` learned a new
+  ``EXPORT_PACKAGE_DEPENDENCIES`` argument, which can be used to generate
+  :command:`find_dependency` calls based on what targets the exported targets
+  depend on.
+
+Variables
+---------
+
+* The :envvar:`CMAKE_INSTALL_PREFIX` environment variable was added to
+  provide a default value for the :variable:`CMAKE_INSTALL_PREFIX` variable.
+
+* The :variable:`CMAKE_LINKER_TYPE` variable and corresponding
+  :prop_tgt:`LINKER_TYPE` target property were added to specify
+  what linker to use with some toolchains.
+
+* The :variable:`CMAKE_<LANG>_COMPILER_LINKER`,
+  :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID`,
+  :variable:`CMAKE_<LANG>_COMPILER_LINKER_VERSION` and
+  :variable:`CMAKE_<LANG>_COMPILER_LINKER_FRONTEND_VARIANT` variables
+  were added to describe the linker used by the language's link step.
+
+* The :variable:`CMAKE_PROJECT_INCLUDE`,
+  :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
+  :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`, and
+  :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE` variables learned
+  to support a :ref:`semicolon-separated list <CMake Language Lists>` of
+  CMake language files to be included sequentially. These variables can also
+  reference module names to be found in :variable:`CMAKE_MODULE_PATH` or
+  builtin to CMake.
+
+* The :variable:`CMAKE_SKIP_TEST_ALL_DEPENDENCY` variable was added
+  to control whether the ``test`` (or ``RUN_TESTS``) buildsystem
+  target depends on the ``all`` (or ``ALL_BUILD``) target.
+
+* A :variable:`CMAKE_TEST_LAUNCHER` variable and corresponding
+  :prop_tgt:`TEST_LAUNCHER` target property were added to specify
+  a launcher to be used by executable targets when invoked by
+  tests added by the :command:`add_test` command.
+
+Properties
+----------
+
+* The :prop_tgt:`CROSSCOMPILING_EMULATOR` target property now
+  supports :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+* The :prop_tgt:`EXPORT_FIND_PACKAGE_NAME` target property was added to
+  allow targets to specify what package name to pass when exporting
+  :command:`find_dependency` calls. This property is initialized with a new
+  :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` variable.
+
+* The :prop_tgt:`UNITY_BUILD` target property now supports the
+  Objective C (``OBJC``) and Objective C++ (``OBJCXX``) languages.
+
+* The :prop_tgt:`XCODE_EMBED_XPC_SERVICES <XCODE_EMBED_<type>>` target property
+  was added to tell the :generator:`Xcode` generator what targets to put in
+  the ``Embed XPC Resources`` build phase.
+
+Modules
+-------
+
+* The :module:`CMakePackageConfigHelpers` module gained new
+  :command:`generate_apple_platform_selection_file` and
+  :command:`generate_apple_architecture_selection_file` functions, which can
+  be used to generate a file that includes another Apple-platform-specific
+  file or the includes an architecture-specific implementation of a package
+  for an Apple platform, respectively.
+
+* The :module:`FindOpenGL` module learned to find a GLU include
+  directory different than the GL include directory.  A new
+  ``OPENGL_INCLUDE_DIRS`` result variable provides all include
+  directories.
+
+CTest
+-----
+
+* :manual:`ctest(1)` gained a :option:`--http-header <ctest --http-header>`
+  option to add custom headers on submission to CDash.
+
+* :manual:`ctest(1)` gained the :option:`--tests-from-file <ctest
+  --tests-from-file>` and :option:`--exclude-from-file <ctest
+  --exclude-from-file>` options to run or exclude tests named in a file.
+
+* :manual:`ctest(1)` now supports :ref:`job server integration
+  <ctest-job-server-integration>` on POSIX systems.
+
+* The :command:`ctest_test` command gained options
+  ``INCLUDE_FROM_FILE`` and ``EXCLUDE_FROM_FILE`` to run or exclude
+  tests named in a file.
+
+CPack
+-----
+
+* The :cpack_gen:`CPack DEB Generator` :variable:`CPACK_DEBIAN_FILE_NAME`
+  variable may now be set without any suffix, and the ``.deb`` suffix
+  will be added automatically.
+
+* The :cpack_gen:`CPack RPM Generator` :variable:`CPACK_RPM_FILE_NAME`
+  variable may now be set without any suffix, and the ``.rpm`` suffix
+  will be added automatically.
+
+* The :cpack_gen:`CPack WIX Generator` gained a new variable,
+  :variable:`CPACK_WIX_INSTALL_SCOPE`, to control the
+  ``InstallScope`` property of WiX MSI installers.
+
+Other Changes
+=============
+
+* CMake learned to de-duplicate libraries on link lines based on linker
+  capabilities.  See policy :policy:`CMP0156`.
+
+* The :command:`add_test` command now honors
+  :variable:`CMAKE_CROSSCOMPILING_EMULATOR` only when cross-compiling.
+  See policy :policy:`CMP0158`.
+
+* :command:`FetchContent_MakeAvailable` now sets the
+  :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` variable for CMake projects.
+
+* On Windows, when targeting the MSVC ABI, the :command:`find_library` command
+  now accepts ``.a`` file names after first considering ``.lib``.  This is
+  symmetric with existing behavior when targeting the GNU ABI, in which the
+  command accepts ``.lib`` file names after first considering ``.a``.
+
+* On Windows, when targeting the MSVC ABI, the :command:`find_library` command
+  now considers ``.dll.lib`` file names before ``.lib``.  This is the default
+  suffix for DLL import libraries created by Rust toolchains for the MSVC ABI.
+
+* The :generator:`Ninja` and :generator:`NMake Makefiles` generators now use
+  the ``-external:I`` flag for system includes when using IntelLLVM as of
+  version 2021.4. The ``-external:W0`` flag is also used as of version 2022.2.
+
+* The :command:`create_test_sourcelist` command now provides a full path to
+  the generated driver source file.
+
+* The :variable:`CPACK_PRODUCTBUILD_DOMAINS` variable now defaults to true.
+  See policy :policy:`CMP0161`.
+
+* The :cpack_gen:`CPack WIX Generator` now produces WiX MSI installers
+  that create start menu and uninstall entries for all users by default,
+  as documented by the :variable:`CPACK_WIX_INSTALL_SCOPE` variable
+  ``perMachine`` value.  Previously, without a custom WiX template,
+  it produced installers that would only create start menu and uninstall
+  entries for the current user, even though they install for all users.
diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000..e4cc01e
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+  Developers should add similar notes for each topic branch
+  making a noteworthy change.  Each document should be named
+  and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/genex-quote.rst b/Help/release/dev/genex-quote.rst
new file mode 100644
index 0000000..61bcfc0
--- /dev/null
+++ b/Help/release/dev/genex-quote.rst
@@ -0,0 +1,4 @@
+genex-quote
+-----------
+
+* The :genex:`$<QUOTE>` generator expression was added to evaluate to ``"``.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index b84bdb4..a809467 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,12 +7,15 @@
   This file should include the adjacent "dev.txt" file
   in development versions but not in release versions.
 
+.. include:: dev.txt
+
 Releases
 ========
 
 .. toctree::
    :maxdepth: 1
 
+   3.29 <3.29>
    3.28 <3.28>
    3.27 <3.27>
    3.26 <3.26>
diff --git a/Help/variable/CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst b/Help/variable/CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst
new file mode 100644
index 0000000..223cfcc
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG
+---------------------------------------
+
+.. versionadded:: 3.29
+
+This variable is used to initialize the
+:prop_tgt:`AUTOGEN_BETTER_GRAPH_MULTI_CONFIG` property on all targets as they
+are created.  See that target property for additional information.
+
+By default ``CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG`` is unset.
diff --git a/Help/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst b/Help/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst
new file mode 100644
index 0000000..eabda43
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX
+-------------------------------------
+
+.. versionadded:: 3.29
+
+Command line length limit for autogen targets, i.e. ``moc`` or ``uic``,
+that triggers the use of response files on Windows instead of passing all
+arguments to the command line.
+
+By default ``CMAKE_AUTOGEN_COMMAND_LINE_LENGTH_MAX`` is unset.
diff --git a/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst
index f490974..52aa891 100644
--- a/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst
+++ b/Help/variable/CMAKE_AUTOGEN_ORIGIN_DEPENDS.rst
@@ -4,7 +4,14 @@
 .. versionadded:: 3.14
 
 Switch for forwarding origin target dependencies to the corresponding
-``_autogen`` targets.
+:ref:`<ORIGIN>_autogen` targets.
+
+  .. note::
+
+    If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
+    or :ref:`Makefile Generators`, additional target dependencies are added to
+    the :ref:`<ORIGIN>_autogen_timestamp_deps` target instead of the
+    :ref:`<ORIGIN>_autogen` target.
 
 This variable is used to initialize the :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`
 property on all the targets.  See that target property for additional
diff --git a/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst b/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
index 1c3a26c..2640389 100644
--- a/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
+++ b/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
@@ -19,5 +19,8 @@
 The command will be used to run :command:`try_run` generated executables,
 which avoids manual population of the ``TryRunResults.cmake`` file.
 
-It is also used as the default value for the
-:prop_tgt:`CROSSCOMPILING_EMULATOR` target property of executables.
+This variable is also used as the default value for the
+:prop_tgt:`CROSSCOMPILING_EMULATOR` target property of executables.  However,
+while :manual:`generator expressions <cmake-generator-expressions(7)>` are
+supported by the target property (since CMake 3.29), they are *not* supported
+by this variable's :command:`try_run` functionality.
diff --git a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
index 58818f4..b375b77 100644
--- a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
+++ b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
@@ -15,4 +15,14 @@
 that case it is recommended to remove the cache variables for this
 package from the cache using the cache editor or :option:`cmake -U`.
 
+Note that this variable can lead to inconsistent results within the project.
+Consider the case where a dependency is requested via :command:`find_package`
+from two different places within the project.  If the first call does not
+have the ``REQUIRED`` keyword, it will not find the dependency when
+``CMAKE_DISABLE_FIND_PACKAGE_<PackageName>`` is set to true for that
+dependency.  The project will proceed under the assumption that the dependency
+isn't available.  If the second call elsewhere in the project *does* have the
+``REQUIRED`` keyword, it can succeed.  Two different parts of the same project
+have then seen opposite results for the same dependency.
+
 See also the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable.
diff --git a/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst
new file mode 100644
index 0000000..b6c8b76
--- /dev/null
+++ b/Help/variable/CMAKE_EXPORT_FIND_PACKAGE_NAME.rst
@@ -0,0 +1,6 @@
+CMAKE_EXPORT_FIND_PACKAGE_NAME
+------------------------------
+
+.. versionadded:: 3.29
+
+Initializes the value of :prop_tgt:`EXPORT_FIND_PACKAGE_NAME`.
diff --git a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
index c2c2609..d78dd15 100644
--- a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
+++ b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
@@ -5,8 +5,8 @@
 
 This specifies what suffixes to add to library names when the
 :command:`find_library` command looks for libraries.  On Windows systems this
-is typically ``.lib`` and, depending on the compiler, ``.dll.a``, ``.a``
-(e.g. GCC and Clang), so when it tries to find the ``foo`` library, it will
-look for ``[<prefix>]foo.lib`` and/or ``[<prefix>]foo[.dll].a``, depending on
-the compiler used and the ``<prefix>`` specified in the
-:variable:`CMAKE_FIND_LIBRARY_PREFIXES`.
+is typically ``.lib`` and, depending on the compiler, ``.dll.lib``, ``.dll.a``,
+``.a`` (e.g. rustc, GCC, or Clang), so when it tries to find the ``foo``
+library, it will look for ``[<prefix>]foo[.dll].lib`` and/or
+``[<prefix>]foo[.dll].a``, depending on the compiler used and the ``<prefix>``
+specified in the :variable:`CMAKE_FIND_LIBRARY_PREFIXES`.
diff --git a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
index 4855477..b83fa01 100644
--- a/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
+++ b/Help/variable/CMAKE_GENERATOR_TOOLSET.rst
@@ -48,6 +48,20 @@
   See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA` and
   :variable:`CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR` variables.
 
+``fortran=<compiler>``
+  .. versionadded:: 3.29
+
+  Specify the Fortran compiler to use, among those that integrate with VS.
+  The value may be one of:
+
+  ``ifort``
+    Intel classic Fortran compiler.
+
+  ``ifx``
+    Intel oneAPI Fortran compiler.
+
+  See the :variable:`CMAKE_VS_PLATFORM_TOOLSET_FORTRAN` variable.
+
 ``host=<arch>``
   Specify the host tools architecture as ``x64`` or ``x86``.
   Supported by VS 2013 and above.
diff --git a/Help/variable/CMAKE_GLOBAL_AUTOGEN_TARGET.rst b/Help/variable/CMAKE_GLOBAL_AUTOGEN_TARGET.rst
index 7d3f9c3..2bf5f05 100644
--- a/Help/variable/CMAKE_GLOBAL_AUTOGEN_TARGET.rst
+++ b/Help/variable/CMAKE_GLOBAL_AUTOGEN_TARGET.rst
@@ -7,7 +7,7 @@
 
 When ``CMAKE_GLOBAL_AUTOGEN_TARGET`` is enabled, a custom target
 ``autogen`` is generated.  This target depends on all :prop_tgt:`AUTOMOC` and
-:prop_tgt:`AUTOUIC` generated ``<ORIGIN>_autogen`` targets in the project.
+:prop_tgt:`AUTOUIC` generated :ref:`<ORIGIN>_autogen` targets in the project.
 By building the global ``autogen`` target, all :prop_tgt:`AUTOMOC` and
 :prop_tgt:`AUTOUIC` files in the project will be generated.
 
@@ -19,10 +19,9 @@
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
 
-Note
-^^^^
+.. note::
 
-``<ORIGIN>_autogen`` targets by default inherit their origin target's
-dependencies.  This might result in unintended dependency target
-builds when only ``<ORIGIN>_autogen`` targets are built.  A solution is to
-disable :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` on the respective origin targets.
+    :ref:`<ORIGIN>_autogen` targets by default inherit their origin target's
+    dependencies. This might result in unintended dependency target builds when
+    only :ref:`<ORIGIN>_autogen` targets are built.  A solution is to disable
+    :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS` on the respective origin targets.
diff --git a/Help/variable/CMAKE_HOST_SYSTEM_NAME.rst b/Help/variable/CMAKE_HOST_SYSTEM_NAME.rst
index e892677..0fe6146 100644
--- a/Help/variable/CMAKE_HOST_SYSTEM_NAME.rst
+++ b/Help/variable/CMAKE_HOST_SYSTEM_NAME.rst
@@ -6,3 +6,5 @@
 On systems that have the uname command, this variable is set to the
 output of ``uname -s``.  ``Linux``, ``Windows``, and ``Darwin`` for macOS
 are the values found on the big three operating systems.
+
+For a list of possible values, see :variable:`CMAKE_SYSTEM_NAME`.
diff --git a/Help/variable/CMAKE_INSTALL_PREFIX.rst b/Help/variable/CMAKE_INSTALL_PREFIX.rst
index c76727e..ce7cb8b 100644
--- a/Help/variable/CMAKE_INSTALL_PREFIX.rst
+++ b/Help/variable/CMAKE_INSTALL_PREFIX.rst
@@ -4,8 +4,19 @@
 Install directory used by :command:`install`.
 
 If ``make install`` is invoked or ``INSTALL`` is built, this directory is
-prepended onto all install directories.  This variable defaults to
-``/usr/local`` on UNIX and ``c:/Program Files/${PROJECT_NAME}`` on Windows.
+prepended onto all install directories.
+
+This variable defaults as follows:
+
+* .. versionadded:: 3.29
+
+    If the :envvar:`CMAKE_INSTALL_PREFIX` environment variable is set,
+    its value is used as default for this variable.
+
+* ``c:/Program Files/${PROJECT_NAME}`` on Windows.
+
+* ``/usr/local`` on UNIX platforms.
+
 See :variable:`CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT` for how a
 project might choose its own default.
 
diff --git a/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst b/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst
index 93cc319..316fea2 100644
--- a/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst
+++ b/Help/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT.rst
@@ -5,9 +5,10 @@
 
 CMake sets this variable to a ``TRUE`` value when the
 :variable:`CMAKE_INSTALL_PREFIX` has just been initialized to
-its default value, typically on the first run of CMake within
-a new build tree.  This can be used by project code to change
-the default without overriding a user-provided value:
+its default value, typically on the first
+run of CMake within a new build tree and the :envvar:`CMAKE_INSTALL_PREFIX`
+environment variable is not set on the first run of CMake. This can be used
+by project code to change the default without overriding a user-provided value:
 
 .. code-block:: cmake
 
diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
index 6893eea..b1e2687 100644
--- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst
+++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
@@ -43,6 +43,7 @@
 ``SunPro``                      Oracle Solaris Studio
 ``Tasking``                     `Tasking Compiler Toolsets`_
 ``TI``                          Texas Instruments
+``TIClang``                     `Texas Instruments Clang-based Compilers`_
 ``TinyCC``                      `Tiny C Compiler`_
 ``XL``, ``VisualAge``, ``zOS``  IBM XL
 ``XLClang``                     IBM Clang-based XL
@@ -68,3 +69,4 @@
 .. _Small Device C Compiler: https://sdcc.sourceforge.net
 .. _Tiny C Compiler: https://bellard.org/tcc
 .. _Tasking Compiler Toolsets: https://www.tasking.com
+.. _Texas Instruments Clang-based Compilers: https://www.ti.com/tool/download/ARM-CGT-CLANG
diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst
new file mode 100644
index 0000000..c0ae1cd
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER.rst
@@ -0,0 +1,15 @@
+CMAKE_<LANG>_COMPILER_LINKER
+----------------------------
+
+.. versionadded:: 3.29
+
+The full path to the linker for ``LANG``.
+
+This is the command that will be used as the ``<LANG>`` linker.
+
+This variable is not guaranteed to be defined for all linkers or languages.
+
+.. note::
+  This variable is read-only. It must not be set by the user. To select a
+  specific linker, use the :variable:`CMAKE_LINKER_TYPE` variable or the
+  :prop_tgt:`LINKER_TYPE` target property.
diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst
new file mode 100644
index 0000000..d7d028a
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_FRONTEND_VARIANT.rst
@@ -0,0 +1,21 @@
+CMAKE_<LANG>_COMPILER_LINKER_FRONTEND_VARIANT
+---------------------------------------------
+
+.. versionadded:: 3.29
+
+Identification string of the linker frontend variant.
+
+Some linkers have multiple, different frontends for accepting command
+line options.  (For example ``LLCM LLD`` originally only had a frontend
+compatible with the ``GNU`` compiler but since its port to Windows
+(``lld-link``) it now also supports a frontend compatible with ``MSVC``.)
+When CMake detects such a linker it sets this variable to what would have been
+the :variable:`CMAKE_<LANG>_COMPILER_LINKER_ID` for the linker whose frontend
+it resembles.
+
+.. note::
+  In other words, this variable describes what command line options
+  and language extensions the linker frontend expects.
+
+  This variable is set for ``GNU``, ``MSVC``, ``MOLD`` and ``AppleClang``
+  linkers that have only one frontend variant.
diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst
new file mode 100644
index 0000000..0607aea
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_ID.rst
@@ -0,0 +1,33 @@
+CMAKE_<LANG>_COMPILER_LINKER_ID
+-------------------------------
+
+.. versionadded:: 3.29
+
+Linker identification string.
+
+A short string unique to the linker vendor.  Possible values
+include:
+
+=============================== ===============================================
+Value                           Name
+=============================== ===============================================
+``AppleClang``                  Apple Clang
+``LLD``                         `LLVM LLD`_
+``GNU``                         `GNU Binutils - ld linker`_ (also known as
+                                ``bfd``)
+``GNUgold``                     `GNU Binutils - gold linker`_
+``MSVC``                        `Microsoft Visual Studio`_
+``MOLD``                        `mold: A Modern Linker`_ or, on Apple, `sold`_
+                                linker
+``AIX``                         AIX system linker
+``Solaris``                     SunOS system linker
+=============================== ===============================================
+
+This variable is not guaranteed to be defined for all linkers or languages.
+
+.. _LLVM LLD: https://lld.llvm.org
+.. _GNU Binutils - ld linker: https://sourceware.org/binutils
+.. _GNU Binutils - gold linker: https://sourceware.org/binutils
+.. _Microsoft Visual Studio: https://visualstudio.microsoft.com
+.. _mold\: A Modern Linker: https://github.com/rui314/mold
+.. _sold: https://github.com/bluewhalesystems/sold
diff --git a/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst b/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst
new file mode 100644
index 0000000..72b0551
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_COMPILER_LINKER_VERSION.rst
@@ -0,0 +1,10 @@
+CMAKE_<LANG>_COMPILER_LINKER_VERSION
+------------------------------------
+
+.. versionadded:: 3.29
+
+Linker version string.
+
+Linker version in major[.minor[.patch[.tweak]]] format.  This
+variable is not guaranteed to be defined for all linkers or
+languages.
diff --git a/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst b/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst
new file mode 100644
index 0000000..c9b6779
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_USING_LINKER_MODE.rst
@@ -0,0 +1,15 @@
+CMAKE_<LANG>_USING_LINKER_MODE
+------------------------------
+
+.. versionadded:: 3.29
+
+This variable specify what is the type of data stored in variable
+ :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>`. There are two possible values:
+
+``FLAG``
+  :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds compiler flags. This is
+  the default.
+
+``TOOL``
+  :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` holds the path to the linker
+  tool.
diff --git a/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst b/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst
new file mode 100644
index 0000000..e4d9fa6
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_USING_LINKER_TYPE.rst
@@ -0,0 +1,30 @@
+CMAKE_<LANG>_USING_LINKER_<TYPE>
+--------------------------------
+
+.. versionadded:: 3.29
+
+This variable defines how to specify the linker for the link step for the type
+as specified by the variable :variable:`CMAKE_LINKER_TYPE` or the target
+property :prop_tgt:`LINKER_TYPE`. It can hold compiler flags for the link step
+or directly the linker tool. The type of data is given by the variable
+:variable:`CMAKE_<LANG>_USING_LINKER_MODE`.
+
+For example, to specify the ``LLVM`` linker for ``GNU`` compilers, we have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld")
+
+Or on ``Windows`` platform, for ``Clang`` compilers simulating ``MSVC``, we
+have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "-fuse-ld=lld-link")
+
+And for the ``MSVC`` compiler, linker is directly used, so we have:
+
+.. code-block:: cmake
+
+  set(CMAKE_C_USING_LINKER_LLD "/path/to/lld-link.exe")
+  set(CMAKE_C_USING_LINKER_MODE TOOL)
diff --git a/Help/variable/CMAKE_LINKER_TYPE.rst b/Help/variable/CMAKE_LINKER_TYPE.rst
new file mode 100644
index 0000000..17ea947
--- /dev/null
+++ b/Help/variable/CMAKE_LINKER_TYPE.rst
@@ -0,0 +1,18 @@
+CMAKE_LINKER_TYPE
+-----------------
+
+.. versionadded:: 3.29
+
+Specify which linker will be used for the link step.
+
+.. note::
+  It is assumed that the linker specified is fully compatible with the standard
+  one. CMake will not do any options translation.
+
+This variable is used to initialize the :prop_tgt:`LINKER_TYPE` target
+property when they are created by calls to :command:`add_library` or
+:command:`add_executable` commands. It is meaningful only for targets having a
+link step. If set, its value is also used by the :command:`try_compile`
+command.
+
+.. include:: LINKER_PREDEFINED_TYPES.txt
diff --git a/Help/variable/CMAKE_MATCH_n.rst b/Help/variable/CMAKE_MATCH_n.rst
index a92788e..c7dd623 100644
--- a/Help/variable/CMAKE_MATCH_n.rst
+++ b/Help/variable/CMAKE_MATCH_n.rst
@@ -1,8 +1,6 @@
 CMAKE_MATCH_<n>
 ---------------
 
-.. versionadded:: 3.9
-
 Capture group ``<n>`` matched by the last regular expression, for groups
 0 through 9.  Group 0 is the entire match.  Groups 1 through 9 are the
 subexpressions captured by ``()`` syntax.
diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE.rst
index 76b9d92..217604f 100644
--- a/Help/variable/CMAKE_PROJECT_INCLUDE.rst
+++ b/Help/variable/CMAKE_PROJECT_INCLUDE.rst
@@ -3,12 +3,17 @@
 
 .. versionadded:: 3.15
 
-A CMake language file or module to be included as the last step of all
+A CMake language file to be included as the last step of all
 :command:`project` command calls.  This is intended for injecting custom code
 into project builds without modifying their source.  See :ref:`Code Injection`
 for a more detailed discussion of files potentially included during a
 :command:`project` call.
 
+.. versionadded:: 3.29
+  This variable can be a :ref:`semicolon-separated list <CMake Language Lists>`
+  of CMake language files to be included sequentially. It can also now refer to
+  module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake.
+
 See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
 :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
 :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`, and
diff --git a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst
index 9a8c4b5..1dc0241 100644
--- a/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst
+++ b/Help/variable/CMAKE_PROJECT_INCLUDE_BEFORE.rst
@@ -3,12 +3,17 @@
 
 .. versionadded:: 3.15
 
-A CMake language file or module to be included as the first step of all
+A CMake language file to be included as the first step of all
 :command:`project` command calls.  This is intended for injecting custom code
 into project builds without modifying their source.  See :ref:`Code Injection`
 for a more detailed discussion of files potentially included during a
 :command:`project` call.
 
+.. versionadded:: 3.29
+  This variable can be a :ref:`semicolon-separated list <CMake Language Lists>`
+  of CMake language files to be included sequentially. It can also now refer to
+  module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake.
+
 See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
 :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
 :variable:`CMAKE_PROJECT_INCLUDE`, and
diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst
index 3bb5cd8..6d37c1a 100644
--- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst
+++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE.rst
@@ -1,12 +1,17 @@
 CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE
 ------------------------------------
 
-A CMake language file or module to be included as the last step of any
+A CMake language file to be included as the last step of any
 :command:`project` command calls that specify ``<PROJECT-NAME>`` as the project
 name.  This is intended for injecting custom code into project builds without
 modifying their source.  See :ref:`Code Injection` for a more detailed
 discussion of files potentially included during a :command:`project` call.
 
+.. versionadded:: 3.29
+  This variable can be a :ref:`semicolon-separated list <CMake Language Lists>`
+  of CMake language files to be included sequentially. It can also now refer to
+  module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake.
+
 See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE_BEFORE`,
 :variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
 and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.
diff --git a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst
index ca584c1..96ddbf6 100644
--- a/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst
+++ b/Help/variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE.rst
@@ -3,12 +3,17 @@
 
 .. versionadded:: 3.17
 
-A CMake language file or module to be included as the first step of any
+A CMake language file to be included as the first step of any
 :command:`project` command calls that specify ``<PROJECT-NAME>`` as the project
 name.  This is intended for injecting custom code into project builds without
 modifying their source.  See :ref:`Code Injection` for a more detailed
 discussion of files potentially included during a :command:`project` call.
 
+.. versionadded:: 3.29
+  This variable can be a :ref:`semicolon-separated list <CMake Language Lists>`
+  of CMake language files to be included sequentially. It can also now refer to
+  module names to be found in :variable:`CMAKE_MODULE_PATH` or builtin to CMake.
+
 See also the :variable:`CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE`,
 :variable:`CMAKE_PROJECT_INCLUDE`, :variable:`CMAKE_PROJECT_INCLUDE_BEFORE`,
 and :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variables.
diff --git a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst
index 2010b08..54f530e 100644
--- a/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst
+++ b/Help/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES.rst
@@ -12,6 +12,10 @@
 See :ref:`Code Injection` for a more detailed discussion of files potentially
 included during a :command:`project` call.
 
+.. versionadded:: 3.29
+  This variable can also now refer to module names to be found in
+  :variable:`CMAKE_MODULE_PATH` or builtin to CMake.
+
 This variable is intended for specifying files that perform one-time setup
 for the build. It provides an injection point for things like configuring
 package managers, adding logic the user shares between projects (e.g. defining
diff --git a/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst
index 893f1ae..52bf30a 100644
--- a/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst
+++ b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst
@@ -11,4 +11,35 @@
 This can be used to assert assumptions about build environment and to
 ensure the build will fail early if they do not hold.
 
+Note that setting this variable to true breaks some commonly used patterns.
+Multiple calls to :command:`find_package` are sometimes used to obtain a
+different search order to the default.
+For example, projects can force checking a known path for a particular
+package first before searching any of the other default search paths:
+
+.. code:: cmake
+
+  find_package(something PATHS /some/local/path NO_DEFAULT_PATH)
+  find_package(something)
+
+In the above, the first call looks for the ``something`` package in a specific
+directory.  If ``CMAKE_REQUIRE_FIND_PACKAGE_something`` is set to true, then
+this first call must succeed, otherwise a fatal error occurs.  The second call
+never gets a chance to provide a fall-back to using the default search
+locations.
+
+A similar pattern is used even by some of CMake's own Find modules to search
+for a config package first:
+
+.. code:: cmake
+
+  find_package(something CONFIG QUIET)
+  if(NOT something_FOUND)
+    # Fall back to searching using typical Find module logic...
+  endif()
+
+Again, if ``CMAKE_REQUIRE_FIND_PACKAGE_something`` is true, the first call
+must succeed.  It effectively means a config package must be found for the
+dependency, and the Find module logic is never used.
+
 See also the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable.
diff --git a/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst b/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst
index e88db36..69c762b 100644
--- a/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst
+++ b/Help/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY.rst
@@ -9,3 +9,5 @@
 If ``CMAKE_SKIP_INSTALL_ALL_DEPENDENCY`` is set to ``TRUE``, this
 dependency is not created, so the installation process will start immediately,
 independent from whether the project has been completely built or not.
+
+See also :variable:`CMAKE_SKIP_TEST_ALL_DEPENDENCY`.
diff --git a/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst b/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst
new file mode 100644
index 0000000..bae8e99
--- /dev/null
+++ b/Help/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY.rst
@@ -0,0 +1,19 @@
+CMAKE_SKIP_TEST_ALL_DEPENDENCY
+------------------------------
+
+.. versionadded:: 3.29
+
+Control whether the ``test`` target depends on the ``all`` target.
+
+If this variable is not defined, or is set to ``TRUE``, then the
+``test`` (or ``RUN_TESTS``) target does not depend on the
+``all`` (or ``ALL_BUILD``) target.  When the ``test`` target is built,
+e.g., via ``make test``, the test process will start immediately,
+regardless of whether the project has been completely built or not.
+
+If ``CMAKE_SKIP_TEST_ALL_DEPENDENCY`` is explicitly set to ``FALSE``,
+then the ``test`` target will depend on the ``all`` target.  When the
+``test`` target is built, e.g., via ``make test``, the ``all`` target
+will be built first, and then the tests will run.
+
+See also :variable:`CMAKE_SKIP_INSTALL_ALL_DEPENDENCY`.
diff --git a/Help/variable/CMAKE_SYSTEM_NAME.rst b/Help/variable/CMAKE_SYSTEM_NAME.rst
index fef53ee..e9ffec4 100644
--- a/Help/variable/CMAKE_SYSTEM_NAME.rst
+++ b/Help/variable/CMAKE_SYSTEM_NAME.rst
@@ -21,3 +21,86 @@
 tree in order to enable :ref:`cross compiling <Cross Compiling Toolchain>`.
 In this case the :variable:`CMAKE_SYSTEM_VERSION` variable must also be
 set explicitly.
+
+System Names Known to CMake
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The following is a list of possible values, each associated with corresponding
+operating systems or environments.
+
+========================= ======================================================
+Value                     Name
+========================= ======================================================
+``ADSP``                  Analog Devices Audio Digital Signal Processing
+``AIX``                   IBM Unix operating system
+``Android``               Android operating system
+``ARTOS``                 Operating system for microcontrollers
+``BeOS``                  Operating system for personal computers (discontinued)
+``BlueGeneL``             Blue Gene/L static environment
+``BlueGeneP-dynamic``     Blue Gene/P dynamic environment
+``BlueGeneP-static``      Blue Gene/P static environment
+``BlueGeneQ-dynamic``     Blue Gene/Q dynamic environment
+``BlueGeneQ-static``      Blue Gene/Q static environment
+``BSDOS``                 BSD operating system (discontinued)
+``Catamount``             Operating system for Cray XT series
+``CrayLinuxEnvironment``  Cray Linux Environment
+``CYGWIN``                Cygwin environment for Windows
+``Darwin``                Apple stationary operating systems (macOS, OS X, etc.)
+``DOS``                   MS-DOS or compatible
+``DragonFly``             BSD-derived operating system
+``eCos``                  Real-time embedded operating system
+``Emscripten``            Compiler toolchain to WebAssembly
+``Euros``                 Real-time operating system for embedded devices
+``FreeBSD``               FreeBSD operating system
+``Fuchsia``               Operating system by Google based on the Zircon kernel
+``Generic-ADSP``          Generic ADSP (Audio DSP) environment
+``Generic-ELF``           Generic ELF (Executable and Linkable Format) environment
+``Generic``               Some platforms, e.g. bare metal embedded devices
+``GHS-MULTI``             Green Hills Software MULTI environment
+``GNU``                   GNU/Hurd-based operating system
+``Haiku``                 Unix operating system inspired by BeOS
+``HP-UX``                 Hewlett Packard Unix
+``iOS``                   Apple mobile phone operating system
+``kFreeBSD``              FreeBSD kernel with a GNU userland
+``Linux``                 All Linux-based distributions
+``Midipix``               POSIX-compatible layer for Windows
+``MirBSD``                MirOS BSD operating system
+``MP-RAS``                MP-RAS UNIX operating system
+``MSYS``                  MSYS environment (MSYSTEM=MSYS)
+``NetBSD``                NetBSD operating systems
+``OpenBSD``               OpenBSD operating systems
+``OpenVMS``               OpenVMS operating system by HP
+``OS2``                   OS/2 operating system
+``OSF1``                  Compaq Tru64 UNIX (formerly DEC OSF/1, Digital Unix) (discontinued)
+``QNX``                   Unix-like operating system by BlackBerry
+``RISCos``                RISC OS operating system
+``SCO_SV``                SCO OpenServer 5
+``SerenityOS``            Unix-like operating system
+``SINIX``                 SINIX operating system
+``SunOS``                 Oracle Solaris and all illumos operating systems
+``syllable``              Syllable operating system
+``Tru64``                 Compaq Tru64 UNIX (formerly DEC OSF/1) operating system
+``tvOS``                  Apple TV operating system
+``ULTRIX``                Unix operating system (discontinued)
+``UNIX_SV``               SCO UnixWare (pre release 7)
+``UnixWare``              SCO UnixWare 7
+``visionOS``              Apple mixed reality operating system
+``watchOS``               Apple watch operating system
+``Windows``               Windows stationary operating systems
+``WindowsCE``             Windows Embedded Compact
+``WindowsPhone``          Windows mobile phone operating system
+``WindowsStore``          Universal Windows Platform applications
+``Xenix``                 SCO Xenix Unix operating system (discontinued)
+========================= ======================================================
+
+Platform-specific notes:
+
+* MSYS2's ``msys/cmake`` package (``/usr/bin/cmake``) works only under
+  ``MSYSTEM=MSYS`` environments, with system name ``MSYS``.  Under other
+  environments like ``MSYSTEM=MINGW64``, use another package such
+  as ``mingw64/mingw-w64-x86_64-cmake`` (``/mingw64/bin/cmake``),
+  which targets ``MSYSTEM=MINGW64`` with system name ``Windows``.
+
+* Cygwin's ``cmake`` package (``/usr/bin/cmake``) uses system name ``CYGWIN``.
+  A non-cygwin CMake on Windows (e.g. ``$PROGRAMFILES/CMake/bin/cmake``)
+  uses system name ``Windows`` even when it runs under a Cygwin environment.
diff --git a/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst b/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst
new file mode 100644
index 0000000..5e55d8c
--- /dev/null
+++ b/Help/variable/CMAKE_Swift_COMPILATION_MODE.rst
@@ -0,0 +1,32 @@
+CMAKE_Swift_COMPILATION_MODE
+----------------------------
+
+.. versionadded:: 3.29
+
+Specify how Swift compiles a target. This variable is used to initialize the
+:prop_tgt:`Swift_COMPILATION_MODE` property on targets as they are created.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/Swift_COMPILATION_MODE-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to support
+per-configuration specification. For example, the code:
+
+.. code-block:: cmake
+
+   set(CMAKE_Swift_COMPILATION_MODE
+     "$<IF:$<CONFIG:Release>,wholemodule,incremental>")
+
+sets the default Swift compilation mode to wholemodule mode when building a
+release configuration and to incremental mode in other configurations.
+
+If this variable is not set then the :prop_tgt:`Swift_COMPILATION_MODE` target
+property will not be set automatically. If that property is unset then CMake
+uses the default value ``incremental`` to build the Swift source files.
+
+.. note::
+
+   This property only has effect when policy :policy:`CMP0157` is set to ``NEW``
+   prior to the first :command:`project` or :command:`enable_language` command
+   that enables the Swift language.
diff --git a/Help/variable/CMAKE_TEST_LAUNCHER.rst b/Help/variable/CMAKE_TEST_LAUNCHER.rst
new file mode 100644
index 0000000..2a5fe4c
--- /dev/null
+++ b/Help/variable/CMAKE_TEST_LAUNCHER.rst
@@ -0,0 +1,16 @@
+CMAKE_TEST_LAUNCHER
+-------------------
+
+.. versionadded:: 3.29
+
+This variable is used to initialize the :prop_tgt:`TEST_LAUNCHER` target
+property of executable targets as they are created.  It is used to specify
+a launcher for running tests, added by the :command:`add_test` command,
+that run an executable target.
+
+If this variable contains a :ref:`semicolon-separated list <CMake Language
+Lists>`, then the first value is the command and remaining values are its
+arguments.
+
+This variable can be initialized via an
+:envvar:`CMAKE_TEST_LAUNCHER` environment variable.
diff --git a/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst
new file mode 100644
index 0000000..e0ecb12
--- /dev/null
+++ b/Help/variable/CMAKE_VS_PLATFORM_TOOLSET_FORTRAN.rst
@@ -0,0 +1,12 @@
+CMAKE_VS_PLATFORM_TOOLSET_FORTRAN
+---------------------------------
+
+.. versionadded:: 3.29
+
+Fortran compiler to be used by Visual Studio projects.
+
+:ref:`Visual Studio Generators` support selecting among Fortran compilers
+whose Visual Studio Integration is installed.  The compiler may be specified
+by a field in :variable:`CMAKE_GENERATOR_TOOLSET` of the form ``fortran=...``.
+CMake provides the selected Fortran compiler in this variable.
+The value may be empty if the field was not specified.
diff --git a/Help/variable/LINKER_PREDEFINED_TYPES.txt b/Help/variable/LINKER_PREDEFINED_TYPES.txt
new file mode 100644
index 0000000..e8cb19f
--- /dev/null
+++ b/Help/variable/LINKER_PREDEFINED_TYPES.txt
@@ -0,0 +1,59 @@
+Linker types are case-sensitive and may only contain letters, numbers and
+underscores. Linker types defined in all uppercase are reserved for CMake's own
+built-in types. The pre-defined linker types are:
+
+``DEFAULT``
+  This type corresponds to standard linking, essentially equivalent to
+  not specifying :prop_tgt:`LINKER_TYPE` target property.
+
+``SYSTEM``
+  Use the standard linker delivered by the platform or the standard toolkit
+  (for example, ``SYSTEM`` imply Microsoft linker for all ``MSVC`` compatible
+  compilers). This type is supported for the following platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers.
+  * All Apple variants for ``AppleClang``, ``Clang`` and ``GNU`` compilers.
+  * Windows, for ``MSVC``, ``GNU``, ``Clang`` and ``NVIDIA`` compilers.
+
+``LLD``
+  Use the ``LLVM`` linker. This type is supported for the following
+  platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers.
+  * All Apple variants for ``Clang`` and ``AppleClang`` compilers.
+  * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end and
+    ``CLang``, ``MSVC`` and ``NVIDIA`` compilers with ``MSVC`` front-end.
+
+``BFD``
+  Use the ``GNU`` linker.  This type is supported for the following
+  platforms/compilers:
+
+  * Linux, for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA`` compilers.
+  * Windows, for ``GNU``, ``Clang`` compilers with ``GNU`` front-end.
+
+``GOLD``
+  Supported on Linux platform for ``GNU``, ``Clang``, ``LLVMFlang`` and
+  ``NVIDIA`` compilers.
+
+``MOLD``
+  Use the `mold linker <https://github.com/rui314/mold>`_. This type is
+  supported on the following platforms:
+
+  * Linux platform for ``GNU``, ``Clang``, ``LLVMFlang`` and ``NVIDIA``
+    compilers.
+  * All Apple variants for ``Clang`` and ``AppleClang`` compilers as an
+    alias to ``SOLD``.
+
+``SOLD``
+  Use the `sold linker <https://github.com/bluewhalesystems/sold>`_. This type
+  is only supported on Apple platforms for ``Clang`` and ``AppleClang``
+  compilers.
+
+``APPLE_CLASSIC``
+  Use the Apple linker in the classic behavior (i.e. before ``Xcode 15.0``).
+  This type is only supported on Apple platforms for ``GNU``, ``Clang`` and
+  ``AppleClang`` compilers.
+
+``MSVC``
+  Use the Microsoft linker. This type is only supported on Windows
+  platform for ``MSVC`` and ``Clang`` compiler with ``MSVC`` front-end.
diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in
index 1efd9f5..c7dbfae 100644
--- a/Modules/CMakeASMCompiler.cmake.in
+++ b/Modules/CMakeASMCompiler.cmake.in
@@ -5,6 +5,12 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER "@CMAKE_ASM_COMPILER_LINKER@")
+set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_ID "@CMAKE_ASM_COMPILER_LINKER_ID@")
+set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_VERSION @CMAKE_ASM_COMPILER_LINKER_VERSION@)
+set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_ASM_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)
diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in
index 2f0b774..df2a060 100644
--- a/Modules/CMakeCCompiler.cmake.in
+++ b/Modules/CMakeCCompiler.cmake.in
@@ -26,6 +26,12 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_C_COMPILER_LINKER "@CMAKE_C_COMPILER_LINKER@")
+set(CMAKE_C_COMPILER_LINKER_ID "@CMAKE_C_COMPILER_LINKER_ID@")
+set(CMAKE_C_COMPILER_LINKER_VERSION @CMAKE_C_COMPILER_LINKER_VERSION@)
+set(CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake
index 665f309..998e476 100644
--- a/Modules/CMakeCInformation.cmake
+++ b/Modules/CMakeCInformation.cmake
@@ -67,7 +67,7 @@
 endif ()
 
 if(CMAKE_C_SIZEOF_DATA_PTR)
-  foreach(f ${CMAKE_C_ABI_FILES})
+  foreach(f IN LISTS CMAKE_C_ABI_FILES)
     include(${f})
   endforeach()
   unset(CMAKE_C_ABI_FILES)
diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in
index 3c28c28..3af02a4 100644
--- a/Modules/CMakeCUDACompiler.cmake.in
+++ b/Modules/CMakeCUDACompiler.cmake.in
@@ -72,5 +72,12 @@
 @_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT@
 
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_CUDA_COMPILER_LINKER "@CMAKE_CUDA_COMPILER_LINKER@")
+set(CMAKE_CUDA_COMPILER_LINKER_ID "@CMAKE_CUDA_COMPILER_LINKER_ID@")
+set(CMAKE_CUDA_COMPILER_LINKER_VERSION @CMAKE_CUDA_COMPILER_LINKER_VERSION@)
+set(CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CUDA_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_AR "@CMAKE_AR@")
+set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_MT "@CMAKE_MT@")
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index 8b6f82b..4459898 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -27,6 +27,12 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_CXX_COMPILER_LINKER "@CMAKE_CXX_COMPILER_LINKER@")
+set(CMAKE_CXX_COMPILER_LINKER_ID "@CMAKE_CXX_COMPILER_LINKER_ID@")
+set(CMAKE_CXX_COMPILER_LINKER_VERSION @CMAKE_CXX_COMPILER_LINKER_VERSION@)
+set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
@@ -40,7 +46,7 @@
 set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm;ccm;cxxm;c++m)
 set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
 
-foreach (lang C OBJC OBJCXX)
+foreach (lang IN ITEMS C OBJC OBJCXX)
   if (CMAKE_${lang}_COMPILER_ID_RUN)
     foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS)
       list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension})
diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake
index 53abf37..3753d18 100644
--- a/Modules/CMakeCXXInformation.cmake
+++ b/Modules/CMakeCXXInformation.cmake
@@ -66,7 +66,7 @@
 endif ()
 
 if(CMAKE_CXX_SIZEOF_DATA_PTR)
-  foreach(f ${CMAKE_CXX_ABI_FILES})
+  foreach(f IN LISTS CMAKE_CXX_ABI_FILES)
     include(${f})
   endforeach()
   unset(CMAKE_CXX_ABI_FILES)
@@ -182,7 +182,7 @@
 endif()
 
 # Initialize CXX link type selection flags from C versions.
-foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
+foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE)
   if(NOT CMAKE_${type}_LINK_STATIC_CXX_FLAGS)
     set(CMAKE_${type}_LINK_STATIC_CXX_FLAGS
       ${CMAKE_${type}_LINK_STATIC_C_FLAGS})
diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake
index 7eb93e2..2817d37 100644
--- a/Modules/CMakeCompilerIdDetection.cmake
+++ b/Modules/CMakeCompilerIdDetection.cmake
@@ -42,11 +42,6 @@
 
     # Order is relevant here. For example, compilers which pretend to be
     # GCC must appear before the actual GCC.
-    if ("x${lang}" STREQUAL "xCXX")
-      list(APPEND ordered_compilers
-        Comeau
-      )
-    endif()
     list(APPEND ordered_compilers
       Intel
       IntelLLVM
@@ -85,6 +80,7 @@
       ARMCC
       AppleClang
       ARMClang
+      TIClang
     )
     list(APPEND ordered_compilers
       Clang
diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake
index 6d7d17e..2042e64 100644
--- a/Modules/CMakeDetermineASMCompiler.cmake
+++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -102,6 +102,10 @@
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_TI "-h")
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_TI "Texas Instruments")
 
+  list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS TIClang )
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_TIClang "--version")
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_TIClang "(TI (.*) Clang Compiler)")
+
   list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS IAR)
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_IAR )
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_IAR "IAR Assembler")
diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake
index 8beebc5..73b6cee 100644
--- a/Modules/CMakeDetermineCCompiler.cmake
+++ b/Modules/CMakeDetermineCCompiler.cmake
@@ -166,6 +166,11 @@
       set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
       set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_4})
       set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_6})
+    elseif(CMAKE_C_COMPILER_ID MATCHES "TIClang")
+       if (COMPILER_BASENAME MATCHES "^(.+)?clang(\\.exe)?$")
+         set(_CMAKE_TOOLCHAIN_PREFIX "${CMAKE_MATCH_1}")
+         set(_CMAKE_TOOLCHAIN_SUFFIX "${CMAKE_MATCH_2}")
+       endif()
     elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
       if(CMAKE_C_COMPILER_TARGET)
         set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_C_COMPILER_TARGET}-)
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake
index 6ac4dad..70528da 100644
--- a/Modules/CMakeDetermineCUDACompiler.cmake
+++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -74,10 +74,6 @@
     set(CMAKE_CUDA_COMPILER_ID_VENDOR_REGEX_Clang "(clang version)")
     CMAKE_DETERMINE_COMPILER_ID_VENDOR(CUDA "--version")
 
-    if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" AND WIN32)
-      message(FATAL_ERROR "Clang with CUDA is not yet supported on Windows. See CMake issue #20776.")
-    endif()
-
     # Find the CUDA toolkit to get:
     # - CMAKE_CUDA_COMPILER_TOOLKIT_VERSION
     # - CMAKE_CUDA_COMPILER_TOOLKIT_ROOT
diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake
index 40934ec..891ba6e 100644
--- a/Modules/CMakeDetermineCXXCompiler.cmake
+++ b/Modules/CMakeDetermineCXXCompiler.cmake
@@ -171,6 +171,11 @@
       set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_MATCH_1})
       set(_CMAKE_TOOLCHAIN_SUFFIX ${CMAKE_MATCH_3})
       set(_CMAKE_COMPILER_SUFFIX ${CMAKE_MATCH_5})
+    elseif(CMAKE_CXX_COMPILER_ID MATCHES "TIClang")
+       if (COMPILER_BASENAME MATCHES "^(.+)?clang(\\.exe)?$")
+         set(_CMAKE_TOOLCHAIN_PREFIX "${CMAKE_MATCH_1}")
+         set(_CMAKE_TOOLCHAIN_SUFFIX "${CMAKE_MATCH_2}")
+       endif()
     elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
       if(CMAKE_CXX_COMPILER_TARGET)
         set(_CMAKE_TOOLCHAIN_PREFIX ${CMAKE_CXX_COMPILER_TARGET}-)
diff --git a/Modules/CMakeDetermineCompiler.cmake b/Modules/CMakeDetermineCompiler.cmake
index 3156ca9..fc0b714 100644
--- a/Modules/CMakeDetermineCompiler.cmake
+++ b/Modules/CMakeDetermineCompiler.cmake
@@ -15,7 +15,7 @@
     set(_${lang}_COMPILER_LIST "${CMAKE_${lang}_COMPILER_LIST}")
     set(CMAKE_${lang}_COMPILER_LIST "")
     # Prefer vendors of compilers from reference languages.
-    foreach(l ${_languages})
+    foreach(l IN LISTS _languages)
       list(APPEND CMAKE_${lang}_COMPILER_LIST
         ${_${lang}_COMPILER_NAMES_${CMAKE_${l}_COMPILER_ID}})
     endforeach()
@@ -33,7 +33,7 @@
 
   # Look for directories containing compilers of reference languages.
   set(_${lang}_COMPILER_HINTS "${CMAKE_${lang}_COMPILER_HINTS}")
-  foreach(l ${_languages})
+  foreach(l IN LISTS _languages)
     if(CMAKE_${l}_COMPILER AND IS_ABSOLUTE "${CMAKE_${l}_COMPILER}")
       get_filename_component(_hint "${CMAKE_${l}_COMPILER}" PATH)
       if(IS_DIRECTORY "${_hint}")
@@ -99,7 +99,7 @@
     if (CMAKE_${lang}_COMPILER MATCHES "^/usr/bin/(.+)$")
       _query_xcrun("${CMAKE_MATCH_1}" RESULT_VAR xcrun_result)
     elseif (CMAKE_${lang}_COMPILER STREQUAL "CMAKE_${lang}_COMPILER-NOTFOUND")
-      foreach(comp ${CMAKE_${lang}_COMPILER_LIST})
+      foreach(comp IN LISTS CMAKE_${lang}_COMPILER_LIST)
         _query_xcrun("${comp}" RESULT_VAR xcrun_result)
         if(xcrun_result)
           break()
@@ -120,6 +120,10 @@
     # CMAKE_${lang}_COMPILER and the rest as CMAKE_${lang}_COMPILER_ARG1
     # Otherwise, preserve any existing CMAKE_${lang}_COMPILER_ARG1 that might
     # have been saved by CMakeDetermine${lang}Compiler in a previous run.
+
+    # Necessary for Windows paths to avoid improper escaping of backslashes
+    cmake_path(CONVERT "${CMAKE_${lang}_COMPILER}" TO_CMAKE_PATH_LIST CMAKE_${lang}_COMPILER NORMALIZE)
+
     list(LENGTH CMAKE_${lang}_COMPILER _CMAKE_${lang}_COMPILER_LENGTH)
     if(_CMAKE_${lang}_COMPILER_LENGTH GREATER 1)
       set(CMAKE_${lang}_COMPILER_ARG1 "${CMAKE_${lang}_COMPILER}")
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake
index efc18f9..012a87c 100644
--- a/Modules/CMakeDetermineCompilerABI.cmake
+++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -6,6 +6,7 @@
 # This is used internally by CMake and should not be included by user
 # code.
 
+include(${CMAKE_ROOT}/Modules/Internal/CMakeDetermineLinkerId.cmake)
 include(${CMAKE_ROOT}/Modules/CMakeParseImplicitIncludeInfo.cmake)
 include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
 include(${CMAKE_ROOT}/Modules/CMakeParseLibraryArchitecture.cmake)
@@ -19,15 +20,19 @@
     set(BIN "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_${lang}.bin")
     set(CMAKE_FLAGS )
     set(COMPILE_DEFINITIONS )
+    set(LINK_OPTIONS )
     if(DEFINED CMAKE_${lang}_VERBOSE_FLAG)
-      set(CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${CMAKE_${lang}_VERBOSE_FLAG}")
+      set(LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
       set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
     endif()
     if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG)
       set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}")
     endif()
+    if(DEFINED CMAKE_${lang}_VERBOSE_LINK_FLAG)
+      list(APPEND LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_LINK_FLAG}")
+    endif()
     if(lang MATCHES "^(CUDA|HIP)$")
-      if(CMAKE_${lang}_ARCHITECTURES STREQUAL "native")
+      if(CMAKE_CUDA_ARCHITECTURES STREQUAL "native")
         # We are about to detect the native architectures, so we do
         # not yet know them.  Use all architectures during detection.
         set(CMAKE_${lang}_ARCHITECTURES "all")
@@ -39,6 +44,9 @@
       # from which we might detect implicit link libraries.
       list(APPEND CMAKE_FLAGS "-DCMAKE_${lang}_STANDARD_LIBRARIES=")
     endif()
+    list(JOIN LINK_OPTIONS " " LINK_OPTIONS)
+    list(APPEND CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${LINK_OPTIONS}")
+
     __TestCompiler_setTryCompileTargetType()
 
     # Avoid failing ABI detection on warnings.
@@ -53,7 +61,6 @@
     set(ENV{LC_ALL}      C)
     set(ENV{LC_MESSAGES} C)
     set(ENV{LANG}        C)
-
     try_compile(CMAKE_${lang}_ABI_COMPILED
       SOURCES ${src}
       CMAKE_FLAGS ${CMAKE_FLAGS}
@@ -146,39 +153,42 @@
         set(implicit_libs "${CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES}")
         set(implicit_fwks "${CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES}")
       else()
-        # Parse implicit linker information for this language, if available.
-        set(implicit_dirs "")
-        set(implicit_objs "")
-        set(implicit_libs "")
-        set(implicit_fwks "")
-        if(CMAKE_${lang}_VERBOSE_FLAG)
-          CMAKE_PARSE_IMPLICIT_LINK_INFO("${OUTPUT}" implicit_libs implicit_dirs implicit_fwks log
-            "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
-            COMPUTE_IMPLICIT_OBJECTS implicit_objs
-            LANGUAGE ${lang})
-          message(CONFIGURE_LOG
-            "Parsed ${lang} implicit link information:\n${log}\n\n")
-        endif()
-        # for VS IDE Intel Fortran we have to figure out the
-        # implicit link path for the fortran run time using
-        # a try-compile
-        if("${lang}" MATCHES "Fortran"
-            AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
-          message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path")
-          # Build a sample project which reports symbols.
-          try_compile(IFORT_LIB_PATH_COMPILED
-            PROJECT IntelFortranImplicit
-            SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
-            BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
-            CMAKE_FLAGS
-            "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
-            OUTPUT_VARIABLE _output)
-          file(WRITE
-            "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt"
-            "${_output}")
-          include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL)
-          message(CHECK_PASS "done")
-        endif()
+      # Parse implicit linker information for this language, if available.
+      set(implicit_dirs "")
+      set(implicit_objs "")
+      set(implicit_libs "")
+      set(implicit_fwks "")
+      set(compute_artifacts COMPUTE_LINKER linker_tool)
+      if(CMAKE_${lang}_VERBOSE_FLAG)
+        list(APPEND compute_artifacts COMPUTE_IMPLICIT_LIBS implicit_libs
+                                      COMPUTE_IMPLICIT_DIRS implicit_dirs
+                                      COMPUTE_IMPLICIT_FWKS implicit_fwks
+                                      COMPUTE_IMPLICIT_OBJECTS implicit_objs)
+      endif()
+      cmake_parse_implicit_link_info2("${OUTPUT}" log "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
+        ${compute_artifacts} LANGUAGE ${lang})
+      message(CONFIGURE_LOG
+          "Parsed ${lang} implicit link information:\n${log}\n\n")
+      # for VS IDE Intel Fortran we have to figure out the
+      # implicit link path for the fortran run time using
+      # a try-compile
+      if("${lang}" MATCHES "Fortran"
+          AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+        message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path")
+        # Build a sample project which reports symbols.
+        try_compile(IFORT_LIB_PATH_COMPILED
+          PROJECT IntelFortranImplicit
+          SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
+          BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
+          CMAKE_FLAGS
+          "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
+          OUTPUT_VARIABLE _output)
+        file(WRITE
+          "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt"
+          "${_output}")
+        include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL)
+        message(CHECK_PASS "done")
+      endif()
       endif()
 
       # Implicit link libraries cannot be used explicitly for multiple
@@ -193,6 +203,12 @@
         list(REMOVE_ITEM implicit_dirs $ENV{CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES_EXCLUDE})
       endif()
 
+      set(CMAKE_${lang}_COMPILER_LINKER "${linker_tool}" PARENT_SCOPE)
+      cmake_determine_linker_id(${lang} "${linker_tool}")
+      set(CMAKE_${lang}_COMPILER_LINKER_ID "${CMAKE_${lang}_COMPILER_LINKER_ID}" PARENT_SCOPE)
+      set(CMAKE_${lang}_COMPILER_LINKER_VERSION ${CMAKE_${lang}_COMPILER_LINKER_VERSION} PARENT_SCOPE)
+      set(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT ${CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT} PARENT_SCOPE)
+
       set(CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES "${implicit_libs}" PARENT_SCOPE)
       set(CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES "${implicit_dirs}" PARENT_SCOPE)
       set(CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "${implicit_fwks}" PARENT_SCOPE)
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index dde2b28..1cf1e79 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -174,27 +174,30 @@
     endif()
   endif()
 
-  # FIXME(LLVMFlang): It does not provide predefines identifying the MSVC ABI or architecture.
-  # It should be taught to define _MSC_VER and its _M_* architecture flags.
   if("x${lang}" STREQUAL "xFortran" AND "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xLLVMFlang")
-    # Parse the target triple to detect information we should later be able
-    # to get during preprocessing above, once LLVMFlang provides it.
+    # Parse the target triple to detect information not always available from the preprocessor.
     if(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-msvc([0-9]+)\\.([0-9]+)")
-      set(CMAKE_${lang}_SIMULATE_ID "MSVC")
+      # CMakeFortranCompilerId.F.in does not extract the _MSC_VER minor version.
+      # We can do better using the version parsed here.
       set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
-      set(arch ${CMAKE_MATCH_1})
-      if(arch STREQUAL "x86_64")
-        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "x64")
-      elseif(arch STREQUAL "aarch64")
-        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64")
-      elseif(arch STREQUAL "arm64ec")
-        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64EC")
-      elseif(arch MATCHES "^i[3-9]86$")
-        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "X86")
-      else()
-        message(FATAL_ERROR "LLVMFlang target architecture unrecognized: ${arch}")
+
+      if (CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 18.0)
+        # LLVMFlang < 18.0 does not provide predefines identifying the MSVC ABI or architecture.
+        set(CMAKE_${lang}_SIMULATE_ID "MSVC")
+        set(arch ${CMAKE_MATCH_1})
+        if(arch STREQUAL "x86_64")
+          set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "x64")
+        elseif(arch STREQUAL "aarch64")
+          set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64")
+        elseif(arch STREQUAL "arm64ec")
+          set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64EC")
+        elseif(arch MATCHES "^i[3-9]86$")
+          set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "X86")
+        else()
+          message(FATAL_ERROR "LLVMFlang target architecture unrecognized: ${arch}")
+        endif()
+        set(MSVC_${lang}_ARCHITECTURE_ID "${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}")
       endif()
-      set(MSVC_${lang}_ARCHITECTURE_ID "${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}")
     elseif(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-gnu")
       set(CMAKE_${lang}_SIMULATE_ID "GNU")
     endif()
@@ -277,7 +280,8 @@
     endif()
   elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xGNU"
     OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xAppleClang"
-    OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xFujitsuClang")
+    OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xFujitsuClang"
+    OR "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xTIClang")
     set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "GNU")
   elseif("x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
     set(CMAKE_${lang}_COMPILER_FRONTEND_VARIANT "MSVC")
@@ -402,7 +406,13 @@
     elseif(lang STREQUAL Fortran)
       set(v Intel)
       set(ext vfproj)
-      set(id_cl ifort.exe)
+      if(CMAKE_VS_PLATFORM_TOOLSET_FORTRAN)
+        set(id_cl "${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}.exe")
+        set(id_UseCompiler "UseCompiler=\"${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}Compiler\"")
+      else()
+        set(id_cl ifort.exe)
+        set(id_UseCompiler "")
+      endif()
     elseif(lang STREQUAL CSharp)
       set(v 10)
       set(ext csproj)
diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake
index 392f0f1..613b0c4 100644
--- a/Modules/CMakeDetermineFortranCompiler.cmake
+++ b/Modules/CMakeDetermineFortranCompiler.cmake
@@ -22,7 +22,7 @@
   _cmake_find_compiler_path(Fortran)
 else()
   if(NOT CMAKE_Fortran_COMPILER)
-    # prefer the environment variable CC
+    # prefer the environment variable FC
     if(NOT $ENV{FC} STREQUAL "")
       get_filename_component(CMAKE_Fortran_COMPILER_INIT $ENV{FC} PROGRAM PROGRAM_ARGS CMAKE_Fortran_FLAGS_ENV_INIT)
       if(CMAKE_Fortran_FLAGS_ENV_INIT)
@@ -140,11 +140,11 @@
   set(CMAKE_Fortran_COMPILER_ID_TOOL_MATCH_INDEX 2)
 
   set(_version_info "")
-  foreach(m MAJOR MINOR PATCH TWEAK)
+  foreach(m IN ITEMS MAJOR MINOR PATCH TWEAK)
     set(_COMP "_${m}")
     string(APPEND _version_info "
 #if defined(COMPILER_VERSION${_COMP})")
-    foreach(d 1 2 3 4 5 6 7 8)
+    foreach(d RANGE 1 8)
       string(APPEND _version_info "
 # undef DEC
 # undef HEX
@@ -265,8 +265,8 @@
       "${log}\n"
       )
     set(_CMAKE_Fortran_IMPLICIT_LINK_INFORMATION_DETERMINED_EARLY 1)
-    if("x${CMAKE_Fortran_COMPILER_ARCHITECTURE_ID}" STREQUAL "xARM64")
-      # FIXME(LLVMFlang): It does not add `-defaultlib:` fields to object
+    if("x${CMAKE_Fortran_COMPILER_ARCHITECTURE_ID}" STREQUAL "xARM64" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 18.0)
+      # LLVMFlang < 18.0 does not add `-defaultlib:` fields to object
       # files to specify link dependencies on its runtime libraries.
       # For now, we add them ourselves.
       list(APPEND CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES "clang_rt.builtins-aarch64.lib")
diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake
index fff4e9d..b330ed9 100644
--- a/Modules/CMakeDetermineSystem.cmake
+++ b/Modules/CMakeDetermineSystem.cmake
@@ -6,30 +6,6 @@
 # CMAKE_SYSTEM_NAME - on unix this is uname -s, for windows it is Windows
 # CMAKE_SYSTEM_VERSION - on unix this is uname -r, for windows it is empty
 # CMAKE_SYSTEM - ${CMAKE_SYSTEM}-${CMAKE_SYSTEM_VERSION}, for windows: ${CMAKE_SYSTEM}
-#
-#  Expected uname -s output:
-#
-# AIX                           AIX
-# BSD/OS                        BSD/OS
-# FreeBSD                       FreeBSD
-# HP-UX                         HP-UX
-# Linux                         Linux
-# GNU/kFreeBSD                  GNU/kFreeBSD
-# NetBSD                        NetBSD
-# OpenBSD                       OpenBSD
-# OFS/1 (Digital Unix)          OSF1
-# SCO OpenServer 5              SCO_SV
-# SCO UnixWare 7                UnixWare
-# SCO UnixWare (pre release 7)  UNIX_SV
-# SCO XENIX                     Xenix
-# Solaris                       SunOS
-# SunOS                         SunOS
-# Tru64                         Tru64
-# Ultrix                        ULTRIX
-# cygwin                        CYGWIN_NT-5.1
-# MSYS                          MSYS_NT-6.1
-# MacOSX                        Darwin
-
 
 # find out on which system cmake runs
 if(CMAKE_HOST_UNIX)
@@ -177,22 +153,19 @@
   endif()
 endif()
 
-
-# if CMAKE_SYSTEM_NAME is here already set, either it comes from a toolchain file
-# or it was set via -DCMAKE_SYSTEM_NAME=...
-# if that's the case, assume we are crosscompiling
 if(CMAKE_SYSTEM_NAME)
+  # CMAKE_SYSTEM_NAME was set by a toolchain file or on the command line.
+  # Assume it set CMAKE_SYSTEM_VERSION and CMAKE_SYSTEM_PROCESSOR too.
   if(NOT DEFINED CMAKE_CROSSCOMPILING)
     set(CMAKE_CROSSCOMPILING TRUE)
   endif()
-  set(PRESET_CMAKE_SYSTEM_NAME TRUE)
 elseif(CMAKE_VS_WINCE_VERSION)
   set(CMAKE_SYSTEM_NAME      "WindowsCE")
   set(CMAKE_SYSTEM_VERSION   "${CMAKE_VS_WINCE_VERSION}")
   set(CMAKE_SYSTEM_PROCESSOR "${MSVC_C_ARCHITECTURE_ID}")
   set(CMAKE_CROSSCOMPILING TRUE)
-  set(PRESET_CMAKE_SYSTEM_NAME TRUE)
 else()
+  # Build for the host platform and architecture by default.
   set(CMAKE_SYSTEM_NAME      "${CMAKE_HOST_SYSTEM_NAME}")
   if(NOT DEFINED CMAKE_SYSTEM_VERSION)
     set(CMAKE_SYSTEM_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
@@ -206,7 +179,6 @@
       )
   endif()
   set(CMAKE_CROSSCOMPILING FALSE)
-  set(PRESET_CMAKE_SYSTEM_NAME FALSE)
 endif()
 
 include(Platform/${CMAKE_SYSTEM_NAME}-Determine OPTIONAL)
@@ -224,7 +196,7 @@
 # in this case there is no CMAKE_BINARY_DIR
 if(CMAKE_BINARY_DIR)
   # write entry to the log file
-  if(PRESET_CMAKE_SYSTEM_NAME)
+  if(CMAKE_CROSSCOMPILING)
     message(CONFIGURE_LOG
       "The target system is: ${CMAKE_SYSTEM_NAME} - ${CMAKE_SYSTEM_VERSION} - ${CMAKE_SYSTEM_PROCESSOR}\n"
       "The host system is: ${CMAKE_HOST_SYSTEM_NAME} - ${CMAKE_HOST_SYSTEM_VERSION} - ${CMAKE_HOST_SYSTEM_PROCESSOR}\n"
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index e12b175..5e85440 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -60,6 +60,13 @@
 __resolve_tool_path(CMAKE_LINKER "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Linker")
 __resolve_tool_path(CMAKE_MT     "${_CMAKE_TOOLCHAIN_LOCATION}" "Default Manifest Tool")
 
+macro(__resolve_linker_path __linker_type __name __search_path __doc)
+  if(NOT CMAKE_LINKER_${__linker_type})
+    set( CMAKE_LINKER_${__linker_type} "${__name}")
+  endif()
+  __resolve_tool_path(CMAKE_LINKER_${__linker_type} "${__search_path}" "${__doc}")
+endmacro()
+
 set(_CMAKE_TOOL_VARS "")
 
 # if it's the MS C/CXX compiler, search for link
@@ -93,6 +100,10 @@
 
   list(APPEND _CMAKE_TOOL_VARS LINKER MT AR)
 
+  # look-up for possible usable linker
+  __resolve_linker_path(LINK "link" "${_CMAKE_TOOLCHAIN_LOCATION}" "link Linker")
+  __resolve_linker_path(LLD "lld-link" "${_CMAKE_TOOLCHAIN_LOCATION}" "lld-link Linker")
+
 elseif("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Open)?Watcom$")
   set(_CMAKE_LINKER_NAMES "wlink")
   set(_CMAKE_AR_NAMES "wlib")
@@ -193,10 +204,10 @@
     endif()
     list(PREPEND _CMAKE_NM_NAMES "llvm-nm")
     if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}" VERSION_GREATER_EQUAL 9)
-      # llvm-objdump versions prior to 9 did not support everything we need.
+      # llvm-objcopy and llvm-objdump on versions prior to 9 did not support everything we need.
+      list(PREPEND _CMAKE_OBJCOPY_NAMES "llvm-objcopy")
       list(PREPEND _CMAKE_OBJDUMP_NAMES "llvm-objdump")
     endif()
-    list(PREPEND _CMAKE_OBJCOPY_NAMES "llvm-objcopy")
     list(PREPEND _CMAKE_READELF_NAMES "llvm-readelf")
     list(PREPEND _CMAKE_DLLTOOL_NAMES "llvm-dlltool")
     list(PREPEND _CMAKE_ADDR2LINE_NAMES "llvm-addr2line")
diff --git a/Modules/CMakeFindPackageMode.cmake b/Modules/CMakeFindPackageMode.cmake
index 726e2a2..a6bbcc4 100644
--- a/Modules/CMakeFindPackageMode.cmake
+++ b/Modules/CMakeFindPackageMode.cmake
@@ -5,10 +5,9 @@
 CMakeFindPackageMode
 --------------------
 
-
-
-This file is executed by cmake when invoked with --find-package.  It
-expects that the following variables are set using -D:
+This file is executed by cmake when invoked with
+:ref:`--find-package <Find-Package Tool Mode>`.
+It expects that the following variables are set using ``-D``:
 
 ``NAME``
   name of the package
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index 89a00ab..90c2ad0 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -15,6 +15,10 @@
 set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_Fortran_COMPILER_LINKER "@CMAKE_Fortran_COMPILER_LINKER@")
+set(CMAKE_Fortran_COMPILER_LINKER_ID "@CMAKE_Fortran_COMPILER_LINKER_ID@")
+set(CMAKE_Fortran_COMPILER_LINKER_VERSION @CMAKE_Fortran_COMPILER_LINKER_VERSION@)
+set(CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_Fortran_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
diff --git a/Modules/CMakeFortranCompilerId.F.in b/Modules/CMakeFortranCompilerId.F.in
index f5c2ab5..a040073 100644
--- a/Modules/CMakeFortranCompilerId.F.in
+++ b/Modules/CMakeFortranCompilerId.F.in
@@ -240,7 +240,7 @@
 #else
         PRINT *, 'INFO:platform[]'
 #endif
-#if defined(_WIN32) && (defined(__INTEL_COMPILER) || defined(__ICC))
+#if defined(_MSC_VER)
 # if defined(_M_IA64)
         PRINT *, 'INFO:arch[IA64]'
 # elif defined(_M_X64) || defined(_M_AMD64)
diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake
index 0f71c6f..e364755 100644
--- a/Modules/CMakeFortranInformation.cmake
+++ b/Modules/CMakeFortranInformation.cmake
@@ -43,7 +43,7 @@
 endif ()
 
 if(CMAKE_Fortran_SIZEOF_DATA_PTR)
-  foreach(f ${CMAKE_Fortran_ABI_FILES})
+  foreach(f IN LISTS CMAKE_Fortran_ABI_FILES)
     include(${f})
   endforeach()
   unset(CMAKE_Fortran_ABI_FILES)
diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake
index 77c1780..ccfde60 100644
--- a/Modules/CMakeGenericSystem.cmake
+++ b/Modules/CMakeGenericSystem.cmake
@@ -177,20 +177,27 @@
 # was initialized by the block below.  This is useful for user
 # projects to change the default prefix while still allowing the
 # command line to override it.
-if(NOT DEFINED CMAKE_INSTALL_PREFIX)
+if(NOT DEFINED CMAKE_INSTALL_PREFIX AND
+   NOT DEFINED ENV{CMAKE_INSTALL_PREFIX})
   set(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT 1)
 endif()
 
-# Choose a default install prefix for this platform.
-if(CMAKE_HOST_UNIX)
-  set(CMAKE_INSTALL_PREFIX "/usr/local"
+if(DEFINED ENV{CMAKE_INSTALL_PREFIX})
+  set(CMAKE_INSTALL_PREFIX "$ENV{CMAKE_INSTALL_PREFIX}"
     CACHE PATH "Install path prefix, prepended onto install directories.")
 else()
-  GetDefaultWindowsPrefixBase(CMAKE_GENERIC_PROGRAM_FILES)
-  set(CMAKE_INSTALL_PREFIX
-    "${CMAKE_GENERIC_PROGRAM_FILES}/${PROJECT_NAME}"
-    CACHE PATH "Install path prefix, prepended onto install directories.")
-  set(CMAKE_GENERIC_PROGRAM_FILES)
+  # If CMAKE_INSTALL_PREFIX env variable is not set,
+  # choose a default install prefix for this platform.
+  if(CMAKE_HOST_UNIX)
+    set(CMAKE_INSTALL_PREFIX "/usr/local"
+      CACHE PATH "Install path prefix, prepended onto install directories.")
+  else()
+    GetDefaultWindowsPrefixBase(CMAKE_GENERIC_PROGRAM_FILES)
+    set(CMAKE_INSTALL_PREFIX
+      "${CMAKE_GENERIC_PROGRAM_FILES}/${PROJECT_NAME}"
+      CACHE PATH "Install path prefix, prepended onto install directories.")
+    set(CMAKE_GENERIC_PROGRAM_FILES)
+  endif()
 endif()
 
 # Set a variable which will be used as component name in install() commands
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in
index 6d5e62a..9d70e03 100644
--- a/Modules/CMakeHIPCompiler.cmake.in
+++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -74,5 +74,11 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_HIP_COMPILER_LINKER "@CMAKE_HIP_COMPILER_LINKER@")
+set(CMAKE_HIP_COMPILER_LINKER_ID "@CMAKE_HIP_COMPILER_LINKER_ID@")
+set(CMAKE_HIP_COMPILER_LINKER_VERSION @CMAKE_HIP_COMPILER_LINKER_VERSION@)
+set(CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_HIP_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in
index de73645..a8bb0d2 100644
--- a/Modules/CMakeOBJCCompiler.cmake.in
+++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -24,6 +24,12 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_OBJC_COMPILER_LINKER "@CMAKE_OBJC_COMPILER_LINKER@")
+set(CMAKE_OBJC_COMPILER_LINKER_ID "@CMAKE_OBJC_COMPILER_LINKER_ID@")
+set(CMAKE_OBJC_COMPILER_LINKER_VERSION @CMAKE_OBJC_COMPILER_LINKER_VERSION@)
+set(CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJC_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake
index 4c697da..8b8f10e 100644
--- a/Modules/CMakeOBJCInformation.cmake
+++ b/Modules/CMakeOBJCInformation.cmake
@@ -67,7 +67,7 @@
 endif ()
 
 if(CMAKE_OBJC_SIZEOF_DATA_PTR)
-  foreach(f ${CMAKE_OBJC_ABI_FILES})
+  foreach(f IN LISTS CMAKE_OBJC_ABI_FILES)
     include(${f})
   endforeach()
   unset(CMAKE_OBJC_ABI_FILES)
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in
index 94d24ff..6a80d50 100644
--- a/Modules/CMakeOBJCXXCompiler.cmake.in
+++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -25,6 +25,12 @@
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
 set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
 set(CMAKE_LINKER "@CMAKE_LINKER@")
+set(CMAKE_LINKER_LINK "@CMAKE_LINKER_LINK@")
+set(CMAKE_LINKER_LLD "@CMAKE_LINKER_LLD@")
+set(CMAKE_OBJCXX_COMPILER_LINKER "@CMAKE_OBJCXX_COMPILER_LINKER@")
+set(CMAKE_OBJCXX_COMPILER_LINKER_ID "@CMAKE_OBJCXX_COMPILER_LINKER_ID@")
+set(CMAKE_OBJCXX_COMPILER_LINKER_VERSION @CMAKE_OBJCXX_COMPILER_LINKER_VERSION@)
+set(CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT @CMAKE_OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT@)
 set(CMAKE_MT "@CMAKE_MT@")
 set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
@@ -44,7 +50,7 @@
   endforeach()
 endif()
 
-foreach (lang C CXX OBJC)
+foreach (lang IN ITEMS C CXX OBJC)
   foreach(extension IN LISTS CMAKE_OBJCXX_SOURCE_FILE_EXTENSIONS)
     if (CMAKE_${lang}_COMPILER_ID_RUN)
       list(REMOVE_ITEM CMAKE_${lang}_SOURCE_FILE_EXTENSIONS ${extension})
diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake
index a6d824f..da1d6c6 100644
--- a/Modules/CMakeOBJCXXInformation.cmake
+++ b/Modules/CMakeOBJCXXInformation.cmake
@@ -62,7 +62,7 @@
 endif ()
 
 if(CMAKE_OBJCXX_SIZEOF_DATA_PTR)
-  foreach(f ${CMAKE_OBJCXX_ABI_FILES})
+  foreach(f IN LISTS CMAKE_OBJCXX_ABI_FILES)
     include(${f})
   endforeach()
   unset(CMAKE_OBJCXX_ABI_FILES)
@@ -178,7 +178,7 @@
 endif()
 
 # Initialize OBJCXX link type selection flags from OBJC versions.
-foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
+foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE)
   if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS)
     set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS
       ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS})
diff --git a/Modules/CMakePackageConfigHelpers.cmake b/Modules/CMakePackageConfigHelpers.cmake
index 581e65c..b5bcd02 100644
--- a/Modules/CMakePackageConfigHelpers.cmake
+++ b/Modules/CMakePackageConfigHelpers.cmake
@@ -8,9 +8,6 @@
 Helpers functions for creating config files that can be included by other
 projects to find and use a package.
 
-Adds the :command:`configure_package_config_file()` and
-:command:`write_basic_package_version_file()` commands.
-
 Generating a Package Configuration File
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -190,6 +187,132 @@
 :command:`configure_file()` on them yourself, but they can be used as starting
 point to create more sophisticated custom ``ConfigVersion.cmake`` files.
 
+Generating an Apple Platform Selection File
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. command:: generate_apple_platform_selection_file
+
+  .. versionadded:: 3.29
+
+  Create an Apple platform selection file:
+
+  .. code-block:: cmake
+
+    generate_apple_platform_selection_file(<filename>
+      INSTALL_DESTINATION <path>
+      [INSTALL_PREFIX <path>]
+      [MACOS_INCLUDE_FILE <file>]
+      [IOS_INCLUDE_FILE <file>]
+      [IOS_SIMULATOR_INCLUDE_FILE <file>]
+      [TVOS_INCLUDE_FILE <file>]
+      [TVOS_SIMULATOR_INCLUDE_FILE <file>]
+      [WATCHOS_INCLUDE_FILE <file>]
+      [WATCHOS_SIMULATOR_INCLUDE_FILE <file>]
+      [VISIONOS_INCLUDE_FILE <file>]
+      [VISIONOS_SIMULATOR_INCLUDE_FILE <file>]
+      )
+
+  Write a file that includes an Apple-platform-specific ``.cmake`` file,
+  e.g., for use as ``<PackageName>Config.cmake``.  This can be used in
+  conjunction with the ``XCFRAMEWORK_LOCATION`` argument of
+  :command:`export(SETUP)` to export packages in a way that a project
+  built for any Apple platform can use them.
+
+  ``INSTALL_DESTINATION <path>``
+    Path to which the generated file will be installed by the caller, e.g.,
+    via :command:`install(FILES)`.  The path may be either relative to the
+    ``INSTALL_PREFIX`` or absolute.
+
+  ``INSTALL_PREFIX <path>``
+    Path prefix to which the package will be installed by the caller.
+    The ``<path>`` argument must be an absolute path.  If this argument
+    is not passed, the :variable:`CMAKE_INSTALL_PREFIX` variable will be
+    used instead.
+
+  ``MACOS_INCLUDE_FILE <file>``
+    File to include if the platform is macOS.
+
+  ``IOS_INCLUDE_FILE <file>``
+    File to include if the platform is iOS.
+
+  ``IOS_SIMULATOR_INCLUDE_FILE <file>``
+    File to include if the platform is iOS Simulator.
+
+  ``TVOS_INCLUDE_FILE <file>``
+    File to include if the platform is tvOS.
+
+  ``TVOS_SIMULATOR_INCLUDE_FILE <file>``
+    File to include if the platform is tvOS Simulator.
+
+  ``WATCHOS_INCLUDE_FILE <file>``
+    File to include if the platform is watchOS.
+
+  ``WATCHOS_SIMULATOR_INCLUDE_FILE <file>``
+    File to include if the platform is watchOS Simulator.
+
+  ``VISIONOS_INCLUDE_FILE <file>``
+    File to include if the platform is visionOS.
+
+  ``VISIONOS_SIMULATOR_INCLUDE_FILE <file>``
+    File to include if the platform is visionOS Simulator.
+
+  If any of the optional include files is not specified, and the consuming
+  project is built for its corresponding platform, an error will be thrown
+  when including the generated file.
+
+.. command:: generate_apple_architecture_selection_file
+
+  .. versionadded:: 3.29
+
+  Create an Apple architecture selection file:
+
+  .. code-block:: cmake
+
+    generate_apple_architecture_selection_file(<filename>
+      INSTALL_DESTINATION <path>
+      [INSTALL_PREFIX <path>]
+      [SINGLE_ARCHITECTURES <archs>
+       SINGLE_ARCHITECTURE_INCLUDE_FILES <files>]
+      [UNIVERSAL_ARCHITECTURES <archs>
+       UNIVERSAL_INCLUDE_FILE <file>]
+      )
+
+  Write a file that includes an Apple-architecture-specific ``.cmake`` file
+  based on :variable:`CMAKE_OSX_ARCHITECTURES`, e.g., for inclusion from an
+  Apple-specific ``<PackageName>Config.cmake`` file.
+
+  ``INSTALL_DESTINATION <path>``
+    Path to which the generated file will be installed by the caller, e.g.,
+    via :command:`install(FILES)`.  The path may be either relative to the
+    ``INSTALL_PREFIX`` or absolute.
+
+  ``INSTALL_PREFIX <path>``
+    Path prefix to which the package will be installed by the caller.
+    The ``<path>`` argument must be an absolute path.  If this argument
+    is not passed, the :variable:`CMAKE_INSTALL_PREFIX` variable will be
+    used instead.
+
+  ``SINGLE_ARCHITECTURES <archs>``
+    A :ref:`semicolon-separated list <CMake Language Lists>` of
+    architectures provided by entries of
+    ``SINGLE_ARCHITECTURE_INCLUDE_FILES``.
+
+  ``SINGLE_ARCHITECTURE_INCLUDE_FILES <files>``
+    A :ref:`semicolon-separated list <CMake Language Lists>` of
+    architecture-specific files.  One of them will be loaded
+    when :variable:`CMAKE_OSX_ARCHITECTURES` contains a single
+    architecture matching the corresponding entry of
+    ``SINGLE_ARCHITECTURES``.
+
+  ``UNIVERSAL_ARCHITECTURES <archs>``
+    A :ref:`semicolon-separated list <CMake Language Lists>` of
+    architectures provided by the ``UNIVERSAL_INCLUDE_FILE``.
+
+  ``UNIVERSAL_INCLUDE_FILE <file>``
+    A file to load when :variable:`CMAKE_OSX_ARCHITECTURES` contains
+    a (non-strict) subset of the ``UNIVERSAL_ARCHITECTURES`` and
+    does not match any one of the ``SINGLE_ARCHITECTURES``.
+
 Example Generating Package Files
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -344,3 +467,130 @@
   configure_file("${_inputFile}" "${_outputFile}" @ONLY)
 
 endfunction()
+
+function(generate_apple_platform_selection_file _output_file)
+  set(_config_file_options
+    MACOS_INCLUDE_FILE
+    IOS_INCLUDE_FILE
+    IOS_SIMULATOR_INCLUDE_FILE
+    TVOS_INCLUDE_FILE
+    TVOS_SIMULATOR_INCLUDE_FILE
+    WATCHOS_INCLUDE_FILE
+    WATCHOS_SIMULATOR_INCLUDE_FILE
+    VISIONOS_INCLUDE_FILE
+    VISIONOS_SIMULATOR_INCLUDE_FILE
+    )
+
+  set(_options)
+  set(_single
+    INSTALL_DESTINATION
+    INSTALL_PREFIX
+    ${_config_file_options}
+    )
+  set(_multi)
+  cmake_parse_arguments(PARSE_ARGV 0 _gpsf "${_options}" "${_single}" "${_multi}")
+
+  if(NOT _gpsf_INSTALL_DESTINATION)
+    message(FATAL_ERROR "No INSTALL_DESTINATION given to generate_apple_platform_selection_file()")
+  endif()
+  if(_gpsf_INSTALL_PREFIX)
+    set(maybe_INSTALL_PREFIX INSTALL_PREFIX ${_gpsf_INSTALL_PREFIX})
+  else()
+    set(maybe_INSTALL_PREFIX "")
+  endif()
+
+  set(_have_relative 0)
+  foreach(_opt IN LISTS _config_file_options)
+    if(_gpsf_${_opt})
+      set(_config_file "${_gpsf_${_opt}}")
+      if(NOT IS_ABSOLUTE "${_config_file}")
+        string(PREPEND _config_file [[${PACKAGE_PREFIX_DIR}/]])
+        set(_have_relative 1)
+      endif()
+      set(_branch_${_opt} "include(\"${_config_file}\")")
+    else()
+      set(_branch_${_opt} "message(FATAL_ERROR \"Platform not supported\")")
+    endif()
+  endforeach()
+
+  configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/ApplePlatformSelection.cmake.in" "${_output_file}"
+    INSTALL_DESTINATION "${_gpsf_INSTALL_DESTINATION}"
+    ${maybe_INSTALL_PREFIX}
+    NO_SET_AND_CHECK_MACRO
+    NO_CHECK_REQUIRED_COMPONENTS_MACRO
+    )
+endfunction()
+
+function(generate_apple_architecture_selection_file _output_file)
+  set(_options)
+  set(_single
+    INSTALL_DESTINATION
+    INSTALL_PREFIX
+    SINGLE_ARCHITECTURES
+    SINGLE_ARCHITECTURE_INCLUDE_FILES
+    UNIVERSAL_ARCHITECTURES
+    UNIVERSAL_INCLUDE_FILE
+    )
+  set(_multi)
+  cmake_parse_arguments(PARSE_ARGV 0 _gasf "${_options}" "${_single}" "${_multi}")
+
+  if(NOT _gasf_INSTALL_DESTINATION)
+    message(FATAL_ERROR "No INSTALL_DESTINATION given to generate_apple_platform_selection_file()")
+  endif()
+  if(_gasf_INSTALL_PREFIX)
+    set(maybe_INSTALL_PREFIX INSTALL_PREFIX ${_gasf_INSTALL_PREFIX})
+  else()
+    set(maybe_INSTALL_PREFIX "")
+  endif()
+
+  list(LENGTH _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURES_len)
+  list(LENGTH _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES_len)
+  if(NOT _gasf_SINGLE_ARCHITECTURES_len EQUAL _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES_len)
+    message(FATAL_ERROR "SINGLE_ARCHITECTURES and SINGLE_ARCHITECTURE_INCLUDE_FILES do not have the same number of entries.")
+  endif()
+
+  set(_branch_code "")
+
+  foreach(pair IN ZIP_LISTS _gasf_SINGLE_ARCHITECTURES _gasf_SINGLE_ARCHITECTURE_INCLUDE_FILES)
+    set(arch "${pair_0}")
+    set(config_file "${pair_1}")
+    if(NOT IS_ABSOLUTE "${config_file}")
+      string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
+    endif()
+    string(APPEND _branch_code
+      "\n"
+      "if(CMAKE_OSX_ARCHITECTURES STREQUAL \"${arch}\")\n"
+      "  include(\"${config_file}\")\n"
+      "  return()\n"
+      "endif()\n"
+      )
+  endforeach()
+
+  if(_gasf_UNIVERSAL_ARCHITECTURES AND _gasf_UNIVERSAL_INCLUDE_FILE)
+    string(JOIN " " universal_archs "${_gasf_UNIVERSAL_ARCHITECTURES}")
+    set(config_file "${_gasf_UNIVERSAL_INCLUDE_FILE}")
+    if(NOT IS_ABSOLUTE "${config_file}")
+      string(PREPEND config_file [[${PACKAGE_PREFIX_DIR}/]])
+    endif()
+    string(APPEND _branch_code
+      "\n"
+      "set(_cmake_apple_archs \"\${CMAKE_OSX_ARCHITECTURES}\")\n"
+      "list(REMOVE_ITEM _cmake_apple_archs ${universal_archs})\n"
+      "if(NOT _cmake_apple_archs)\n"
+      "  include(\"${config_file}\")\n"
+      "  return()\n"
+      "endif()\n"
+      )
+  elseif(_gasf_UNIVERSAL_ARCHITECTURES)
+    message(FATAL_ERROR "UNIVERSAL_INCLUDE_FILE requires UNIVERSAL_ARCHITECTURES")
+  elseif(_gasf_UNIVERSAL_INCLUDE_FILE)
+    message(FATAL_ERROR "UNIVERSAL_ARCHITECTURES requires UNIVERSAL_INCLUDE_FILE")
+  endif()
+
+  configure_package_config_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Internal/AppleArchitectureSelection.cmake.in" "${_output_file}"
+    INSTALL_DESTINATION "${_gasf_INSTALL_DESTINATION}"
+    ${maybe_INSTALL_PREFIX}
+    NO_SET_AND_CHECK_MACRO
+    NO_CHECK_REQUIRED_COMPONENTS_MACRO
+    )
+endfunction()
diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake
index cbdb915..896eac0 100644
--- a/Modules/CMakeParseImplicitLinkInfo.cmake
+++ b/Modules/CMakeParseImplicitLinkInfo.cmake
@@ -15,6 +15,26 @@
 # compatibility don't break.
 #
 function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj_regex)
+  set(keywordArgs)
+  set(oneValueArgs LANGUAGE COMPUTE_IMPLICIT_OBJECTS)
+  set(multiValueArgs )
+  cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  cmake_parse_implicit_link_info2("${text}" "${log_var}" "${obj_regex}"
+    COMPUTE_IMPLICIT_LIBS "${lib_var}" COMPUTE_IMPLICIT_DIRS "${dir_var}"
+    COMPUTE_IMPLICIT_FWKS "${fwk_var}" ${ARGN})
+
+  set(${lib_var} "${${lib_var}}" PARENT_SCOPE)
+  set(${dir_var} "${${dir_var}}" PARENT_SCOPE)
+  set(${fwk_var} "${${fwk_var}}" PARENT_SCOPE)
+  set(${log_var} "${${log_var}}" PARENT_SCOPE)
+
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS)
+    set(${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS} "${${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS}}" PARENT_SCOPE)
+  endif()
+endfunction()
+
+function(cmake_parse_implicit_link_info2 text log_var obj_regex)
   set(implicit_libs_tmp "")
   set(implicit_objs_tmp "")
   set(implicit_dirs_tmp)
@@ -22,25 +42,29 @@
   set(log "")
 
   set(keywordArgs)
-  set(oneValueArgs COMPUTE_IMPLICIT_OBJECTS LANGUAGE)
+  set(oneValueArgs LANGUAGE
+                   COMPUTE_IMPLICIT_LIBS COMPUTE_IMPLICIT_DIRS COMPUTE_IMPLICIT_FWKS
+                   COMPUTE_IMPLICIT_OBJECTS COMPUTE_LINKER)
   set(multiValueArgs )
   cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
   set(is_msvc 0)
   if(EXTRA_PARSE_LANGUAGE AND
-    ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_ID}" STREQUAL "xMSVC" OR
+    ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_COMPILER_ID}" STREQUAL "xMSVC" OR
      "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC"))
     set(is_msvc 1)
   endif()
-
   # Parse implicit linker arguments.
-  set(linker "CMAKE_LINKER-NOTFOUND")
-  if(CMAKE_LINKER)
-    get_filename_component(linker ${CMAKE_LINKER} NAME)
-    string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" linker "${linker}")
-  endif()
+  set(linker "ld[0-9]*(\\.[a-z]+)?")
   if(is_msvc)
-    string(APPEND linker "|link\\.exe|lld-link")
+    string(APPEND linker "|link\\.exe|lld-link(\\.exe)?")
+  endif()
+  if(CMAKE_LINKER)
+    get_filename_component(default_linker ${CMAKE_LINKER} NAME)
+    if (NOT default_linker MATCHES "(${linker})")
+      string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" default_linker "${default_linker}")
+      list(PREPEND linker "${default_linker}|")
+    endif()
   endif()
   set(startfile "CMAKE_LINK_STARTFILE-NOTFOUND")
   if(CMAKE_LINK_STARTFILE)
@@ -50,9 +74,43 @@
   # whole line and just the command (argv[0]).
   set(linker_regex "^( *|.*[/\\])(${linker}|${startfile}|([^/\\]+-)?ld|collect2)[^/\\]*( |$)")
   set(linker_exclude_regex "collect2 version |^[A-Za-z0-9_]+=|/ldfe ")
+  set(linker_tool_regex "^[ \t]*(->|\")?[ \t]*(([^\"]*[/\\])?(${linker}))(\"|,| |$)")
+  set(linker_tool_exclude_regex "cuda-fake-ld|-fuse-ld=")
+  set(linker_tool "NOTFOUND")
+  set(linker_tool_fallback "")
+  set(link_line_parsed 0)
   string(APPEND log "  link line regex: [${linker_regex}]\n")
+  if(EXTRA_PARSE_COMPUTE_LINKER)
+    string(APPEND log "  linker tool regex: [${linker_tool_regex}]\n")
+  endif()
   string(REGEX REPLACE "\r?\n" ";" output_lines "${text}")
   foreach(line IN LISTS output_lines)
+    if(EXTRA_PARSE_COMPUTE_LINKER AND
+        NOT linker_tool AND NOT "${line}" MATCHES "${linker_tool_exclude_regex}")
+      if("${line}" MATCHES "exec: ([^()]*/(${linker}))") # IBM XL as nvcc host compiler
+        set(linker_tool "${CMAKE_MATCH_1}")
+      elseif("${line}" MATCHES "^export XL_LINKER=(.*/${linker})[ \t]*$") # IBM XL
+        set(linker_tool "${CMAKE_MATCH_1}")
+      elseif("${line}" MATCHES "--with-ld=") # GNU
+        # The GNU compiler reports how it was configured.
+        # This does not account for -fuse-ld= so use it only as a fallback.
+        if("${line}" MATCHES " --with-ld=([^ ]+/${linker})( |$)")
+          set(linker_tool_fallback "${CMAKE_MATCH_1}")
+        endif()
+      elseif("${line}" MATCHES "vs_link.*-- +([^\"]*[/\\](${linker})) ") # cmake -E vs_link_exe
+        set(linker_tool "${CMAKE_MATCH_1}")
+      elseif("${line}" MATCHES "${linker_tool_regex}")
+        set(linker_tool "${CMAKE_MATCH_2}")
+      endif()
+    endif()
+    if(NOT (EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS OR EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS
+          OR EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS OR EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS))
+      if(linker_tool)
+        break()
+      else()
+        continue()
+      endif()
+    endif()
     set(cmd)
     if("${line}" MATCHES "${linker_regex}" AND
         NOT "${line}" MATCHES "${linker_exclude_regex}")
@@ -86,7 +144,8 @@
       endif()
     endif()
     set(search_static 0)
-    if("${cmd}" MATCHES "${linker_regex}")
+    if(NOT link_line_parsed AND "${cmd}" MATCHES "${linker_regex}")
+      set(link_line_parsed 1)
       string(APPEND log "  link line: [${line}]\n")
       string(REGEX REPLACE ";-([LYz]);" ";-\\1" args "${args}")
       set(skip_value_of "")
@@ -95,60 +154,78 @@
           string(APPEND log "    arg [${arg}] ==> skip value of ${skip_value_of}\n")
           set(skip_value_of "")
         elseif("${arg}" MATCHES "^-L(.:)?[/\\]")
-          # Unix search path.
-          string(REGEX REPLACE "^-L" "" dir "${arg}")
-          list(APPEND implicit_dirs_tmp ${dir})
-          string(APPEND log "    arg [${arg}] ==> dir [${dir}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS)
+            # Unix search path.
+            string(REGEX REPLACE "^-L" "" dir "${arg}")
+            list(APPEND implicit_dirs_tmp ${dir})
+            string(APPEND log "    arg [${arg}] ==> dir [${dir}]\n")
+          endif()
         elseif("${arg}" MATCHES "^[-/](LIBPATH|libpath):(.+)")
-          # MSVC search path.
-          set(dir "${CMAKE_MATCH_2}")
-          list(APPEND implicit_dirs_tmp ${dir})
-          string(APPEND log "    arg [${arg}] ==> dir [${dir}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS)
+            # MSVC search path.
+            set(dir "${CMAKE_MATCH_2}")
+            list(APPEND implicit_dirs_tmp ${dir})
+            string(APPEND log "    arg [${arg}] ==> dir [${dir}]\n")
+          endif()
         elseif(is_msvc AND "${arg}" STREQUAL "-link")
           string(APPEND log "    arg [${arg}] ==> ignore MSVC cl option\n")
         elseif(is_msvc AND "${arg}" MATCHES "^[-/][Ii][Mm][Pp][Ll][Ii][Bb]:")
           string(APPEND log "    arg [${arg}] ==> ignore MSVC link option\n")
+        elseif(is_msvc AND "${arg}" MATCHES "^[-/][Ww][Hh][Oo][Ll][Ee][Aa][Rr][Cc][Hh][Ii][Vv][Ee]:Fortran_main")
+          string(APPEND log "    arg [${arg}] ==> ignore LLVMFlang program entry point\n")
         elseif(is_msvc AND "${arg}" MATCHES "^(.*\\.[Ll][Ii][Bb])$")
-          set(lib "${CMAKE_MATCH_1}")
-          list(APPEND implicit_libs_tmp ${lib})
-          string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            set(lib "${CMAKE_MATCH_1}")
+            list(APPEND implicit_libs_tmp ${lib})
+            string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
+          endif()
         elseif("${arg}" STREQUAL "-lto_library")
           # ld argument "-lto_library <path>"
           set(skip_value_of "${arg}")
           string(APPEND log "    arg [${arg}] ==> ignore, skip following value\n")
         elseif("${arg}" MATCHES "^-l([^:].*)$")
-          # Unix library.
-          set(lib "${CMAKE_MATCH_1}")
-          if(search_static AND lib MATCHES "^(gfortran|stdc\\+\\+)$")
-            # Search for the static library later, once all link dirs are known.
-            set(lib "SEARCH_STATIC:${lib}")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            # Unix library.
+            set(lib "${CMAKE_MATCH_1}")
+            if(search_static AND lib MATCHES "^(gfortran|stdc\\+\\+)$")
+              # Search for the static library later, once all link dirs are known.
+              set(lib "SEARCH_STATIC:${lib}")
+            endif()
+            list(APPEND implicit_libs_tmp ${lib})
+            string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
           endif()
-          list(APPEND implicit_libs_tmp ${lib})
-          string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
         elseif("${arg}" MATCHES "^(.:)?[/\\].*\\.a$")
-          # Unix library full path.
-          list(APPEND implicit_libs_tmp ${arg})
-          string(APPEND log "    arg [${arg}] ==> lib [${arg}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            # Unix library full path.
+            list(APPEND implicit_libs_tmp ${arg})
+            string(APPEND log "    arg [${arg}] ==> lib [${arg}]\n")
+          endif()
         elseif("${arg}" MATCHES "^[-/](DEFAULTLIB|defaultlib):(.+)")
-          # Windows library.
-          set(lib "${CMAKE_MATCH_2}")
-          list(APPEND implicit_libs_tmp ${lib})
-          string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            # Windows library.
+            set(lib "${CMAKE_MATCH_2}")
+            list(APPEND implicit_libs_tmp ${lib})
+            string(APPEND log "    arg [${arg}] ==> lib [${lib}]\n")
+          endif()
         elseif("${arg}" MATCHES "^(.:)?[/\\].*\\.o$")
           if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS)
             list(APPEND implicit_objs_tmp ${arg})
             string(APPEND log "    arg [${arg}] ==> obj [${arg}]\n")
           endif()
-          if(obj_regex AND "${arg}" MATCHES "${obj_regex}")
-            # Object file full path.
-            list(APPEND implicit_libs_tmp ${arg})
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            if(obj_regex AND "${arg}" MATCHES "${obj_regex}")
+              # Object file full path.
+              list(APPEND implicit_libs_tmp ${arg})
+            endif()
           endif()
         elseif("${arg}" MATCHES "^-Y(P,)?[^0-9]")
-          # Sun search path ([^0-9] avoids conflict with Mac -Y<num>).
-          string(REGEX REPLACE "^-Y(P,)?" "" dirs "${arg}")
-          string(REPLACE ":" ";" dirs "${dirs}")
-          list(APPEND implicit_dirs_tmp ${dirs})
-          string(APPEND log "    arg [${arg}] ==> dirs [${dirs}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS)
+            # Sun search path ([^0-9] avoids conflict with Mac -Y<num>).
+            string(REGEX REPLACE "^-Y(P,)?" "" dirs "${arg}")
+            string(REPLACE ":" ";" dirs "${dirs}")
+            list(APPEND implicit_dirs_tmp ${dirs})
+            string(APPEND log "    arg [${arg}] ==> dirs [${dirs}]\n")
+          endif()
         elseif("${arg}" STREQUAL "-Bstatic")
           set(search_static 1)
           string(APPEND log "    arg [${arg}] ==> search static\n" )
@@ -156,13 +233,17 @@
           set(search_static 0)
           string(APPEND log "    arg [${arg}] ==> search dynamic\n" )
         elseif("${arg}" MATCHES "^-l:")
-          # HP named library.
-          list(APPEND implicit_libs_tmp ${arg})
-          string(APPEND log "    arg [${arg}] ==> lib [${arg}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            # HP named library.
+            list(APPEND implicit_libs_tmp ${arg})
+            string(APPEND log "    arg [${arg}] ==> lib [${arg}]\n")
+          endif()
         elseif("${arg}" MATCHES "^-z(all|default|weak)extract")
-          # Link editor option.
-          list(APPEND implicit_libs_tmp ${arg})
-          string(APPEND log "    arg [${arg}] ==> opt [${arg}]\n")
+          if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+            # Link editor option.
+            list(APPEND implicit_libs_tmp ${arg})
+            string(APPEND log "    arg [${arg}] ==> opt [${arg}]\n")
+          endif()
         elseif("${arg}" STREQUAL "cl.exe")
           string(APPEND log "    arg [${arg}] ==> recognize MSVC cl\n")
           set(is_msvc 1)
@@ -170,25 +251,39 @@
           string(APPEND log "    arg [${arg}] ==> ignore\n")
         endif()
       endforeach()
-      break()
     elseif("${line}" MATCHES "LPATH(=| is:? *)(.*)$")
-      string(APPEND log "  LPATH line: [${line}]\n")
-      # HP search path.
-      string(REPLACE ":" ";" paths "${CMAKE_MATCH_2}")
-      list(APPEND implicit_dirs_tmp ${paths})
-      string(APPEND log "    dirs [${paths}]\n")
+      if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS)
+        string(APPEND log "  LPATH line: [${line}]\n")
+        # HP search path.
+        string(REPLACE ":" ";" paths "${CMAKE_MATCH_2}")
+        list(APPEND implicit_dirs_tmp ${paths})
+        string(APPEND log "    dirs [${paths}]\n")
+      endif()
     else()
       string(APPEND log "  ignore line: [${line}]\n")
     endif()
+    if((NOT EXTRA_PARSE_COMPUTE_LINKER OR linker_tool) AND link_line_parsed)
+      break()
+    endif()
   endforeach()
 
+  if(NOT linker_tool AND linker_tool_fallback)
+    set(linker_tool "${linker_tool_fallback}")
+  endif()
+  if(linker_tool)
+    if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
+      cmake_path(NORMAL_PATH linker_tool)
+    endif()
+    string(APPEND log "  linker tool for '${EXTRA_PARSE_LANGUAGE}': ${linker_tool}\n")
+  endif()
+
   # Look for library search paths reported by linker.
-  if("${output_lines}" MATCHES ";Library search paths:((;\t[^;]+)+)")
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS AND "${output_lines}" MATCHES ";Library search paths:((;\t[^;]+)+)")
     string(REPLACE ";\t" ";" implicit_dirs_match "${CMAKE_MATCH_1}")
     string(APPEND log "  Library search paths: [${implicit_dirs_match}]\n")
     list(APPEND implicit_dirs_tmp ${implicit_dirs_match})
   endif()
-  if("${output_lines}" MATCHES ";Framework search paths:((;\t[^;]+)+)")
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS AND "${output_lines}" MATCHES ";Framework search paths:((;\t[^;]+)+)")
     string(REPLACE ";\t" ";" implicit_fwks_match "${CMAKE_MATCH_1}")
     string(APPEND log "  Framework search paths: [${implicit_fwks_match}]\n")
     list(APPEND implicit_fwks_tmp ${implicit_fwks_match})
@@ -273,11 +368,21 @@
   string(APPEND log "  implicit fwks: [${implicit_fwks}]\n")
 
   # Return results.
-  set(${lib_var} "${implicit_libs}" PARENT_SCOPE)
-  set(${dir_var} "${implicit_dirs}" PARENT_SCOPE)
-  set(${fwk_var} "${implicit_fwks}" PARENT_SCOPE)
+  if(EXTRA_PARSE_COMPUTE_LINKER)
+    set(${EXTRA_PARSE_COMPUTE_LINKER} "${linker_tool}" PARENT_SCOPE)
+  endif()
+
   set(${log_var} "${log}" PARENT_SCOPE)
 
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS)
+    set(${EXTRA_PARSE_COMPUTE_IMPLICIT_LIBS} "${implicit_libs}" PARENT_SCOPE)
+  endif()
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS)
+    set(${EXTRA_PARSE_COMPUTE_IMPLICIT_DIRS} "${implicit_dirs}" PARENT_SCOPE)
+  endif()
+  if(EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS)
+    set(${EXTRA_PARSE_COMPUTE_IMPLICIT_FWKS} "${implicit_fwks}" PARENT_SCOPE)
+  endif()
   if(EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS)
     set(${EXTRA_PARSE_COMPUTE_IMPLICIT_OBJECTS} "${implicit_objs}" PARENT_SCOPE)
   endif()
diff --git a/Modules/CMakePlatformId.h.in b/Modules/CMakePlatformId.h.in
index 32b7166..fd0367e 100644
--- a/Modules/CMakePlatformId.h.in
+++ b/Modules/CMakePlatformId.h.in
@@ -219,6 +219,14 @@
 #  define ARCHITECTURE_ID ""
 # endif
 
+#elif defined(__clang__) && defined(__ti__)
+# if defined(__ARM_ARCH)
+#  define ARCHITECTURE_ID "Arm"
+
+# else /* unknown architecture */
+#  define ARCHITECTURE_ID ""
+# endif
+
 #elif defined(__TI_COMPILER_VERSION__)
 # if defined(__TI_ARM__)
 #  define ARCHITECTURE_ID "ARM"
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 1c6f0df..f8b6748 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -56,6 +56,7 @@
 set(CMAKE_Swift_LINK_LIBRARY_FLAG "-l")
 set(CMAKE_Swift_LINKER_WRAPPER_FLAG "-Xlinker" " ")
 set(CMAKE_Swift_RESPONSE_FILE_LINK_FLAG @)
+set(CMAKE_Swift_RESPONSE_FILE_FLAG @)
 
 set(CMAKE_Swift_LINKER_PREFERENCE 50)
 set(CMAKE_Swift_LINKER_PREFERENCE_PROPAGATES 1)
@@ -68,30 +69,42 @@
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -libc MTd)
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -libc MDd)
 
+set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g")
+set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
+set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
+set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
+
 if(CMAKE_GENERATOR STREQUAL "Xcode")
+  string(APPEND CMAKE_Swift_FLAGS_DEBUG_INIT " ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}")
+  string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " ${CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS}")
+endif()
+
+# Warns if unset and uses old policy.
+# Old policy flag-smashes the wmo and incremental flags onto the compiler flags.
+# New policy respects the Swift_COMPILATION_MODE target property to add
+# incremental and wholemodule optimization flags as appropriate.
+cmake_policy(GET CMP0157 __SWIFT_COMP_MODE_CMP0157)
+if(__SWIFT_COMP_MODE_CMP0157 STREQUAL "NEW")
+  set(CMAKE_Swift_COMPILATION_MODE_DEFAULT "incremental")
+else()
   # Xcode has a separate Xcode project option (SWIFT_COMPILATION_MODE) used to set
   # whether compiling with whole-module optimizations or incrementally. Setting
   # these options here will have no effect when compiling with the built-in driver,
   # and will explode violently, leaving build products in the source directory, when
-  # using the old swift driver.
-  set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}")
-  set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
-  set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g ${CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS}")
-  set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
-else()
-  set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g -incremental")
-  set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
-  set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
-  set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
-
-  # Enable Whole Module Optimization by default unless the old
-  # C++ driver is being used, which behaves differently under WMO.
-  if(NOT CMAKE_Swift_COMPILER_USE_OLD_DRIVER)
-    string(APPEND CMAKE_Swift_FLAGS_RELEASE_INIT " -wmo")
-    string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " -wmo")
-    string(APPEND CMAKE_Swift_FLAGS_MINSIZEREL_INIT " -wmo")
+  # using the old swift driver. Don't append `-incremental` or `-wmo` to the
+  # flags in the Xcode generator.
+  if(NOT CMAKE_GENERATOR STREQUAL "Xcode")
+    # Enable Whole Module Optimization by default unless the old
+    # C++ driver is being used, which behaves differently under WMO.
+    if(NOT CMAKE_Swift_COMPILER_USE_OLD_DRIVER)
+      string(APPEND CMAKE_Swift_FLAGS_RELEASE_INIT " -wmo")
+      string(APPEND CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT " -wmo")
+      string(APPEND CMAKE_Swift_FLAGS_MINSIZEREL_INIT " -wmo")
+    endif()
+    string(APPEND CMAKE_Swift_FLAGS_DEBUG_INIT " -incremental")
   endif()
 endif()
+unset(__SWIFT_COMP_MODE_CMP0157)
 
 if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
   if(NOT DEFINED CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG)
@@ -104,36 +117,66 @@
 
 cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags")
 
-# NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step
-if(NOT CMAKE_Swift_COMPILE_OBJECT)
-  set(CMAKE_Swift_COMPILE_OBJECT ":")
-endif()
-
 if(NOT CMAKE_Swift_NUM_THREADS MATCHES "^[0-9]+$")
   cmake_host_system_information(RESULT CMAKE_Swift_NUM_THREADS QUERY NUMBER_OF_LOGICAL_CORES)
 endif()
 
-if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY)
-  set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
-endif()
+# Swift split-compilation requires CMP0157 NEW policy
+if(CMAKE_Swift_COMPILATION_MODE_DEFAULT)
+  set(CMAKE_Swift_PARALLEL_FLAGS "-j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS}")
+  if(NOT CMAKE_Swift_COMPILE_OBJECT)
+    # Omit the object output. The output is controlled by the output-file-map
+    # for normal builds. For wholemodule builds, CMake appends the appropriate
+    # flags.
+    set(CMAKE_Swift_COMPILE_OBJECT "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -c <DEFINES> <FLAGS> <INCLUDES> <SOURCE>")
+  endif()
 
-if(NOT CMAKE_Swift_CREATE_SHARED_MODULE)
-  set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY})
-endif()
+  if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY)
+    set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-library <CMAKE_SHARED_LIBRARY_Swift_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <SONAME_FLAG> <TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
+  endif()
 
-if(NOT CMAKE_Swift_LINK_EXECUTABLE)
-  set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
-endif()
+  if(NOT CMAKE_Swift_CREATE_SHARED_MODULE)
+    set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY})
+  endif()
 
-if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS)
-  set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path <SWIFT_MODULE> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}")
-endif()
+  if(NOT CMAKE_Swift_LINK_EXECUTABLE)
+    set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> ${CMAKE_Swift_PARALLEL_FLAGS} -emit-executable -o <TARGET> <FLAGS> <OBJECTS> <LINK_FLAGS> <LINK_LIBRARIES>")
+  endif()
 
-if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY)
-  set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
+  if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY)
+    set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -emit-library -static -o <TARGET> <OBJECTS> <LINK_FLAGS>")
+    set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>")
+    set(CMAKE_Swift_ARCHIVE_FINISH "")
+  endif()
+  unset(CMAKE_Swift_PARALLEL_FLAGS)
+else()
+  # NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step
+  if(NOT CMAKE_Swift_COMPILE_OBJECT)
+    set(CMAKE_Swift_COMPILE_OBJECT ":")
+  endif()
 
-  set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>")
-  set(CMAKE_Swift_ARCHIVE_FINISH "")
+  if(NOT CMAKE_Swift_CREATE_SHARED_LIBRARY)
+    set(CMAKE_Swift_CREATE_SHARED_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <SONAME_FLAG> <TARGET_INSTALLNAME_DIR><TARGET_SONAME> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS} <LINK_LIBRARIES>")
+  endif()
+
+  if(NOT CMAKE_Swift_CREATE_SHARED_MODULE)
+    set(CMAKE_Swift_CREATE_SHARED_MODULE ${CMAKE_Swift_CREATE_SHARED_LIBRARY})
+  endif()
+
+  if(NOT CMAKE_Swift_LINK_EXECUTABLE)
+    set(CMAKE_Swift_LINK_EXECUTABLE "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-executable -o <TARGET> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
+  endif()
+
+  if(NOT CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS)
+    set(CMAKE_Swift_LINK_EXECUTABLE_WITH_EXPORTS "${CMAKE_Swift_LINK_EXECUTABLE} -emit-module -emit-module-path <SWIFT_MODULE> ${CMAKE_Swift_IMPLIB_LINKER_FLAGS}")
+  endif()
+
+  if(NOT CMAKE_Swift_CREATE_STATIC_LIBRARY)
+    set(CMAKE_Swift_CREATE_STATIC_LIBRARY "<CMAKE_Swift_COMPILER> -j ${CMAKE_Swift_NUM_THREADS} -num-threads ${CMAKE_Swift_NUM_THREADS} -emit-library -static -o <TARGET> -module-name <SWIFT_MODULE_NAME> -module-link-name <SWIFT_LIBRARY_NAME> -emit-module -emit-module-path <SWIFT_MODULE> -emit-dependencies <DEFINES> <FLAGS> <INCLUDES> <SWIFT_SOURCES> <LINK_FLAGS> <LINK_LIBRARIES>")
+
+    set(CMAKE_Swift_ARCHIVE_CREATE "<CMAKE_AR> crs <TARGET> <OBJECTS>")
+    set(CMAKE_Swift_ARCHIVE_FINISH "")
+  endif()
 endif()
 
 set(CMAKE_Swift_INFORMATION_LOADED 1)
diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake
index ff1cb7e..813ac3c 100644
--- a/Modules/CPack.cmake
+++ b/Modules/CPack.cmake
@@ -866,6 +866,7 @@
         "${_CMP0133_warning}\n"
         "For compatibility, CMake will enable the SLA in the CPack DragNDrop Generator."
         )
+      unset(_CMP0133_warning)
     endif()
     _cpack_set_default(CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE ON)
   endif()
@@ -882,6 +883,24 @@
 # WiX specific variables
 _cpack_set_default(CPACK_WIX_SIZEOF_VOID_P "${CMAKE_SIZEOF_VOID_P}")
 
+# productbuild specific variables
+cmake_policy(GET CMP0161 _CPack_CMP0161)
+if("x${_CPack_CMP0161}x" STREQUAL "xNEWx")
+  _cpack_set_default(CPACK_PRODUCTBUILD_DOMAINS ON)
+elseif(APPLE AND CPACK_BINARY_PRODUCTBUILD AND
+       NOT DEFINED CPACK_PRODUCTBUILD_DOMAINS AND
+       NOT "x${_CPack_CMP0161}x" STREQUAL "xOLDx")
+  cmake_policy(GET_WARNING CMP0161 _CMP0161_warning)
+  message(AUTHOR_WARNING
+    "${_CMP0161_warning}\n"
+    "For compatibility, CPACK_PRODUCTBUILD_DOMAINS will remain unset. "
+    "Explicitly setting CPACK_PRODUCTBUILD_DOMAINS or setting policy CMP0161 "
+    "to NEW will prevent this warning."
+  )
+  unset(_CMP0161_warning)
+endif()
+unset(_CPack_CMP0161)
+
 # set sysroot so SDK tools can be used
 if(CMAKE_OSX_SYSROOT)
   _cpack_set_default(CPACK_OSX_SYSROOT "${_CMAKE_OSX_SYSROOT_PATH}")
diff --git a/Modules/CSharpUtilities.cmake b/Modules/CSharpUtilities.cmake
index dedb146..b5520ef 100644
--- a/Modules/CSharpUtilities.cmake
+++ b/Modules/CSharpUtilities.cmake
@@ -64,7 +64,7 @@
   Sets source file properties of ``.Designer.cs`` files depending on
   sibling filenames. Use this, if your CSharp target does **not**
   use Windows Forms (for Windows Forms use
-  :command:`csharp_set_designer_cs_properties` instead)::
+  :command:`csharp_set_windows_forms_properties` instead)::
 
     csharp_set_designer_cs_properties([<file1> [<file2> [...]]])
 
diff --git a/Modules/CheckIPOSupported/main.c b/Modules/CheckIPOSupported/main.c
index 5be0864..8d2ab01 100644
--- a/Modules/CheckIPOSupported/main.c
+++ b/Modules/CheckIPOSupported/main.c
@@ -1,6 +1,6 @@
 int foo();
 
-int main()
+int main(void)
 {
   return foo();
 }
diff --git a/Modules/CheckLibraryExists.lists.in b/Modules/CheckLibraryExists.lists.in
deleted file mode 100644
index 741b87d..0000000
--- a/Modules/CheckLibraryExists.lists.in
+++ /dev/null
@@ -1,8 +0,0 @@
-PROJECT(CHECK_LIBRARY_EXISTS)
-
-
-ADD_DEFINITIONS(-DCHECK_FUNCTION_EXISTS=${CHECK_LIBRARY_EXISTS_FUNCTION})
-LINK_DIRECTORIES(${CHECK_LIBRARY_EXISTS_LOCATION})
-ADD_EXECUTABLE(CheckLibraryExists ${CHECK_LIBRARY_EXISTS_SOURCE})
-TARGET_LINK_LIBRARIES(CheckLibraryExists ${CHECK_LIBRARY_EXISTS_LIBRARY})
-
diff --git a/Modules/Compiler/ADSP-ASM.cmake b/Modules/Compiler/ADSP-ASM.cmake
new file mode 100644
index 0000000..9ef5142
--- /dev/null
+++ b/Modules/Compiler/ADSP-ASM.cmake
@@ -0,0 +1,6 @@
+include(Compiler/ADSP)
+__compiler_adsp(ASM)
+
+set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS asm)
+set(CMAKE_ASM_OUTPUT_EXTENSION ".o" )
+set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> <SOURCE>")
diff --git a/Modules/Compiler/ADSP.cmake b/Modules/Compiler/ADSP.cmake
index 62566a0..39dcf39 100644
--- a/Modules/Compiler/ADSP.cmake
+++ b/Modules/Compiler/ADSP.cmake
@@ -10,6 +10,8 @@
 
   set(_CMAKE_${lang}_ADSP_FLAGS "-proc=${CMAKE_ADSP_PROCESSOR}")
 
+  set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -Mo <DEP_FILE>")
+
   set(CMAKE_${lang}_COMPILE_OBJECT
     "<CMAKE_${lang}_COMPILER> ${_CMAKE_${lang}_ADSP_FLAGS} <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")
 
diff --git a/Modules/Compiler/Clang.cmake b/Modules/Compiler/Clang.cmake
index 4c033ca..2452d5e 100644
--- a/Modules/Compiler/Clang.cmake
+++ b/Modules/Compiler/Clang.cmake
@@ -17,6 +17,7 @@
 
 if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"
+    OR "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC"
     OR "x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC")
   macro(__compiler_clang lang)
   endmacro()
@@ -94,11 +95,11 @@
     endif()
 
     set(CMAKE_${lang}_ARCHIVE_CREATE_IPO
-      "\"${__ar}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>"
+      "\"${__ar}\" qc <TARGET> <LINK_FLAGS> <OBJECTS>"
     )
 
     set(CMAKE_${lang}_ARCHIVE_APPEND_IPO
-      "\"${__ar}\" r <TARGET> <LINK_FLAGS> <OBJECTS>"
+      "\"${__ar}\" q <TARGET> <LINK_FLAGS> <OBJECTS>"
     )
 
     set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
diff --git a/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake b/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake
deleted file mode 100644
index 2265e5e..0000000
--- a/Modules/Compiler/Comeau-CXX-DetermineCompiler.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-
-set(_compiler_id_pp_test "defined(__COMO__)")
-
-set(_compiler_id_version_compute "
-  /* __COMO_VERSION__ = VRR */
-# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__COMO_VERSION__ / 100)
-# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__COMO_VERSION__ % 100)")
diff --git a/Modules/Compiler/GNU-Fortran.cmake b/Modules/Compiler/GNU-Fortran.cmake
index 5dfb03e..452598b 100644
--- a/Modules/Compiler/GNU-Fortran.cmake
+++ b/Modules/Compiler/GNU-Fortran.cmake
@@ -17,10 +17,6 @@
 
 set(CMAKE_Fortran_POSTPROCESS_FLAG "-fpreprocessed")
 
-# No -DNDEBUG for Fortran.
-string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " -Os")
-string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3")
-
 # No -isystem for Fortran because it will not find .mod files.
 unset(CMAKE_INCLUDE_SYSTEM_FLAG_Fortran)
 
diff --git a/Modules/Compiler/GNU.cmake b/Modules/Compiler/GNU.cmake
index d01054b..138bb4b 100644
--- a/Modules/Compiler/GNU.cmake
+++ b/Modules/Compiler/GNU.cmake
@@ -74,36 +74,49 @@
 
     if (NOT DEFINED CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED)
       ## check if this feature is supported by the linker
-      execute_process(COMMAND "${CMAKE_${lang}_COMPILER}" -Wl,--help
-        OUTPUT_VARIABLE _linker_capabilities
-        ERROR_VARIABLE _linker_capabilities)
-      if(_linker_capabilities MATCHES "--dependency-file")
-        set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE)
+      if (CMAKE_${lang}_COMPILER_LINKER AND CMAKE_${lang}_COMPILER_LINKER_ID MATCHES "GNU|LLD")
+        execute_process(COMMAND "${CMAKE_${lang}_COMPILER_LINKER}" --help
+                        OUTPUT_VARIABLE _linker_capabilities
+                        ERROR_VARIABLE _linker_capabilities)
+        if(_linker_capabilities MATCHES "--dependency-file")
+          set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED TRUE)
+        else()
+          set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE)
+        endif()
+        unset(_linker_capabilities)
       else()
         set(CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED FALSE)
       endif()
-      unset(_linker_capabilities)
     endif()
   endif()
   if (CMAKE_${lang}_LINKER_DEPFILE_SUPPORTED)
     set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER TRUE)
   else()
-    unset(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER)
+    set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE)
   endif()
 
-  # For now, due to GNU binutils ld bug when LTO is enabled (see GNU bug
-    # `30568 <https://sourceware.org/bugzilla/show_bug.cgi?id=30568>`_),
-  # deactivate this feature.
-  if (NOT DEFINED CMAKE_LINK_DEPENDS_USE_LINKER)
-    set(CMAKE_LINK_DEPENDS_USE_LINKER FALSE)
+  # Due to GNU binutils ld bug when LTO is enabled (see GNU bug
+  # `30568 <https://sourceware.org/bugzilla/show_bug.cgi?id=30568>`_),
+  # deactivate this feature if the version is less than 2.41.
+  # For now, all known versions of gold linker have also this bug.
+  if (CMAKE_${lang}_COMPILER_LINKER_ID
+      AND (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNUgold"
+           OR (CMAKE_${lang}_COMPILER_LINKER_ID STREQUAL "GNU"
+               AND CMAKE_${lang}_COMPILER_LINKER_VERSION VERSION_LESS "2.41")))
+    set(CMAKE_${lang}_LINK_DEPENDS_USE_LINKER FALSE)
   endif()
 
   # Initial configuration flags.
   string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
   string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
-  string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os -DNDEBUG")
-  string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG")
-  string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG")
+  string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os")
+  string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3")
+  string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
+  if(NOT "x${lang}" STREQUAL "xFortran")
+    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -DNDEBUG")
+  endif()
   set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
   set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
   if(NOT APPLE OR NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4) # work around #4462
@@ -155,11 +168,11 @@
     #
     # [1]: https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Optimize-Options.html
     set(CMAKE_${lang}_ARCHIVE_CREATE_IPO
-      "\"${CMAKE_${lang}_COMPILER_AR}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>"
+      "\"${CMAKE_${lang}_COMPILER_AR}\" qc <TARGET> <LINK_FLAGS> <OBJECTS>"
     )
 
     set(CMAKE_${lang}_ARCHIVE_APPEND_IPO
-      "\"${CMAKE_${lang}_COMPILER_AR}\" r <TARGET> <LINK_FLAGS> <OBJECTS>"
+      "\"${CMAKE_${lang}_COMPILER_AR}\" q <TARGET> <LINK_FLAGS> <OBJECTS>"
     )
 
     set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
diff --git a/Modules/Compiler/IBMClang.cmake b/Modules/Compiler/IBMClang.cmake
index 169a0f0..a43f788 100644
--- a/Modules/Compiler/IBMClang.cmake
+++ b/Modules/Compiler/IBMClang.cmake
@@ -58,11 +58,11 @@
   set(__ranlib "${CMAKE_${lang}_COMPILER_RANLIB}")
 
   set(CMAKE_${lang}_ARCHIVE_CREATE_IPO
-    "\"${__ar}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>"
+    "\"${__ar}\" qc <TARGET> <LINK_FLAGS> <OBJECTS>"
   )
 
   set(CMAKE_${lang}_ARCHIVE_APPEND_IPO
-    "\"${__ar}\" r <TARGET> <LINK_FLAGS> <OBJECTS>"
+    "\"${__ar}\" q <TARGET> <LINK_FLAGS> <OBJECTS>"
   )
 
   set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
diff --git a/Modules/Compiler/IntelLLVM.cmake b/Modules/Compiler/IntelLLVM.cmake
index f3c0bf4..5afb1ce 100644
--- a/Modules/Compiler/IntelLLVM.cmake
+++ b/Modules/Compiler/IntelLLVM.cmake
@@ -19,8 +19,8 @@
 macro(__compiler_intel_llvm_common lang)
   set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
   set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-  set(CMAKE_${lang}_ARCHIVE_CREATE_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>")
-  set(CMAKE_${lang}_ARCHIVE_APPEND_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" r <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_CREATE_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" qc <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" q <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_FINISH_IPO "\"${CMAKE_${lang}_COMPILER_RANLIB}\" <TARGET>")
 endmacro()
 
@@ -36,6 +36,12 @@
     __compiler_intel_llvm_common(${lang})
     set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-Qipo")
     set(CMAKE_${lang}_LINK_OPTIONS_IPO "-Qipo")
+    if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "2021.4")
+      set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-external:I")
+      if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "2022.2")
+        set(_CMAKE_INCLUDE_SYSTEM_FLAG_${lang}_WARNING "-external:W0 ")
+      endif ()
+    endif ()
   endmacro()
 else()
   # GNU-like
diff --git a/Modules/Compiler/LCC-C.cmake b/Modules/Compiler/LCC-C.cmake
index 3dd6e68..52c3bf3 100644
--- a/Modules/Compiler/LCC-C.cmake
+++ b/Modules/Compiler/LCC-C.cmake
@@ -12,18 +12,25 @@
 
 set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
 
-set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
-set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
-set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 1.23)
+  set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
+  set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
+  set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
 set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99")
 set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99")
 set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
-set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11")
-set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11")
-set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
-set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17")
-set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17")
-set(CMAKE_C23_STANDARD_COMPILE_OPTION "-std=c2x")
-set(CMAKE_C23_EXTENSION_COMPILE_OPTION "-std=gnu2x")
 
-__compiler_check_default_language_standard(C 1.23 90 1.20 11 1.26 17)
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 1.20)
+  set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11")
+  set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11")
+  set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 1.26)
+  set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std=c17")
+  set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std=gnu17")
+endif()
+
+__compiler_check_default_language_standard(C 1.17 89 1.23 99 1.26 17)
diff --git a/Modules/Compiler/LCC-CXX.cmake b/Modules/Compiler/LCC-CXX.cmake
index b3bdd3c..385947a 100644
--- a/Modules/Compiler/LCC-CXX.cmake
+++ b/Modules/Compiler/LCC-CXX.cmake
@@ -17,15 +17,27 @@
 set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-std=c++98")
 set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98")
 set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON)
-set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11")
-set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11")
-set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON)
-set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
-set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
-set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON)
-set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17")
-set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17")
-set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
-set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a")
+
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.20)
+  set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11")
+  set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11")
+  set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.21)
+  set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
+  set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
+  set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON)
+endif()
+
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.24)
+  set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17")
+  set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17")
+endif()
+
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 1.26)
+  set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a")
+  set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a")
+endif()
 
 __compiler_check_default_language_standard(CXX 1.19 98 1.20 11 1.21 14 1.24 17 1.26 20)
diff --git a/Modules/Compiler/LCC.cmake b/Modules/Compiler/LCC.cmake
index f8c2084..2892ec6 100644
--- a/Modules/Compiler/LCC.cmake
+++ b/Modules/Compiler/LCC.cmake
@@ -65,11 +65,11 @@
   set(CMAKE_${lang}_COMPILE_OPTIONS_IPO ${__lto_flags})
 
   set(CMAKE_${lang}_ARCHIVE_CREATE_IPO
-    "\"${CMAKE_${lang}_COMPILER_AR}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>"
+    "\"${CMAKE_${lang}_COMPILER_AR}\" qc <TARGET> <LINK_FLAGS> <OBJECTS>"
   )
 
   set(CMAKE_${lang}_ARCHIVE_APPEND_IPO
-    "\"${CMAKE_${lang}_COMPILER_AR}\" r <TARGET> <LINK_FLAGS> <OBJECTS>"
+    "\"${CMAKE_${lang}_COMPILER_AR}\" q <TARGET> <LINK_FLAGS> <OBJECTS>"
   )
 
   set(CMAKE_${lang}_ARCHIVE_FINISH_IPO
diff --git a/Modules/Compiler/LLVMFlang-Fortran.cmake b/Modules/Compiler/LLVMFlang-Fortran.cmake
index d27f094..0a432a9 100644
--- a/Modules/Compiler/LLVMFlang-Fortran.cmake
+++ b/Modules/Compiler/LLVMFlang-Fortran.cmake
@@ -15,6 +15,9 @@
 
 set(CMAKE_Fortran_COMPILE_OPTIONS_TARGET "--target=")
 
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG "-Wl,")
+set(CMAKE_Fortran_LINKER_WRAPPER_FLAG_SEP ",")
+
 if(NOT "x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC")
   set(CMAKE_Fortran_VERBOSE_FLAG "-v")
 
diff --git a/Modules/Compiler/TIClang-ASM.cmake b/Modules/Compiler/TIClang-ASM.cmake
new file mode 100644
index 0000000..6bb07e3
--- /dev/null
+++ b/Modules/Compiler/TIClang-ASM.cmake
@@ -0,0 +1,9 @@
+include(Compiler/TIClang)
+
+set(CMAKE_ASM_OUTPUT_EXTENSION ".o")
+set(CMAKE_ASM_OUTPUT_EXTENSION_REPLACE 1)
+
+set(CMAKE_ASM_COMPILE_OBJECT       "<CMAKE_ASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -c -o <OBJECT> <SOURCE>")
+set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS S;s;asm;msa)
+
+__compiler_ticlang(ASM)
diff --git a/Modules/Compiler/TIClang-C-FeatureTests.cmake b/Modules/Compiler/TIClang-C-FeatureTests.cmake
new file mode 100644
index 0000000..ef79229
--- /dev/null
+++ b/Modules/Compiler/TIClang-C-FeatureTests.cmake
@@ -0,0 +1 @@
+include(Compiler/Clang-C-FeatureTests)
diff --git a/Modules/Compiler/TIClang-C.cmake b/Modules/Compiler/TIClang-C.cmake
new file mode 100644
index 0000000..2721fef
--- /dev/null
+++ b/Modules/Compiler/TIClang-C.cmake
@@ -0,0 +1,23 @@
+include(Compiler/Clang-C)
+include(Compiler/TIClang)
+__compiler_ticlang(C)
+
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+    AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+    AND CMAKE_DEPFILE_FLAGS_C)
+  # dependencies are computed by the compiler itself
+  set(CMAKE_C_DEPFILE_FORMAT gcc)
+  set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
+endif()
+
+set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
+set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
+set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_C99_STANDARD_COMPILE_OPTION "-std=c99")
+set(CMAKE_C99_EXTENSION_COMPILE_OPTION "-std=gnu99")
+set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std=c11")
+set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std=gnu11")
+set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
diff --git a/Modules/Compiler/TIClang-CXX-FeatureTests.cmake b/Modules/Compiler/TIClang-CXX-FeatureTests.cmake
new file mode 100644
index 0000000..e038e80
--- /dev/null
+++ b/Modules/Compiler/TIClang-CXX-FeatureTests.cmake
@@ -0,0 +1 @@
+include(Compiler/Clang-CXX-FeatureTests)
diff --git a/Modules/Compiler/TIClang-CXX.cmake b/Modules/Compiler/TIClang-CXX.cmake
new file mode 100644
index 0000000..860bb42
--- /dev/null
+++ b/Modules/Compiler/TIClang-CXX.cmake
@@ -0,0 +1,25 @@
+include(Compiler/Clang-CXX)
+include(Compiler/TIClang)
+__compiler_ticlang(CXX)
+
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+    AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+    AND CMAKE_DEPFILE_FLAGS_CXX)
+  # dependencies are computed by the compiler itself
+  set(CMAKE_CXX_DEPFILE_FORMAT gcc)
+  set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
+endif()
+
+set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-std=c++98")
+set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "-std=gnu++98")
+set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11")
+set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=gnu++11")
+
+set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
+set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14")
+set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17")
+set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17")
diff --git a/Modules/Compiler/TIClang-DetermineCompiler.cmake b/Modules/Compiler/TIClang-DetermineCompiler.cmake
new file mode 100644
index 0000000..2447fdf
--- /dev/null
+++ b/Modules/Compiler/TIClang-DetermineCompiler.cmake
@@ -0,0 +1,10 @@
+# TI Clang-based Toolchains
+set(_compiler_id_pp_test "defined(__clang__) && defined(__ti__)")
+
+set(_compiler_id_version_compute "
+  # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__ti_major__)
+  # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__ti_minor__)
+  # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__ti_patchlevel__)")
+
+string(APPEND _compiler_id_version_compute "
+# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__ti_version__)")
diff --git a/Modules/Compiler/TIClang.cmake b/Modules/Compiler/TIClang.cmake
new file mode 100644
index 0000000..4965e62
--- /dev/null
+++ b/Modules/Compiler/TIClang.cmake
@@ -0,0 +1,30 @@
+if(__COMPILER_TICLANG)
+  return()
+endif()
+set(__COMPILER_TICLANG TRUE)
+
+include(Compiler/CMakeCommonCompilerMacros)
+
+# get linker supported cpu list
+macro(__compiler_ticlang lang)
+  set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-Xlinker ")
+
+  set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
+
+  set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
+
+  set(CMAKE_${lang}_COMPILE_OBJECT  "<CMAKE_${lang}_COMPILER> -c <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+
+  set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_${lang}_COMPILER> <FLAGS> -Xlinker --output_file=<TARGET> -Xlinker --map_file=<TARGET_NAME>.map -Xlinker --rom_model <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>")
+
+  set(CMAKE_${lang}_ARCHIVE_CREATE  "<CMAKE_AR> cr <TARGET> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND  "<CMAKE_AR> r <TARGET> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH  "")
+
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP)
+endmacro()
+
+set(CMAKE_EXECUTABLE_SUFFIX ".out")
+set(CMAKE_LIBRARY_PATH_FLAG "-Wl,--search_path=")
+set(CMAKE_LINK_LIBRARY_FLAG "-Wl,--library=")
diff --git a/Modules/CompilerId/VS-Intel.vfproj.in b/Modules/CompilerId/VS-Intel.vfproj.in
index 044dd20..fdd9d9d 100644
--- a/Modules/CompilerId/VS-Intel.vfproj.in
+++ b/Modules/CompilerId/VS-Intel.vfproj.in
@@ -13,7 +13,7 @@
 			Name="Debug|@id_platform@"
 			OutputDirectory="."
 			IntermediateDirectory="$(ConfigurationName)"
-			>
+			@id_UseCompiler@>
 			<Tool
 				Name="VFFortranCompilerTool"
 				DebugInformationFormat="debugEnabled"
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index a5dceaa..8b10135 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -1518,6 +1518,21 @@
   netrc
   netrc_file
 )
+  if("x${REMOTE}" STREQUAL "x")
+    message(FATAL_ERROR "REMOTE can't be empty")
+  endif()
+  if("x${LOCAL}" STREQUAL "x")
+    message(FATAL_ERROR "LOCAL can't be empty")
+  endif()
+
+  # REMOTE could contain special characters that parse as separate arguments.
+  # Things like parentheses are legitimate characters in a URL, but would be
+  # seen as the start of a new unquoted argument by the cmake language parser.
+  # Avoid those special cases by preparing quoted strings for direct inclusion
+  # in the foreach() call that iterates over the set of URLs in REMOTE.
+  set(REMOTE "[====[${REMOTE}]====]")
+  string(REPLACE ";" "]====] [====[" REMOTE "${REMOTE}")
+
   if(timeout)
     set(TIMEOUT_ARGS TIMEOUT ${timeout})
     set(TIMEOUT_MSG "${timeout} seconds")
@@ -1599,7 +1614,7 @@
 
   set(HTTP_HEADERS_ARGS "")
   if(NOT http_headers STREQUAL "")
-    foreach(header ${http_headers})
+    foreach(header IN LISTS http_headers)
       string(PREPEND HTTP_HEADERS_ARGS
         "HTTPHEADER \"${header}\"\n        "
       )
@@ -1724,7 +1739,7 @@
 
   # Apply defaults and convert to absolute paths.
   set(places stamp download source binary install tmp)
-  foreach(var ${places})
+  foreach(var IN LISTS places)
     string(TOUPPER "${var}" VAR)
     get_property(${var}_dir TARGET ${name} PROPERTY _EP_${VAR}_DIR)
     if(NOT ${var}_dir)
@@ -1796,9 +1811,9 @@
 #
 macro(_ep_replace_location_tags target_name)
   set(vars ${ARGN})
-  foreach(var ${vars})
-    if(${var})
-      foreach(dir
+  foreach(var IN LISTS vars)
+    if(var)
+      foreach(dir IN ITEMS
         SOURCE_DIR
         SOURCE_SUBDIR
         BINARY_DIR
@@ -1828,7 +1843,7 @@
   if(force)
     set(forceArg "FORCE")
   endif()
-  foreach(line ${args})
+  foreach(line IN LISTS args)
     if("${line}" MATCHES "^-D(.*)")
       set(line "${CMAKE_MATCH_1}")
       if(NOT "${setArg}" STREQUAL "")
@@ -1884,7 +1899,7 @@
 
 
 function(ExternalProject_Get_Property name)
-  foreach(var ${ARGN})
+  foreach(var IN LISTS ARGN)
     string(TOUPPER "${var}" VAR)
     get_property(is_set TARGET ${name} PROPERTY _EP_${VAR} SET)
     if(NOT is_set)
@@ -1934,8 +1949,10 @@
   set(args)
   _ep_get_configure_command_id(${name} cfg_cmd_id)
   if(cfg_cmd_id STREQUAL "cmake")
-    # CMake project.  Select build command based on generator.
-    get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR)
+    # Adding a CMake project as an External Project.  Select command based on generator
+    get_property(cmake_generator TARGET ${name} PROPERTY _EP_CMAKE_GENERATOR)
+    # cmake_generator is the CMake generator of the ExternalProject target being added
+    # CMAKE_GENERATOR is the CMake generator of the Current Project
     if("${CMAKE_GENERATOR}" MATCHES "Make" AND
        ("${cmake_generator}" MATCHES "Make" OR NOT cmake_generator))
       # The project uses the same Makefile generator.  Use recursive make.
@@ -1948,6 +1965,11 @@
       endif()
     else()
       # Drive the project with "cmake --build".
+      if(NOT cmake_generator)
+        # If there is no CMake Generator defined on the ExternalProject,
+        # use the same Generator as the current project
+        set(cmake_generator "${CMAKE_GENERATOR}")
+      endif()
       get_target_property(cmake_command ${name} _EP_CMAKE_COMMAND)
       if(cmake_command)
         set(cmd "${cmake_command}")
@@ -1977,7 +1999,11 @@
         list(APPEND args --config ${config})
       endif()
       if(step STREQUAL "INSTALL")
-        list(APPEND args --target install)
+        if("${cmake_generator}" MATCHES "Green Hills MULTI")
+          list(APPEND args --target INSTALL)
+        else()
+          list(APPEND args --target install)
+        endif()
       endif()
       # But for "TEST" drive the project with corresponding "ctest".
       if("x${step}x" STREQUAL "xTESTx")
@@ -2361,7 +2387,7 @@
     endif()
     message(AUTHOR_WARNING "${_cmp0114_warning}")
   endif()
-  foreach(step ${steps})
+  foreach(step IN LISTS steps)
     _ep_step_add_target("${name}" "${step}" "${no_deps}")
   endforeach()
 endfunction()
@@ -2542,7 +2568,7 @@
     get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
     if(_isMultiConfig)
       _ep_get_configuration_subdir_genex(cfgdir)
-      foreach(cfg ${CMAKE_CONFIGURATION_TYPES})
+      foreach(cfg IN LISTS CMAKE_CONFIGURATION_TYPES)
         string(REPLACE "${cfgdir}" "/${cfg}"
           stamp_file_config "${stamp_file}"
         )
@@ -2617,7 +2643,7 @@
       PROPERTY EP_STEP_TARGETS
     )
   endif()
-  foreach(st ${step_targets})
+  foreach(st IN LISTS step_targets)
     if("${st}" STREQUAL "${step}")
       _ep_step_add_target("${name}" "${step}" "FALSE")
       break()
@@ -2664,7 +2690,7 @@
         message(AUTHOR_WARNING "${_cmp0114_warning}")
       endif()
     endif()
-    foreach(st ${independent_step_targets})
+    foreach(st IN LISTS independent_step_targets)
       if("${st}" STREQUAL "${step}")
         _ep_step_add_target("${name}" "${step}" "TRUE")
         break()
@@ -2730,17 +2756,15 @@
   # Always add file-level dependency, but add target-level dependency
   # only if the target exists for that step.
   _ep_get_step_stampfile(${name} ${step} stamp_file)
-  foreach(dep ${dependencies})
+  foreach(dep IN LISTS dependencies)
     add_custom_command(APPEND
       OUTPUT ${stamp_file}
       DEPENDS ${dep}
     )
-    if(TARGET ${name}-${step})
-      foreach(dep ${dependencies})
-        add_dependencies(${name}-${step} ${dep})
-      endforeach()
-    endif()
   endforeach()
+  if(TARGET ${name}-${step})
+    add_dependencies(${name}-${step} ${dependencies})
+  endif()
 
 endfunction()
 
@@ -3068,7 +3092,7 @@
 
     list(LENGTH url url_list_length)
     if(NOT "${url_list_length}" STREQUAL "1")
-      foreach(entry ${url})
+      foreach(entry IN LISTS url)
         if(NOT "${entry}" MATCHES "^[a-z]+://")
           message(FATAL_ERROR
             "At least one entry of URL is a path (invalid in a list)"
@@ -3763,6 +3787,9 @@
         list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}")
       else()
         list(APPEND cmd "-G${CMAKE_GENERATOR}")
+        # GreenHills needs to know about the compiler and toolset.
+        # Be sure to update the similar section in
+        # FetchContent.cmake:__FetchContent_directPopulate()
         if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI")
           set(has_cmake_cache_default_args 1)
           list(APPEND cmake_cache_default_args
diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in
index bf7f209..0ad0dd3 100644
--- a/Modules/ExternalProject/download.cmake.in
+++ b/Modules/ExternalProject/download.cmake.in
@@ -71,14 +71,6 @@
   execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep "${sleep_seconds}")
 endfunction()
 
-if("@LOCAL@" STREQUAL "")
-  message(FATAL_ERROR "LOCAL can't be empty")
-endif()
-
-if("@REMOTE@" STREQUAL "")
-  message(FATAL_ERROR "REMOTE can't be empty")
-endif()
-
 if(EXISTS "@LOCAL@")
   check_file_hash(has_hash hash_is_good)
   if(has_hash)
@@ -115,7 +107,7 @@
   if(status_code IN_LIST download_retry_codes)
     sleep_before_download(${i})
   endif()
-  foreach(url @REMOTE@)
+  foreach(url IN ITEMS @REMOTE@)
     if(NOT url IN_LIST skip_url_list)
       message(STATUS "Using src='${url}'")
 
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index 9bbeac0..48cdaf4 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -195,6 +195,12 @@
       still be called if :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` is
       set to ``OPT_IN`` or is not set.
 
+      It would not normally be appropriate to specify ``REQUIRED`` as one of
+      the additional arguments after ``FIND_PACKAGE_ARGS``.  Doing so would
+      mean the :command:`find_package` call must succeed, so none of the other
+      details specified in the ``FetchContent_Declare()`` call would get a
+      chance to be used as a fall-back.
+
       Everything after the ``FIND_PACKAGE_ARGS`` keyword is appended to the
       :command:`find_package` call, so all other ``<contentOptions>`` must
       come before the ``FIND_PACKAGE_ARGS`` keyword.  If the
@@ -373,6 +379,10 @@
       :command:`FetchContent_Declare`, the ``EXCLUDE_FROM_ALL`` keyword will
       be added to the :command:`add_subdirectory` command as well.
 
+    .. versionadded:: 3.29
+      :variable:`CMAKE_EXPORT_FIND_PACKAGE_NAME` is set to the dependency name
+      before calling :command:`add_subdirectory`.
+
   Projects should aim to declare the details of all dependencies they might
   use before they call ``FetchContent_MakeAvailable()`` for any of them.
   This ensures that if any of the dependencies are also sub-dependencies of
@@ -675,6 +685,17 @@
   any content details, turning this option ``ON`` can significantly speed up
   the configure stage.  It is ``OFF`` by default.
 
+  .. note::
+
+    The ``FETCHCONTENT_FULLY_DISCONNECTED`` variable is not an appropriate way
+    to prevent any network access on the first run in a build directory.
+    Doing so can break projects, lead to misleading error messages, and hide
+    subtle population failures.  This variable is specifically intended to
+    only be turned on *after* the first time CMake has been run.
+    If you want to prevent network access even on the first run, use a
+    :ref:`dependency provider <dependency_providers>` and populate the
+    dependency from local content instead.
+
 .. variable:: FETCHCONTENT_UPDATES_DISCONNECTED
 
   This is a less severe download/update control compared to
@@ -1194,10 +1215,10 @@
     set(propertyName "_FetchContent_${contentNameLower}_find_package_args")
     define_property(GLOBAL PROPERTY ${propertyName})
     if(NOT __sawQuietKeyword)
-      list(INSERT __findPackageArgs 0 QUIET)
+      string(PREPEND __findPackageArgs "QUIET ")
     endif()
     if(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL AND NOT __sawGlobalKeyword)
-      list(APPEND __findPackageArgs GLOBAL)
+      string(APPEND __findPackageArgs " GLOBAL")
     endif()
     cmake_language(EVAL CODE
       "set_property(GLOBAL PROPERTY ${propertyName} ${__findPackageArgs})"
@@ -1596,6 +1617,20 @@
       list(APPEND subCMakeOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}")
     endif()
 
+    # GreenHills needs to know about the compiler and toolset to run the
+    # subbuild commands. Be sure to update the similar section in
+    # ExternalProject.cmake:_ep_extract_configure_command()
+    if(CMAKE_GENERATOR MATCHES "Green Hills MULTI")
+      list(APPEND subCMakeOpts
+        "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}"
+        "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}"
+        "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}"
+        "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}"
+        "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}"
+        "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}"
+      )
+    endif()
+
     # Override the sub-build's configuration types for multi-config generators.
     # This ensures we are not affected by any custom setting from the project
     # and can always request a known configuration further below.
@@ -1930,6 +1965,10 @@
           "Trying FETCHCONTENT_MAKEAVAILABLE_SERIAL dependency provider for "
           "${__cmake_contentName}"
         )
+
+        list(APPEND __cmake_fcCurrentVarsStack "__fcprefix__${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+        set(CMAKE_EXPORT_FIND_PACKAGE_NAME "${__cmake_contentName}")
+
         # It's still valid if there are no saved details. The project may have
         # been written to assume a dependency provider is always set and will
         # provide dependencies without having any declared details for them.
@@ -1947,12 +1986,12 @@
         # This property might be defined but empty. As long as it is defined,
         # find_package() can be called.
         get_property(__cmake_addfpargs GLOBAL PROPERTY
-          _FetchContent_${contentNameLower}_find_package_args
+          _FetchContent_${__cmake_contentNameLower}_find_package_args
           DEFINED
         )
         if(__cmake_addfpargs)
           get_property(__cmake_fpargs GLOBAL PROPERTY
-            _FetchContent_${contentNameLower}_find_package_args
+            _FetchContent_${__cmake_contentNameLower}_find_package_args
           )
           string(APPEND __cmake_providerArgs " FIND_PACKAGE_ARGS")
           foreach(__cmake_item IN LISTS __cmake_fpargs)
@@ -1984,6 +2023,12 @@
         unset(__cmake_item)
         unset(__cmake_contentDetails)
 
+        list(POP_BACK __cmake_fcCurrentVarsStack __cmake_original_export_find_package_name)
+        string(SUBSTRING "${__cmake_original_export_find_package_name}"
+          12 -1 __cmake_original_export_find_package_name
+        )
+        set(CMAKE_EXPORT_FIND_PACKAGE_NAME ${__cmake_original_export_find_package_name})
+
         FetchContent_GetProperties(${__cmake_contentName})
         if(${__cmake_contentNameLower}_POPULATED)
           continue()
@@ -2053,6 +2098,9 @@
       endif()
 
       if(EXISTS ${__cmake_srcdir}/CMakeLists.txt)
+        list(APPEND __cmake_fcCurrentVarsStack "__fcprefix__${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+        set(CMAKE_EXPORT_FIND_PACKAGE_NAME "${__cmake_contentName}")
+
         set(__cmake_add_subdirectory_args ${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR})
         if(__cmake_arg_EXCLUDE_FROM_ALL)
           list(APPEND __cmake_add_subdirectory_args EXCLUDE_FROM_ALL)
@@ -2061,6 +2109,12 @@
           list(APPEND __cmake_add_subdirectory_args SYSTEM)
         endif()
         add_subdirectory(${__cmake_add_subdirectory_args})
+
+        list(POP_BACK __cmake_fcCurrentVarsStack __cmake_original_export_find_package_name)
+        string(SUBSTRING "${__cmake_original_export_find_package_name}"
+          12 -1 __cmake_original_export_find_package_name
+        )
+        set(CMAKE_EXPORT_FIND_PACKAGE_NAME ${__cmake_original_export_find_package_name})
       endif()
 
       unset(__cmake_srcdir)
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake
index 39a1163..e9b118f 100644
--- a/Modules/FindBLAS.cmake
+++ b/Modules/FindBLAS.cmake
@@ -746,7 +746,11 @@
   set(_blas_openblas_lib "openblas")
 
   if(_blas_sizeof_integer EQUAL 8)
-    string(APPEND _blas_openblas_lib "64")
+    if(MINGW)
+      string(APPEND _blas_openblas_lib "_64")
+    else()
+      string(APPEND _blas_openblas_lib "64")
+    endif()
   endif()
 
   if(NOT BLAS_LIBRARIES)
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index 6b8b08e..2229de6 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -1100,7 +1100,10 @@
     if(CUDA_${lib_name}_LIBRARY MATCHES "/stubs/" AND NOT WIN32)
       # Use a SHARED library with IMPORTED_IMPLIB, but not IMPORTED_LOCATION,
       # to indicate that the stub is for linkers but not dynamic loaders.
-      # It will not contribute any RPATH entry.
+      # It will not contribute any RPATH entry.  When encountered as
+      # a private transitive dependency of another shared library,
+      # it will be passed explicitly to linkers so they can find it
+      # even when the runtime library file does not exist on disk.
       set(CUDA_IMPORT_PROPERTY IMPORTED_IMPLIB)
       set(CUDA_IMPORT_TYPE     SHARED)
     endif()
@@ -1134,9 +1137,6 @@
     target_link_directories(CUDA::toolkit INTERFACE "${CUDAToolkit_LIBRARY_DIR}")
   endif()
 
-  _CUDAToolkit_find_and_add_import_lib(cuda_driver ALT cuda)
-
-
   # setup dependencies that are required for cudart/cudart_static when building
   # on linux. These are generally only required when using the CUDA toolkit
   # when CUDA language is disabled
@@ -1159,6 +1159,7 @@
     endif()
   endif()
 
+  _CUDAToolkit_find_and_add_import_lib(cuda_driver ALT cuda DEPS cudart_static_deps)
   _CUDAToolkit_find_and_add_import_lib(cudart DEPS cudart_static_deps)
   _CUDAToolkit_find_and_add_import_lib(cudart_static DEPS cudart_static_deps)
 
diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake
index 2f33dac..5e5f1f8 100644
--- a/Modules/FindCURL.cmake
+++ b/Modules/FindCURL.cmake
@@ -94,8 +94,10 @@
 if(PKG_CONFIG_FOUND)
   pkg_check_modules(PC_CURL QUIET libcurl)
   if(PC_CURL_FOUND)
-    pkg_get_variable(CURL_SUPPORTED_PROTOCOLS libcurl supported_protocols)
-    pkg_get_variable(CURL_SUPPORTED_FEATURES libcurl supported_features)
+    pkg_get_variable(CURL_SUPPORTED_PROTOCOLS_STRING libcurl supported_protocols)
+    string(REPLACE " " ";" CURL_SUPPORTED_PROTOCOLS "${CURL_SUPPORTED_PROTOCOLS_STRING}")
+    pkg_get_variable(CURL_SUPPORTED_FEATURES_STRING libcurl supported_features)
+    string(REPLACE " " ";" CURL_SUPPORTED_FEATURES "${CURL_SUPPORTED_FEATURES_STRING}")
   endif()
 endif()
 
diff --git a/Modules/FindCxxTest.cmake b/Modules/FindCxxTest.cmake
index 714927f..a3283fa 100644
--- a/Modules/FindCxxTest.cmake
+++ b/Modules/FindCxxTest.cmake
@@ -10,7 +10,7 @@
 Find the `CxxTest`_ suite and declare a helper macro for creating
 unit tests and integrating them with CTest.
 
-.. _`CxxTest`: https://github.com/CxxTest/cxxtest#readme
+.. _`CxxTest`: https://github.com/CxxTest/cxxtest
 
 Input Variables
 ^^^^^^^^^^^^^^^
diff --git a/Modules/FindICU.cmake b/Modules/FindICU.cmake
index b4f4d71..9514d2d 100644
--- a/Modules/FindICU.cmake
+++ b/Modules/FindICU.cmake
@@ -136,6 +136,7 @@
             HINTS ${icu_roots}
             PATH_SUFFIXES ${icu_include_suffixes}
             DOC "ICU include directory")
+  mark_as_advanced(ICU_INCLUDE_DIR)
   set(ICU_INCLUDE_DIR "${ICU_INCLUDE_DIR}" PARENT_SCOPE)
 
   # Get version
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake
index 4d3ab5a..e142516 100644
--- a/Modules/FindLAPACK.cmake
+++ b/Modules/FindLAPACK.cmake
@@ -487,7 +487,11 @@
     set(_lapack_openblas_lib "openblas")
 
     if(_lapack_sizeof_integer EQUAL 8)
-      string(APPEND _lapack_openblas_lib "64")
+      if(MINGW)
+        string(APPEND _lapack_openblas_lib "_64")
+      else()
+        string(APPEND _lapack_openblas_lib "64")
+      endif()
     endif()
 
     check_lapack_libraries(
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake
index a25f113..834a3e1 100644
--- a/Modules/FindMPI.cmake
+++ b/Modules/FindMPI.cmake
@@ -301,9 +301,9 @@
   set(_MPI_Intel_Fortran_COMPILER_NAMES      mpiifort.bat mpif77.bat mpif90.bat)
 
   # Intel MPI compiler names
-  set(_MPI_IntelLLVM_C_COMPILER_NAMES            mpiicc.bat)
-  set(_MPI_IntelLLVM_CXX_COMPILER_NAMES          mpiicpc.bat)
-  set(_MPI_IntelLLVM_Fortran_COMPILER_NAMES      mpiifort.bat mpif77.bat mpif90.bat)
+  set(_MPI_IntelLLVM_C_COMPILER_NAMES            mpiicx.bat mpiicc.bat)
+  set(_MPI_IntelLLVM_CXX_COMPILER_NAMES          mpiicpx.bat mpiicpc.bat)
+  set(_MPI_IntelLLVM_Fortran_COMPILER_NAMES      mpiifx.bat mpiifort.bat mpif77.bat mpif90.bat)
 
   # Intel MPI compiler names for MSMPI
   set(_MPI_MSVC_C_COMPILER_NAMES             mpicl.bat)
@@ -315,9 +315,9 @@
   set(_MPI_Intel_Fortran_COMPILER_NAMES      mpiifort mpiif95 mpiif90 mpiif77)
 
   # Intel compiler names
-  set(_MPI_IntelLLVM_C_COMPILER_NAMES            mpiicc)
-  set(_MPI_IntelLLVM_CXX_COMPILER_NAMES          mpiicpc  mpiicxx mpiic++)
-  set(_MPI_IntelLLVM_Fortran_COMPILER_NAMES      mpiifort mpiif95 mpiif90 mpiif77)
+  set(_MPI_IntelLLVM_C_COMPILER_NAMES            mpiicx mpiicc)
+  set(_MPI_IntelLLVM_CXX_COMPILER_NAMES          mpiicpx mpiicpc mpiicxx mpiic++)
+  set(_MPI_IntelLLVM_Fortran_COMPILER_NAMES      mpiifx mpiifort mpiif95 mpiif90 mpiif77)
 endif()
 
 # PGI compiler names
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake
index 15c3f64..c71445e 100644
--- a/Modules/FindMatlab.cmake
+++ b/Modules/FindMatlab.cmake
@@ -287,9 +287,12 @@
 set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}")
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
-include(CheckCXXCompilerFlag)
-include(CheckCCompilerFlag)
 
+if(NOT WIN32 AND NOT APPLE AND NOT Threads_FOUND)
+  # MEX files use pthread if available
+  set(THREADS_PREFER_PTHREAD_FLAG ON)
+  find_package(Threads)
+endif()
 
 # The currently supported versions. Other version can be added by the user by
 # providing MATLAB_ADDITIONAL_VERSIONS
@@ -465,7 +468,7 @@
     message(FATAL_ERROR "[MATLAB] This function can only be called by a Windows host")
   endif()
 
-  if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
+  if(${win64})
     set(_view "64")
   else()
     set(_view "32")
@@ -476,20 +479,14 @@
   foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime")
 
     cmake_host_system_information(RESULT _reg
-    QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}"
-    SUBKEYS VIEW ${_view}
+      QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}"
+      SUBKEYS VIEW ${_view}
     )
 
-    if(_reg)
-      string(REGEX MATCHALL "([0-9]+(\\.[0-9]+)+)" _versions_regex "${_reg}")
+    string(REGEX MATCHALL "([0-9]+(\\.[0-9]+)+)" _versions_regex "${_reg}")
 
-      foreach(_match IN LISTS _versions_regex)
-        if(_match MATCHES "([0-9]+(\\.[0-9]+)+)")
-          list(APPEND matlabs_from_registry ${_match})
-        endif()
-      endforeach()
+    list(APPEND matlabs_from_registry ${_versions_regex})
 
-    endif()
   endforeach()
 
   if(matlabs_from_registry)
@@ -557,51 +554,33 @@
     if(IS_DIRECTORY "${current_MATLAB_ROOT}")
       _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp)
       if("${_matlab_version_tmp}" STREQUAL "unknown")
-        list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT})
-      else()
-        list(APPEND _matlab_roots_list "MATLAB" ${_matlab_version_tmp} ${current_MATLAB_ROOT})
+        set(_matlab_version_tmp ${_matlab_current_version})
       endif()
+      list(APPEND _matlab_roots_list "MATLAB" ${_matlab_version_tmp} ${current_MATLAB_ROOT})
     endif()
 
   endforeach()
 
   # Check for MCR installations
-  foreach(_matlab_current_version IN LISTS matlab_versions)
-    cmake_host_system_information(RESULT current_MATLAB_ROOT
-      QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Runtime/${_matlab_current_version}"
-      VALUE "MATLABROOT"
-    )
-    cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
+  foreach(_installation_type IN ITEMS "MATLAB Runtime" "MATLAB Compiler Runtime")
+    foreach(_matlab_current_version IN LISTS matlab_versions)
+      cmake_host_system_information(RESULT current_MATLAB_ROOT
+        QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${_matlab_current_version}"
+        VALUE "MATLABROOT"
+      )
+      cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
 
-    # remove the dot
-    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
+      # remove the dot
+      string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
 
-    if(IS_DIRECTORY "${current_MATLAB_ROOT}")
-      _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp)
-      if("${_matlab_version_tmp}" STREQUAL "unknown")
-        list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
-      else()
+      if(IS_DIRECTORY "${current_MATLAB_ROOT}")
+        _Matlab_VersionInfoXML("${current_MATLAB_ROOT}" _matlab_version_tmp)
+        if("${_matlab_version_tmp}" STREQUAL "unknown")
+          set(_matlab_version_tmp ${_matlab_current_version})
+        endif()
         list(APPEND _matlab_roots_list "MCR" ${_matlab_version_tmp} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
       endif()
-    endif()
-
-  endforeach()
-
-  # Check for old MCR installations
-  foreach(_matlab_current_version IN LISTS matlab_versions)
-    cmake_host_system_information(RESULT current_MATLAB_ROOT
-      QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/MATLAB Compiler Runtime/${_matlab_current_version}"
-      VALUE "MATLABROOT"
-    )
-    cmake_path(CONVERT "${current_MATLAB_ROOT}" TO_CMAKE_PATH_LIST current_MATLAB_ROOT)
-
-    # remove the dot
-    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
-
-    if(IS_DIRECTORY "${current_MATLAB_ROOT}")
-      list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
-    endif()
-
+    endforeach()
   endforeach()
   set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE)
 endfunction()
@@ -658,23 +637,23 @@
     set(devnull INPUT_FILE NUL)
   endif()
 
+  set(_arch)
   if(WIN32)
     # this environment variable is used to determine the arch on Windows
     if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-      set(ENV{MATLAB_ARCH} "win64")
+      set(_arch "MATLAB_ARCH=win64")
     else()
-      set(ENV{MATLAB_ARCH} "win32")
+      set(_arch "MATLAB_ARCH=win32")
     endif()
   endif()
 
   # this is the preferred way. If this does not work properly (eg. MCR on Windows), then we use our own knowledge
   execute_process(
-    COMMAND ${Matlab_MEXEXTENSIONS_PROG}
+    COMMAND ${CMAKE_COMMAND} -E env ${_arch} ${Matlab_MEXEXTENSIONS_PROG}
     OUTPUT_VARIABLE _matlab_mex_extension
     ERROR_VARIABLE _matlab_mex_extension_error
     OUTPUT_STRIP_TRAILING_WHITESPACE
     ${devnull})
-  unset(ENV{MATLAB_ARCH})
 
   if(_matlab_mex_extension_error)
     if(WIN32)
@@ -1068,20 +1047,6 @@
 #]=======================================================================]
 function(matlab_add_mex)
 
-  if(NOT WIN32)
-    # we do not need all this on Windows
-    # pthread options
-    if(CMAKE_CXX_COMPILER_LOADED)
-      check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD)
-    elseif(CMAKE_C_COMPILER_LOADED)
-      check_c_compiler_flag(-pthread HAS_MINUS_PTHREAD)
-    endif()
-    # we should use try_compile instead, the link flags are discarded from
-    # this compiler_flag function.
-    #check_cxx_compiler_flag(-Wl,--exclude-libs,ALL HAS_SYMBOL_HIDING_CAPABILITY)
-
-  endif()
-
   set(options EXECUTABLE MODULE SHARED R2017b R2018a EXCLUDE_FROM_ALL NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES)
   set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME)
   set(multiValueArgs LINK_TO SRC)
@@ -1098,14 +1063,27 @@
   endif()
 
   if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file
+    # Compilers officially supported by Matlab 9.1 (R2016b):
+    #   MinGW 4.9, MSVC 2012, Intel C++ 2013, Xcode 6, GCC 4.9
+    # These compilers definitely support the -w flag to suppress warnings.
+    # Other compilers (Clang) may support the -w flag and can be added here.
+    set(_Matlab_silenceable_compilers AppleClang Clang GNU Intel IntelLLVM MSVC)
+
     # Add the correct version file depending on which languages are enabled in the project
     if(CMAKE_C_COMPILER_LOADED)
       # If C is enabled, use the .c file as it will work fine also with C++
       set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/c_mexapi_version.c")
+      # Silence warnings for version source file
+      if("${CMAKE_C_COMPILER_ID}" IN_LIST _Matlab_silenceable_compilers)
+        set_source_files_properties("${MEX_VERSION_FILE}" PROPERTIES COMPILE_OPTIONS -w)
+      endif()
     elseif(CMAKE_CXX_COMPILER_LOADED)
       # If C is not enabled, check if CXX is enabled and use the .cpp file
       # to avoid that the .c file is silently ignored
       set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/cpp_mexapi_version.cpp")
+      if("${CMAKE_CXX_COMPILER_ID}" IN_LIST _Matlab_silenceable_compilers)
+        set_source_files_properties("${MEX_VERSION_FILE}" PROPERTIES COMPILE_OPTIONS -w)
+      endif()
     else()
       # If neither C or CXX is enabled, warn because we cannot add the source.
       # TODO: add support for fortran mex files
@@ -1236,10 +1214,8 @@
 
     else() # Linux
 
-      if(HAS_MINUS_PTHREAD)
-        # Apparently, compiling with -pthread generated the proper link flags
-        # and some defines at compilation
-        target_compile_options(${${prefix}_NAME} PRIVATE "-pthread")
+      if(Threads_FOUND)
+        target_link_libraries(${${prefix}_NAME} Threads::Threads)
       endif()
 
       string(APPEND _link_flags " -Wl,--as-needed")
@@ -1330,7 +1306,7 @@
 
       find_program(
           _matlab_current_program
-          matlab
+          NAMES matlab
           ${_find_matlab_options}
           DOC "Matlab main program"
         )
@@ -1624,62 +1600,29 @@
 set(Matlab_VERSION_STRING "NOTFOUND")
 set(Matlab_Or_MCR "UNKNOWN")
 if(_numbers_of_matlab_roots GREATER 0)
-  if(Matlab_FIND_VERSION_EXACT)
-    set(_list_index -1)
-    foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
-      list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
-      # only the major.minor version is used
-      string(REGEX REPLACE "^([0-9]+\\.[0-9]+).*" "\\1" _matlab_root_version "${_matlab_root_version}")
-      if(_matlab_root_version VERSION_EQUAL Matlab_FIND_VERSION)
-        set(_list_index ${_matlab_root_index})
-        break()
-      endif()
-    endforeach()
-
-    if(_list_index LESS 0)
-      set(_list_index 1)
+  set(_list_index -1)
+  foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
+    list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
+    find_package_check_version(${_matlab_root_version} _matlab_version_ok HANDLE_VERSION_RANGE)
+    if(_matlab_version_ok)
+      set(_list_index ${_matlab_root_index})
+      break()
     endif()
+  endforeach()
 
-    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
-    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
+  if(_list_index LESS 0)
+    set(_list_index 1)
+  endif()
 
-    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
-    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
-    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
-  elseif(DEFINED Matlab_FIND_VERSION)
-    set(_list_index -1)
-    foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
-      list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
-      if(_matlab_root_version VERSION_GREATER_EQUAL Matlab_FIND_VERSION)
-        set(_list_index ${_matlab_root_index})
-        break()
-      endif()
-    endforeach()
-
-    if(_list_index LESS 0)
-      set(_list_index 1)
-    endif()
-
-    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
-    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
-    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
-    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
-    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
-    # adding a warning in case of ambiguity
-    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
-      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
-                      " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
-    endif()
-  else()
-    list(GET _matlab_possible_roots 0 Matlab_Or_MCR)
-    list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING)
-    list(GET _matlab_possible_roots 2 Matlab_ROOT_DIR)
-
-    # adding a warning in case of ambiguity
-    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
-      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
-                      " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
-    endif()
+  math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
+  math(EXPR _matlab_root_dir_index "${_list_index} + 1")
+  list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
+  list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
+  list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
+  # adding a warning in case of ambiguity
+  if(_numbers_of_matlab_roots GREATER 3 AND NOT Matlab_FIND_VERSION_EXACT AND MATLAB_FIND_DEBUG)
+    message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
+                    " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
   endif()
 endif()
 
@@ -1743,12 +1686,6 @@
   file(TO_CMAKE_PATH ${Matlab_ROOT_DIR} Matlab_ROOT_DIR)
 endif()
 
-if(CMAKE_SIZEOF_VOID_P EQUAL 4)
-  set(_matlab_64Build FALSE)
-else()
-  set(_matlab_64Build TRUE)
-endif()
-
 
 if(NOT DEFINED Matlab_MEX_EXTENSION)
   set(_matlab_mex_extension "")
@@ -1782,7 +1719,7 @@
 
 
 set(MATLAB_INCLUDE_DIR_TO_LOOK ${Matlab_ROOT_DIR}/extern/include)
-if(_matlab_64Build)
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
   set(_matlab_current_suffix ${_matlab_bin_suffix_64bits})
 else()
   set(_matlab_current_suffix ${_matlab_bin_suffix_32bits})
@@ -1807,11 +1744,9 @@
   set(_matlab_lib_prefix_for_search "lib")
 endif()
 
-unset(_matlab_64Build)
-
 
 if(MATLAB_FIND_DEBUG)
-  message(STATUS "[MATLAB] [DEBUG]_matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}")
+  message(STATUS "[MATLAB] _matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}")
 endif()
 
 
@@ -1835,7 +1770,7 @@
 # the MEX library/header are required
 find_path(
   Matlab_INCLUDE_DIRS
-  mex.h
+  NAMES mex.h
   PATHS ${MATLAB_INCLUDE_DIR_TO_LOOK}
   NO_DEFAULT_PATH
   )
@@ -1845,7 +1780,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_MEX_LIBRARY
-    mex
+    NAMES mex
     PATHS ${_matlab_lib_dir_for_search}
     NO_DEFAULT_PATH
   )
@@ -1861,7 +1796,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_MX_LIBRARY
-    mx
+    NAMES mx
     PATHS ${_matlab_lib_dir_for_search}
     NO_DEFAULT_PATH
   )
@@ -1877,7 +1812,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_ENGINE_LIBRARY
-    MatlabEngine
+    NAMES MatlabEngine
     PATHS ${_matlab_lib_dir_for_search}
     DOC "MatlabEngine Library"
     NO_DEFAULT_PATH
@@ -1890,7 +1825,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_DATAARRAY_LIBRARY
-    MatlabDataArray
+    NAMES MatlabDataArray
     PATHS ${_matlab_lib_dir_for_search}
     DOC "MatlabDataArray Library"
     NO_DEFAULT_PATH
@@ -1906,7 +1841,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_ENG_LIBRARY
-    eng
+    NAMES eng
     PATHS ${_matlab_lib_dir_for_search}
     NO_DEFAULT_PATH
   )
@@ -1920,7 +1855,7 @@
   _Matlab_find_library(
     ${_matlab_lib_prefix_for_search}
     Matlab_MAT_LIBRARY
-    mat
+    NAMES mat
     PATHS ${_matlab_lib_dir_for_search}
     NO_DEFAULT_PATH
   )
@@ -1933,7 +1868,7 @@
 if("SIMULINK" IN_LIST Matlab_FIND_COMPONENTS)
   find_path(
     Matlab_SIMULINK_INCLUDE_DIR
-    simstruc.h
+    NAMES simstruc.h
     PATHS "${Matlab_ROOT_DIR}/simulink/include"
     NO_DEFAULT_PATH
     )
@@ -1947,7 +1882,7 @@
 if("MAIN_PROGRAM" IN_LIST Matlab_FIND_COMPONENTS)
   find_program(
     Matlab_MAIN_PROGRAM
-    matlab
+    NAMES matlab
     PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin
     DOC "Matlab main program"
     NO_DEFAULT_PATH
@@ -1961,7 +1896,7 @@
 if("MEX_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
   find_program(
     Matlab_MEX_COMPILER
-    "mex"
+    NAMES "mex"
     PATHS ${Matlab_BINARIES_DIR}
     DOC "Matlab MEX compiler"
     NO_DEFAULT_PATH
@@ -1975,7 +1910,7 @@
 if("MCC_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
   find_program(
     Matlab_MCC_COMPILER
-    "mcc"
+    NAMES "mcc"
     PATHS ${Matlab_BINARIES_DIR}
     DOC "Matlab MCC compiler"
     NO_DEFAULT_PATH
@@ -2030,6 +1965,7 @@
   FOUND_VAR Matlab_FOUND
   REQUIRED_VARS ${_matlab_required_variables}
   VERSION_VAR Matlab_VERSION
+  HANDLE_VERSION_RANGE
   HANDLE_COMPONENTS)
 
 unset(_matlab_required_variables)
diff --git a/Modules/FindOpenACC.cmake b/Modules/FindOpenACC.cmake
index 436f5ea..e9ca683 100644
--- a/Modules/FindOpenACC.cmake
+++ b/Modules/FindOpenACC.cmake
@@ -147,6 +147,7 @@
   set(ACC_FLAG_PGI "-acc")
   set(ACC_FLAG_GNU "-fopenacc")
   set(ACC_FLAG_Cray "-h acc")
+  set(ACC_FLAG_Clang "-fopenacc")
 
   if(DEFINED ACC_FLAG_${CMAKE_${LANG}_COMPILER_ID})
     set("${FLAG_VAR}" "${ACC_FLAG_${CMAKE_${LANG}_COMPILER_ID}}" PARENT_SCOPE)
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake
index 1527c31..0c77cfc 100644
--- a/Modules/FindOpenGL.cmake
+++ b/Modules/FindOpenGL.cmake
@@ -92,12 +92,17 @@
  Defined if the system has GLES3.
 ``OPENGL_INCLUDE_DIR``
  Path to the OpenGL include directory.
+ The ``OPENGL_INCLUDE_DIRS`` variable is preferred.
 ``OPENGL_EGL_INCLUDE_DIRS``
  Path to the EGL include directory.
 ``OPENGL_LIBRARIES``
  Paths to the OpenGL library, windowing system libraries, and GLU libraries.
  On Linux, this assumes GLX and is never correct for EGL-based targets.
  Clients are encouraged to use the ``OpenGL::*`` import targets instead.
+``OPENGL_INCLUDE_DIRS``
+  .. versionadded:: 3.29
+
+  Paths to the OpenGL include directories.
 
 .. versionadded:: 3.10
   Variables for GLVND-specific libraries ``OpenGL``, ``EGL`` and ``GLX``.
@@ -127,6 +132,11 @@
 
   Path to the OpenGL GLES3 library.
 
+``OPENGL_GLU_INCLUDE_DIR``
+  .. versionadded:: 3.29
+
+  Path to the OpenGL GLU include directory.
+
 .. versionadded:: 3.10
   Variables for GLVND-specific libraries ``OpenGL``, ``EGL`` and ``GLX``.
 
@@ -177,9 +187,24 @@
 macOS-Specific
 ^^^^^^^^^^^^^^
 
-On OSX FindOpenGL defaults to using the framework version of OpenGL. People
-will have to change the cache values of OPENGL_glu_LIBRARY and
-OPENGL_gl_LIBRARY to use OpenGL with X11 on OSX.
+On macOS this module defaults to using the macOS-native framework
+version of OpenGL.  To use the X11 version of OpenGL on macOS, one
+can disable searching of frameworks.  For example:
+
+.. code-block:: cmake
+
+  find_package(X11)
+  if(APPLE AND X11_FOUND)
+    set(CMAKE_FIND_FRAMEWORK NEVER)
+    find_package(OpenGL)
+    unset(CMAKE_FIND_FRAMEWORK)
+  else()
+    find_package(OpenGL)
+  endif()
+
+An end user building this project may need to point CMake at their
+X11 installation, e.g., with ``-DOpenGL_ROOT=/opt/X11``.
+
 #]=======================================================================]
 
 set(_OpenGL_REQUIRED_VARS OPENGL_gl_LIBRARY)
@@ -207,15 +232,21 @@
     OPENGL_glu_LIBRARY
     )
 elseif (APPLE)
-  # The OpenGL.framework provides both gl and glu
-  find_library(OPENGL_gl_LIBRARY OpenGL DOC "OpenGL library for OS X")
-  find_library(OPENGL_glu_LIBRARY OpenGL DOC
-    "GLU library for OS X (usually same as OpenGL library)")
-  find_path(OPENGL_INCLUDE_DIR OpenGL/gl.h DOC "Include for OpenGL on OS X")
+  # The OpenGL.framework provides both gl and glu in OpenGL
+  # XQuartz provides libgl and libglu
+  find_library(OPENGL_gl_LIBRARY NAMES OpenGL GL DOC
+    "OpenGL GL library")
+  find_library(OPENGL_glu_LIBRARY NAMES OpenGL GLU DOC
+    "OpenGL GLU library")
+  find_path(OPENGL_INCLUDE_DIR NAMES OpenGL/gl.h GL/gl.h DOC
+    "Include for OpenGL")
+  find_path(OPENGL_GLU_INCLUDE_DIR NAMES OpenGL/glu.h GL/glu.h DOC
+    "Include for the OpenGL GLU library")
   list(APPEND _OpenGL_REQUIRED_VARS OPENGL_INCLUDE_DIR)
 
   list(APPEND _OpenGL_CACHE_VARS
     OPENGL_INCLUDE_DIR
+    OPENGL_GLU_INCLUDE_DIR
     OPENGL_gl_LIBRARY
     OPENGL_glu_LIBRARY
     )
@@ -271,6 +302,8 @@
     /opt/graphics/OpenGL/include
   )
 
+  find_path(OPENGL_GLU_INCLUDE_DIR GL/glu.h ${_OPENGL_INCLUDE_PATH})
+
   list(APPEND _OpenGL_CACHE_VARS
     OPENGL_INCLUDE_DIR
     OPENGL_GLX_INCLUDE_DIR
@@ -278,6 +311,7 @@
     OPENGL_GLES2_INCLUDE_DIR
     OPENGL_GLES3_INCLUDE_DIR
     OPENGL_xmesa_INCLUDE_DIR
+    OPENGL_GLU_INCLUDE_DIR
     )
 
   # Search for the GLVND libraries.  We do this regardless of COMPONENTS; we'll
@@ -491,7 +525,7 @@
   set( OPENGL_XMESA_FOUND "NO" )
 endif()
 
-if(OPENGL_glu_LIBRARY)
+if(OPENGL_glu_LIBRARY AND (WIN32 OR OPENGL_GLU_INCLUDE_DIR))
   set( OPENGL_GLU_FOUND "YES" )
 else()
   set( OPENGL_GLU_FOUND "NO" )
@@ -549,6 +583,8 @@
 
 # OpenGL:: targets
 if(OPENGL_FOUND)
+  set(OPENGL_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR})
+
   # ::OpenGL is a GLVND library, and thus Linux-only: we don't bother checking
   # for a framework version of this library.
   if(OPENGL_opengl_LIBRARY AND NOT TARGET OpenGL::OpenGL)
@@ -582,6 +618,7 @@
                           OpenGL::OpenGL)
     set_target_properties(OpenGL::GLX PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
                           "${OPENGL_GLX_INCLUDE_DIR}")
+    list(APPEND OPENGL_INCLUDE_DIRS ${OPENGL_GLX_INCLUDE_DIR})
   endif()
 
   # ::GLES2 is a GLVND library, and thus Linux-only: we don't bother checking
@@ -611,6 +648,7 @@
         INTERFACE_INCLUDE_DIRECTORIES
           "${OPENGL_GLES2_INCLUDE_DIR}"
     )
+    list(APPEND OPENGL_INCLUDE_DIRS ${OPENGL_GLES2_INCLUDE_DIR})
 
     if (OPENGL_USE_GLES2)
       set(_OpenGL_EGL_IMPL OpenGL::GLES2)
@@ -644,6 +682,7 @@
       INTERFACE_INCLUDE_DIRECTORIES
         "${OPENGL_GLES3_INCLUDE_DIR}"
     )
+    list(APPEND OPENGL_INCLUDE_DIRS ${OPENGL_GLES3_INCLUDE_DIR})
 
     if (OPENGL_USE_GLES3)
       set(_OpenGL_EGL_IMPL OpenGL::GLES3)
@@ -695,6 +734,7 @@
     # Note that EGL's include directory is different from OpenGL/GLX's!
     set_target_properties(OpenGL::EGL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
                           "${OPENGL_EGL_INCLUDE_DIR}")
+    list(APPEND OPENGL_INCLUDE_DIRS ${OPENGL_EGL_INCLUDE_DIR})
   endif()
 
   if(OPENGL_GLU_FOUND AND NOT TARGET OpenGL::GLU)
@@ -709,6 +749,10 @@
     endif()
     set_target_properties(OpenGL::GLU PROPERTIES
       INTERFACE_LINK_LIBRARIES OpenGL::GL)
+    # Note that GLU's include directory may be different from OpenGL's!
+    set_target_properties(OpenGL::GLU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
+                          "${OPENGL_GLU_INCLUDE_DIR}")
+    list(APPEND OPENGL_INCLUDE_DIRS ${OPENGL_GLU_INCLUDE_DIR})
   endif()
 
   # OPENGL_LIBRARIES mirrors OpenGL::GL's logic ...
@@ -725,6 +769,8 @@
   endif()
 endif()
 
+list(REMOVE_DUPLICATES OPENGL_INCLUDE_DIRS)
+
 # This deprecated setting is for backward compatibility with CMake1.4
 set(OPENGL_LIBRARY ${OPENGL_LIBRARIES})
 # This deprecated setting is for backward compatibility with CMake1.4
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake
index 69099f7..f26b864 100644
--- a/Modules/FindOpenMP.cmake
+++ b/Modules/FindOpenMP.cmake
@@ -224,7 +224,8 @@
       OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
     )
 
-    if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG})
+    if(OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} AND
+       NOT "x${CMAKE_${LANG}_SIMULATE_ID}" STREQUAL "xMSVC")
       set("${OPENMP_FLAG_VAR}" "${OPENMP_FLAG}" PARENT_SCOPE)
 
       if(CMAKE_${LANG}_VERBOSE_FLAG)
diff --git a/Modules/FindOpenThreads.cmake b/Modules/FindOpenThreads.cmake
index bc45eea..6be3422 100644
--- a/Modules/FindOpenThreads.cmake
+++ b/Modules/FindOpenThreads.cmake
@@ -5,26 +5,32 @@
 FindOpenThreads
 ---------------
 
-
-
 OpenThreads is a C++ based threading library.  Its largest userbase
 seems to OpenSceneGraph so you might notice I accept OSGDIR as an
-environment path.  I consider this part of the Findosg* suite used to
+environment path.  I consider this part of the ``Findosg*`` suite used to
 find OpenSceneGraph components.  Each component is separate and you
 must opt in to each module.
 
-Locate OpenThreads This module defines OPENTHREADS_LIBRARY
-OPENTHREADS_FOUND, if false, do not try to link to OpenThreads
-OPENTHREADS_INCLUDE_DIR, where to find the headers
+This module defines:
 
-$OPENTHREADS_DIR is an environment variable that would correspond to
-the ./configure --prefix=$OPENTHREADS_DIR used in building osg.
+``OPENTHREADS_LIBRARY``
 
-[CMake 2.8.10]: The CMake variables OPENTHREADS_DIR or OSG_DIR can now
-be used as well to influence detection, instead of needing to specify
-an environment variable.
+``OPENTHREADS_FOUND``
+  if false, do not try to link to OpenThreads
+``OPENTHREADS_INCLUDE_DIR``
+  where to find the headers
 
-Created by Eric Wing.
+``$OPENTHREADS_DIR`` is an environment variable that would correspond to the::
+
+  ./configure --prefix=$OPENTHREADS_DIR
+
+used in building osg.
+
+.. versionadded:: 2.8.10
+
+  The CMake variables ``OPENTHREADS_DIR`` or ``OSG_DIR`` can now
+  be used as well to influence detection, instead of needing to specify
+  an environment variable.
 #]=======================================================================]
 
 # Header files are presumed to be included like
diff --git a/Modules/FindPackageHandleStandardArgs.cmake b/Modules/FindPackageHandleStandardArgs.cmake
index 56ba1e6..63b2bf2 100644
--- a/Modules/FindPackageHandleStandardArgs.cmake
+++ b/Modules/FindPackageHandleStandardArgs.cmake
@@ -342,7 +342,7 @@
           set(version_msg "Found unsuitable version \"${version}\", but required is exact version \"${${package}_FIND_VERSION}\"")
         else ()
           set(version_ok TRUE)
-          set(version_msg "(found suitable exact version \"${_FOUND_VERSION}\")")
+          set(version_msg "(found suitable exact version \"${version}\")")
         endif ()
       else ()
         if (NOT ${package}_FIND_VERSION VERSION_EQUAL version)
diff --git a/Modules/FindPackageMessage.cmake b/Modules/FindPackageMessage.cmake
index 0628b98..7efbe18 100644
--- a/Modules/FindPackageMessage.cmake
+++ b/Modules/FindPackageMessage.cmake
@@ -37,6 +37,7 @@
     set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg})
     if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}")
       # The message has not yet been printed.
+      string(STRIP "${msg}" msg)
       message(STATUS "${msg}")
 
       # Save the find details in the cache to avoid printing the same
diff --git a/Modules/FindPerl.cmake b/Modules/FindPerl.cmake
index 26962df..49bc54c 100644
--- a/Modules/FindPerl.cmake
+++ b/Modules/FindPerl.cmake
@@ -5,15 +5,20 @@
 FindPerl
 --------
 
-Find perl
+Find a Perl interpreter.
 
-this module looks for Perl
+This module defines the following variables:
 
-::
+``PERL_EXECUTABLE``
+  The full path to Perl.
 
-  PERL_EXECUTABLE     - the full path to perl
-  PERL_FOUND          - If false, don't attempt to use perl.
-  PERL_VERSION_STRING - version of perl found (since CMake 2.8.8)
+``PERL_FOUND``
+  True if the Perl executable was found.
+
+``PERL_VERSION_STRING``
+  .. versionadded:: 2.8.8
+
+  The version of Perl found.
 #]=======================================================================]
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindCygwin.cmake)
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index 27d25fb..1b6a72a 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -656,6 +656,9 @@
 
       if (APPLE AND "-framework" IN_LIST ${_prefix}_LDFLAGS_OTHER)
         _pkgconfig_extract_frameworks("${_prefix}")
+        # Using _pkgconfig_set in this scope so that a future policy can switch to normal variables
+        _pkgconfig_set("${_pkg_check_prefix}_LIBRARIES" "${${_pkg_check_prefix}_LIBRARIES}")
+        _pkgconfig_set("${_pkg_check_prefix}_LDFLAGS_OTHER" "${${_pkg_check_prefix}_LDFLAGS_OTHER}")
       endif()
 
       _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS  "(^| )(-I|-isystem ?)" --cflags-only-I )
@@ -664,6 +667,9 @@
 
       if (${_prefix}_CFLAGS_OTHER MATCHES "-isystem")
         _pkgconfig_extract_isystem("${_prefix}")
+        # Using _pkgconfig_set in this scope so that a future policy can switch to normal variables
+        _pkgconfig_set("${_pkg_check_prefix}_CFLAGS_OTHER" "${${_pkg_check_prefix}_CFLAGS_OTHER}")
+        _pkgconfig_set("${_pkg_check_prefix}_INCLUDE_DIRS" "${${_pkg_check_prefix}_INCLUDE_DIRS}")
       endif ()
 
       _pkg_recalculate("${_prefix}" ${_no_cmake_path} ${_no_cmake_environment_path} ${_imp_target} ${_imp_target_global})
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake
index 581763d..0f45b63 100644
--- a/Modules/FindVulkan.cmake
+++ b/Modules/FindVulkan.cmake
@@ -537,13 +537,7 @@
 _Vulkan_set_library_component_found(glslang-osdependent NO_WARNING)
 _Vulkan_set_library_component_found(glslang-machineindependent NO_WARNING)
 _Vulkan_set_library_component_found(glslang-genericcodegen NO_WARNING)
-_Vulkan_set_library_component_found(glslang
-  DEPENDENT_COMPONENTS
-    glslang-spirv
-    glslang-oglcompiler
-    glslang-osdependent
-    glslang-machineindependent
-    glslang-genericcodegen)
+_Vulkan_set_library_component_found(glslang DEPENDENT_COMPONENTS glslang-spirv)
 _Vulkan_set_library_component_found(shaderc_combined)
 _Vulkan_set_library_component_found(SPIRV-Tools)
 _Vulkan_set_library_component_found(volk)
@@ -747,10 +741,6 @@
 
   if((Vulkan_glslang_LIBRARY OR Vulkan_glslang_DEBUG_LIBRARY)
       AND TARGET Vulkan::glslang-spirv
-      AND TARGET Vulkan::glslang-oglcompiler
-      AND TARGET Vulkan::glslang-osdependent
-      AND TARGET Vulkan::glslang-machineindependent
-      AND TARGET Vulkan::glslang-genericcodegen
       AND NOT TARGET Vulkan::glslang)
     add_library(Vulkan::glslang STATIC IMPORTED)
     set_property(TARGET Vulkan::glslang
@@ -775,10 +765,13 @@
     target_link_libraries(Vulkan::glslang
       INTERFACE
         Vulkan::glslang-spirv
-        Vulkan::glslang-oglcompiler
-        Vulkan::glslang-osdependent
-        Vulkan::glslang-machineindependent
-        Vulkan::glslang-genericcodegen
+        # OGLCompiler library has been fully removed since version 14.0.0
+        # OSDependent, MachineIndependent, and GenericCodeGen may also be removed in the future.
+        # See https://github.com/KhronosGroup/glslang/issues/3462
+        $<TARGET_NAME_IF_EXISTS:Vulkan::glslang-oglcompiler>
+        $<TARGET_NAME_IF_EXISTS:Vulkan::glslang-osdependent>
+        $<TARGET_NAME_IF_EXISTS:Vulkan::glslang-machineindependent>
+        $<TARGET_NAME_IF_EXISTS:Vulkan::glslang-genericcodegen>
     )
   endif()
 
diff --git a/Modules/FindX11.cmake b/Modules/FindX11.cmake
index 1047e4f..491ea46 100644
--- a/Modules/FindX11.cmake
+++ b/Modules/FindX11.cmake
@@ -89,6 +89,7 @@
   X11_Xutil_INCLUDE_PATH,                                  X11_Xutil_FOUND,          X11::Xutil
   X11_Xv_INCLUDE_PATH,             X11_Xv_LIB,             X11_Xv_FOUND,             X11::Xv
   X11_dpms_INCLUDE_PATH,           (in X11_Xext_LIB),      X11_dpms_FOUND
+  X11_Xdbe_INCLUDE_PATH,           (in X11_Xext_LIB),      X11_Xdbe_FOUND
   X11_XShm_INCLUDE_PATH,           (in X11_Xext_LIB),      X11_XShm_FOUND
   X11_Xshape_INCLUDE_PATH,         (in X11_Xext_LIB),      X11_Xshape_FOUND
   X11_XSync_INCLUDE_PATH,          (in X11_Xext_LIB),      X11_XSync_FOUND
@@ -121,6 +122,10 @@
   ``xcb_shm``, ``xcb_sync``, ``xcb_xf86dri``, ``xcb_xinerama``, ``xcb_xinput``,
   ``xcb_xrm``, ``xcb_xvmc``, and ``xcb_xv`` libraries.
 
+.. versionadded:: 3.29
+  Added coverage of double buffer extension (variables
+  ``X11_Xdbe_INCLUDE_PATH`` and ``X11_Xdbe_FOUND``).
+
 #]=======================================================================]
 
 if (UNIX)
@@ -200,6 +205,7 @@
   find_path(X11_Xcomposite_INCLUDE_PATH X11/extensions/Xcomposite.h  ${X11_INC_SEARCH_PATH})
   find_path(X11_Xcursor_INCLUDE_PATH X11/Xcursor/Xcursor.h           ${X11_INC_SEARCH_PATH})
   find_path(X11_Xdamage_INCLUDE_PATH X11/extensions/Xdamage.h        ${X11_INC_SEARCH_PATH})
+  find_path(X11_Xdbe_INCLUDE_PATH X11/extensions/Xdbe.h              ${X11_INC_SEARCH_PATH})
   find_path(X11_Xdmcp_INCLUDE_PATH X11/Xdmcp.h                       ${X11_INC_SEARCH_PATH})
   find_path(X11_Xext_INCLUDE_PATH X11/extensions/Xext.h              ${X11_INC_SEARCH_PATH})
   find_path(X11_dpms_INCLUDE_PATH X11/extensions/dpms.h              ${X11_INC_SEARCH_PATH})
@@ -643,6 +649,11 @@
       set(X11_Xaw_FOUND TRUE)
   endif()
 
+  if (X11_Xdbe_INCLUDE_PATH)
+     set(X11_Xdbe_FOUND TRUE)
+     list(APPEND X11_INCLUDE_DIR ${X11_Xdbe_INCLUDE_PATH})
+  endif ()
+
   # Most of the X11 headers will be in the same directories, avoid
   # creating a huge list of duplicates.
   if (X11_INCLUDE_DIR)
@@ -1358,6 +1369,7 @@
     X11_XSync_INCLUDE_PATH
     X11_Xaw_LIB
     X11_Xaw_INCLUDE_PATH
+    X11_Xdbe_INCLUDE_PATH
   )
   set(CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_SAVE})
   set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
diff --git a/Modules/FindwxWidgets.cmake b/Modules/FindwxWidgets.cmake
index 78fa481..b42a85e 100644
--- a/Modules/FindwxWidgets.cmake
+++ b/Modules/FindwxWidgets.cmake
@@ -37,7 +37,7 @@
   wxWidgets_EXCLUDE_COMMON_LIBRARIES
                           - Set to TRUE to exclude linking of
                             commonly required libs (e.g., png tiff
-                            jpeg zlib regex expat).
+                            jpeg zlib regex expat scintilla lexilla).
 
 
 
@@ -188,6 +188,9 @@
 #    "${CMAKE_CURRENT_LIST_FILE}(${CMAKE_CURRENT_LIST_LINE}): ${_MSG}")
 endmacro()
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0057 NEW) # if IN_LIST
+
 # Clear return values in case the module is loaded more than once.
 set(wxWidgets_FOUND FALSE)
 set(wxWidgets_INCLUDE_DIRS "")
@@ -244,8 +247,14 @@
     "\\2" wxWidgets_VERSION_MINOR "${_wx_version_h}" )
   string(REGEX REPLACE "^(.*\n)?#define +wxRELEASE_NUMBER +([0-9]+).*"
     "\\2" wxWidgets_VERSION_PATCH "${_wx_version_h}" )
+  string(REGEX REPLACE "^(.*\n)?#define +wxSUBRELEASE_NUMBER +([0-9]+).*"
+    "\\2" wxWidgets_VERSION_TWEAK "${_wx_version_h}" )
+
   set(wxWidgets_VERSION_STRING
     "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}.${wxWidgets_VERSION_PATCH}" )
+  if(${wxWidgets_VERSION_TWEAK} GREATER 0)
+    string(APPEND wxWidgets_VERSION_STRING ".${wxWidgets_VERSION_TWEAK}")
+  endif()
   dbg_msg("wxWidgets_VERSION_STRING:    ${wxWidgets_VERSION_STRING}")
 endmacro()
 
@@ -265,6 +274,9 @@
   # Useful common wx libs needed by almost all components.
   set(wxWidgets_COMMON_LIBRARIES png tiff jpeg zlib regex expat)
 
+  # Libraries needed by stc component
+  set(wxWidgets_STC_LIBRARIES scintilla lexilla)
+
   # DEPRECATED: Use find_package(wxWidgets COMPONENTS mono) instead.
   if(NOT wxWidgets_FIND_COMPONENTS)
     if(wxWidgets_USE_MONOLITHIC)
@@ -277,10 +289,15 @@
   # Add the common (usually required libs) unless
   # wxWidgets_EXCLUDE_COMMON_LIBRARIES has been set.
   if(NOT wxWidgets_EXCLUDE_COMMON_LIBRARIES)
-    list(APPEND wxWidgets_FIND_COMPONENTS
-      ${wxWidgets_COMMON_LIBRARIES})
+    if(stc IN_LIST wxWidgets_FIND_COMPONENTS)
+      list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_STC_LIBRARIES})
+    endif()
+    list(APPEND wxWidgets_FIND_COMPONENTS ${wxWidgets_COMMON_LIBRARIES})
   endif()
 
+  # Remove duplicates, for example when user has specified common libraries.
+  list(REMOVE_DUPLICATES wxWidgets_FIND_COMPONENTS)
+
   #-------------------------------------------------------------------
   # WIN32: Helper MACROS
   #-------------------------------------------------------------------
@@ -312,7 +329,7 @@
     # FIXME: What if both regex libs are available. regex should be
     # found outside the loop and only wx${LIB}${_UCD}${_DBG}.
     # Find wxWidgets common libraries.
-    foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla)
+    foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES})
       find_library(WX_${LIB}${_DBG}
         NAMES
         wx${LIB}${_UCD}${_DBG} # for regex
@@ -371,7 +388,7 @@
   # Clear all debug or release library paths (arguments are "d" or "").
   macro(WX_CLEAR_ALL_LIBS _DBG)
     # Clear wxWidgets common libraries.
-    foreach(LIB ${wxWidgets_COMMON_LIBRARIES} scintilla)
+    foreach(LIB ${wxWidgets_COMMON_LIBRARIES} ${wxWidgets_STC_LIBRARIES})
       WX_CLEAR_LIB(WX_${LIB}${_DBG})
     endforeach()
 
@@ -441,12 +458,15 @@
     endif()
 
     DBG_MSG_V("OpenGL")
-    list(FIND ${_LIBS} gl WX_USE_GL)
-    if(NOT WX_USE_GL EQUAL -1)
+    if(gl IN_LIST ${_LIBS})
       DBG_MSG_V("- is required.")
       list(APPEND wxWidgets_LIBRARIES opengl32 glu32)
     endif()
 
+    if(stc IN_LIST ${_LIBS})
+      list(APPEND wxWidgets_LIBRARIES imm32)
+    endif()
+
     list(APPEND wxWidgets_LIBRARIES winmm comctl32 uuid oleacc uxtheme rpcrt4 shlwapi version wsock32)
   endmacro()
 
@@ -458,6 +478,9 @@
   foreach(version ${wx_versions})
     foreach(patch RANGE 15 0 -1)
       list(APPEND wx_paths "wxWidgets-${version}.${patch}")
+      foreach(tweak RANGE 3 1 -1)
+        list(APPEND wx_paths "wxWidgets-${version}.${patch}.${tweak}")
+      endforeach()
     endforeach()
   endforeach()
 
@@ -937,35 +960,38 @@
       endif()
       unset(_cygpath_exe CACHE)
     endif()
-endif()
 
-# Check that all libraries are present, as wx-config does not check it
-set(_wx_lib_missing "")
-foreach(_wx_lib_ ${wxWidgets_LIBRARIES})
-  if("${_wx_lib_}" MATCHES "^-l(.*)")
-    set(_wx_lib_name "${CMAKE_MATCH_1}")
-    unset(_wx_lib_found CACHE)
-    find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS})
-    if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND)
-      list(APPEND _wx_lib_missing ${_wx_lib_name})
+    # Check that all libraries are present, as wx-config does not check it
+    set(_wx_lib_missing "")
+    foreach(_wx_lib_ ${wxWidgets_LIBRARIES})
+      if("${_wx_lib_}" MATCHES "^-l(.*)")
+        set(_wx_lib_name "${CMAKE_MATCH_1}")
+        unset(_wx_lib_found CACHE)
+        find_library(_wx_lib_found NAMES ${_wx_lib_name} HINTS ${wxWidgets_LIBRARY_DIRS})
+        if(_wx_lib_found STREQUAL _wx_lib_found-NOTFOUND)
+          list(APPEND _wx_lib_missing ${_wx_lib_name})
+        endif()
+        unset(_wx_lib_found CACHE)
+      endif()
+    endforeach()
+
+    if (_wx_lib_missing)
+      string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}")
+      DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}")
+      set(wxWidgets_FOUND FALSE)
+      unset(wxWidgets_LIBRARIES)
     endif()
-    unset(_wx_lib_found CACHE)
-  endif()
-endforeach()
-
-if (_wx_lib_missing)
-  string(REPLACE ";" " " _wx_lib_missing "${_wx_lib_missing}")
-  DBG_MSG_V("wxWidgets not found due to following missing libraries: ${_wx_lib_missing}")
-  set(wxWidgets_FOUND FALSE)
-  unset(wxWidgets_LIBRARIES)
+    unset(_wx_lib_missing)
 endif()
-unset(_wx_lib_missing)
 
 # Check if a specific version was requested by find_package().
 if(wxWidgets_FOUND)
   wx_extract_version()
 endif()
 
+file(TO_CMAKE_PATH "${wxWidgets_INCLUDE_DIRS}" wxWidgets_INCLUDE_DIRS)
+file(TO_CMAKE_PATH "${wxWidgets_LIBRARY_DIRS}" wxWidgets_LIBRARY_DIRS)
+
 # Debug output:
 DBG_MSG("wxWidgets_FOUND           : ${wxWidgets_FOUND}")
 DBG_MSG("wxWidgets_INCLUDE_DIRS    : ${wxWidgets_INCLUDE_DIRS}")
@@ -1215,3 +1241,5 @@
 
   set(${_outfiles} ${${_outfiles}} PARENT_SCOPE)
 endfunction()
+
+cmake_policy(POP)
diff --git a/Modules/FortranCInterface/Detect.cmake b/Modules/FortranCInterface/Detect.cmake
index 010661e..d65c865 100644
--- a/Modules/FortranCInterface/Detect.cmake
+++ b/Modules/FortranCInterface/Detect.cmake
@@ -10,7 +10,7 @@
     OR NOT EXISTS ${FortranCInterface_BINARY_DIR}/Input.cmake
     OR NOT ${FortranCInterface_BINARY_DIR}/Output.cmake
       IS_NEWER_THAN ${FortranCInterface_BINARY_DIR}/Input.cmake
-    OR NOT ${FortranCInterface_SOURCE_DIR}/Output.cmake
+    OR NOT ${FortranCInterface_BINARY_DIR}/Output.cmake
       IS_NEWER_THAN ${FortranCInterface_SOURCE_DIR}/Output.cmake.in
     OR NOT ${FortranCInterface_BINARY_DIR}/Output.cmake
       IS_NEWER_THAN ${FortranCInterface_SOURCE_DIR}/CMakeLists.txt
@@ -177,7 +177,6 @@
 # Record the detection results.
 configure_file(${FortranCInterface_SOURCE_DIR}/Output.cmake.in
                ${FortranCInterface_BINARY_DIR}/Output.cmake @ONLY)
-file(APPEND ${FortranCInterface_BINARY_DIR}/Output.cmake "\n")
 
 # Report the results.
 if(FortranCInterface_GLOBAL_FOUND)
diff --git a/Modules/GNUInstallDirs.cmake b/Modules/GNUInstallDirs.cmake
index 9796854..ed34c4a 100644
--- a/Modules/GNUInstallDirs.cmake
+++ b/Modules/GNUInstallDirs.cmake
@@ -20,11 +20,16 @@
 ``CMAKE_INSTALL_<dir>``
 
   Destination for files of a given type.  This value may be passed to
-  the ``DESTINATION`` options of :command:`install` commands for the
-  corresponding file type.  It should typically be a path relative to
-  the installation prefix so that it can be converted to an absolute
-  path in a relocatable way (see ``CMAKE_INSTALL_FULL_<dir>``).
-  However, an absolute path is also allowed.
+  the ``DESTINATION`` options of  :command:`install` commands for the
+  corresponding file type.  It should be a path relative to the installation
+  prefix so that it can be converted to an absolute path in a relocatable way.
+
+  While absolute paths are allowed, they are not recommended as they
+  do not work with the ``cmake --install`` command's
+  :option:`--prefix <cmake--install --prefix>` option, or with the
+  :manual:`cpack <cpack(1)>` installer generators. In particular, there is no
+  need to make paths absolute by prepending :variable:`CMAKE_INSTALL_PREFIX`;
+  this prefix is used by default if the DESTINATION is a relative path.
 
 ``CMAKE_INSTALL_FULL_<dir>``
 
@@ -34,6 +39,11 @@
   :variable:`CMAKE_INSTALL_PREFIX` variable.  However, there are some
   `special cases`_ as documented below.
 
+  These variables shouldn't be used in :command:`install` commands
+  as they do not work with the ``cmake --install`` command's
+  :option:`--prefix <cmake--install --prefix>` option, or with the
+  :manual:`cpack <cpack(1)>` installer generators.
+
 where ``<dir>`` is one of:
 
 ``BINDIR``
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index 0ba35b6..d55c4ca 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -514,7 +514,7 @@
     string(TOLOWER "${resolved_file}" lower)
 
     if(UNIX)
-      if(resolved_file MATCHES "^(/lib/|/lib32/|/libx32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/libx32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
+      if(resolved_file MATCHES "^/*(/lib/|/lib32/|/libx32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/libx32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
         set(is_system 1)
       endif()
     endif()
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake
index 57a7476..b62f839 100644
--- a/Modules/GoogleTest.cmake
+++ b/Modules/GoogleTest.cmake
@@ -475,10 +475,29 @@
   set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}[${counter}]")
   set(ctest_include_file "${ctest_file_base}_include.cmake")
   set(ctest_tests_file "${ctest_file_base}_tests.cmake")
-  get_property(crosscompiling_emulator
+  get_property(test_launcher
     TARGET ${TARGET}
-    PROPERTY CROSSCOMPILING_EMULATOR
+    PROPERTY TEST_LAUNCHER
   )
+  cmake_policy(GET CMP0158 _CMP0158
+    PARENT_SCOPE # undocumented, do not use outside of CMake
+  )
+  if(NOT _CMP0158 OR _CMP0158 STREQUAL "OLD" OR _CMP0158 STREQUAL "NEW" AND CMAKE_CROSSCOMPILING)
+    get_property(crosscompiling_emulator
+      TARGET ${TARGET}
+      PROPERTY CROSSCOMPILING_EMULATOR
+    )
+  endif()
+
+  if(test_launcher AND crosscompiling_emulator)
+    set(test_executor "${test_launcher}" "${crosscompiling_emulator}")
+  elseif(test_launcher)
+    set(test_executor "${test_launcher}")
+  elseif(crosscompiling_emulator)
+    set(test_executor "${crosscompiling_emulator}")
+  else()
+    set(test_executor "")
+  endif()
 
   if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
     add_custom_command(
@@ -487,7 +506,7 @@
       COMMAND "${CMAKE_COMMAND}"
               -D "TEST_TARGET=${TARGET}"
               -D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-              -D "TEST_EXECUTOR=${crosscompiling_emulator}"
+              -D "TEST_EXECUTOR=${test_executor}"
               -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
               -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
               -D "TEST_PROPERTIES=${_PROPERTIES}"
@@ -500,7 +519,7 @@
               -D "CTEST_FILE=${ctest_tests_file}"
               -D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
               -D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
-              -P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
+              -P "${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake"
       VERBATIM
     )
 
@@ -526,10 +545,10 @@
       "  if(NOT EXISTS \"${ctest_tests_file}\" OR"                                 "\n"
       "     NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
       "     NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
-      "    include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")"                      "\n"
+      "    include(\"${CMAKE_ROOT}/Modules/GoogleTestAddTests.cmake\")"            "\n"
       "    gtest_discover_tests_impl("                                             "\n"
       "      TEST_EXECUTABLE"        " [==[" "$<TARGET_FILE:${TARGET}>"   "]==]"   "\n"
-      "      TEST_EXECUTOR"          " [==[" "${crosscompiling_emulator}" "]==]"   "\n"
+      "      TEST_EXECUTOR"          " [==[" "${test_executor}"           "]==]"   "\n"
       "      TEST_WORKING_DIR"       " [==[" "${_WORKING_DIRECTORY}"      "]==]"   "\n"
       "      TEST_EXTRA_ARGS"        " [==[" "${_EXTRA_ARGS}"             "]==]"   "\n"
       "      TEST_PROPERTIES"        " [==[" "${_PROPERTIES}"             "]==]"   "\n"
@@ -573,9 +592,5 @@
 
 ###############################################################################
 
-set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
-  ${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake
-)
-
 # Restore project's policies
 cmake_policy(POP)
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake
index de0f7d6..eea267d 100644
--- a/Modules/GoogleTestAddTests.cmake
+++ b/Modules/GoogleTestAddTests.cmake
@@ -112,6 +112,7 @@
     message(FATAL_ERROR
       "Error running test executable.\n"
       "  Path: '${path}'\n"
+      "  Working directory: '${_TEST_WORKING_DIR}'\n"
       "  Result: ${result}\n"
       "  Output:\n"
       "    ${output}\n"
diff --git a/Modules/Internal/AppleArchitectureSelection.cmake.in b/Modules/Internal/AppleArchitectureSelection.cmake.in
new file mode 100644
index 0000000..fec0d41
--- /dev/null
+++ b/Modules/Internal/AppleArchitectureSelection.cmake.in
@@ -0,0 +1,8 @@
+@PACKAGE_INIT@
+
+if(NOT CMAKE_OSX_ARCHITECTURES)
+  message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES must be explicitly set for this package")
+endif()
+@_branch_code@
+
+message(FATAL_ERROR "Architecture not supported")
diff --git a/Modules/Internal/ApplePlatformSelection.cmake.in b/Modules/Internal/ApplePlatformSelection.cmake.in
new file mode 100644
index 0000000..5f5e01d
--- /dev/null
+++ b/Modules/Internal/ApplePlatformSelection.cmake.in
@@ -0,0 +1,24 @@
+@PACKAGE_INIT@
+
+string(TOLOWER "${CMAKE_OSX_SYSROOT}" _CMAKE_OSX_SYSROOT_LOWER)
+if(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)iphonesimulator")
+  @_branch_IOS_SIMULATOR_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)iphoneos")
+  @_branch_IOS_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)appletvsimulator")
+  @_branch_TVOS_SIMULATOR_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)appletvos")
+  @_branch_TVOS_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)watchsimulator")
+  @_branch_WATCHOS_SIMULATOR_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)watchos")
+  @_branch_WATCHOS_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xrsimulator")
+  @_branch_VISIONOS_SIMULATOR_INCLUDE_FILE@
+elseif(_CMAKE_OSX_SYSROOT_LOWER MATCHES "(^|/)xros")
+  @_branch_VISIONOS_INCLUDE_FILE@
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+  @_branch_MACOS_INCLUDE_FILE@
+else()
+  message(FATAL_ERROR "Platform not supported")
+endif()
diff --git a/Modules/Internal/CMakeDetermineLinkerId.cmake b/Modules/Internal/CMakeDetermineLinkerId.cmake
new file mode 100644
index 0000000..45499a5
--- /dev/null
+++ b/Modules/Internal/CMakeDetermineLinkerId.cmake
@@ -0,0 +1,107 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Function to identify the linker.  This is used internally by CMake and should
+# not be included by user code.
+# If successful, sets CMAKE_<lang>_COMPILER_LINKER_ID and
+# CMAKE_<lang>_COMPILER_LINKER_VERSION
+
+cmake_policy(PUSH)
+cmake_policy(SET CMP0053 NEW)
+cmake_policy(SET CMP0054 NEW)
+
+function(cmake_determine_linker_id lang linker)
+  if (NOT linker)
+    # linker was not identified
+    unset(CMAKE_${lang}_COMPILER_LINKER_ID PARENT_SCOPE)
+    unset(CMAKE_${lang}_COMPILER_LINKER_VERSION PARENT_SCOPE)
+    unset(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT PARENT_SCOPE)
+    return()
+  endif()
+
+  set(linker_id)
+  set(linker_frontend)
+  set(linker_version)
+
+  # Compute the linker ID and version.
+  foreach(flags IN ITEMS
+      "-v"        # AppleClang, GNU, GNUgold, MOLD
+      "-V"        # AIX, Solaris
+      "--version" # LLD
+      )
+    execute_process(COMMAND "${linker}" ${flags}
+                    OUTPUT_VARIABLE linker_desc
+                    ERROR_VARIABLE linker_desc
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    ERROR_STRIP_TRAILING_WHITESPACE)
+
+    string(JOIN "\" \"" flags_string ${flags})
+    string(REGEX REPLACE "\n\n.*" "" linker_desc_head "${linker_desc}")
+    message(CONFIGURE_LOG
+      "Running the ${lang} compiler's linker: \"${linker}\" \"${flags_string}\"\n"
+      "${linker_desc_head}\n"
+      )
+
+    if(CMAKE_EFFECTIVE_SYSTEM_NAME STREQUAL "Apple" AND linker_desc MATCHES "@\\(#\\)PROGRAM:ld.+PROJECT:[a-z0-9]+-([0-9.]+).+")
+      set(linker_id "AppleClang")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif(linker_desc MATCHES "mold \\(sold\\) ([0-9.]+)")
+      set(linker_id "MOLD")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif(linker_desc MATCHES "mold ([0-9.]+)")
+      set(linker_id "MOLD")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif(linker_desc MATCHES "LLD ([0-9.]+)")
+      set(linker_id "LLD")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_1}")
+      if(WIN32 AND NOT linker_desc MATCHES "compatible with GNU")
+        set(linker_frontend "MSVC")
+      endif()
+      break()
+    elseif(linker_desc MATCHES "GNU ld (\\([^)]+\\)|version) ([0-9.]+)")
+      set(linker_id "GNU")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_2}")
+      break()
+    elseif(linker_desc MATCHES "GNU gold \\([^)]+\\) ([0-9.]+)")
+      set(linker_id "GNUgold")
+      set(linker_frontend "GNU")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif(linker_desc MATCHES "Microsoft \\(R\\) Incremental Linker Version ([0-9.]+)")
+      set(linker_id "MSVC")
+      set(linker_frontend "MSVC")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND linker_desc MATCHES "Solaris Link Editors: ([0-9.-]+)")
+      set(linker_id "Solaris")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    elseif (CMAKE_SYSTEM_NAME STREQUAL "AIX" AND linker_desc MATCHES " LD ([0-9.]+)")
+      set(linker_id "AIX")
+      set(linker_version "${CMAKE_MATCH_1}")
+      break()
+    endif()
+  endforeach()
+
+  set(CMAKE_${lang}_COMPILER_LINKER_ID "${linker_id}" PARENT_SCOPE)
+  if (linker_frontend)
+    set(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT "${linker_frontend}" PARENT_SCOPE)
+  else()
+    unset(CMAKE_${lang}_COMPILER_LINKER_FRONTEND_VARIANT PARENT_SCOPE)
+  endif()
+  if (linker_version)
+    set(CMAKE_${lang}_COMPILER_LINKER_VERSION "${linker_version}" PARENT_SCOPE)
+  else()
+    unset(CMAKE_${lang}_COMPILER_LINKER_VERSION PARENT_SCOPE)
+  endif()
+endfunction()
+
+cmake_policy(POP)
diff --git a/Modules/Internal/CPack/CPack.NuGet.nuspec.in b/Modules/Internal/CPack/CPack.NuGet.nuspec.in
index d89d69f..4548a59 100644
--- a/Modules/Internal/CPack/CPack.NuGet.nuspec.in
+++ b/Modules/Internal/CPack/CPack.NuGet.nuspec.in
@@ -15,12 +15,14 @@
         @_CPACK_NUGET_LICENSE_TAG@
         @_CPACK_NUGET_ICONURL_TAG@
         @_CPACK_NUGET_ICON_TAG@
+        @_CPACK_NUGET_README_TAG@
         @_CPACK_NUGET_REQUIRELICENSEACCEPTANCE_TAG@
         @_CPACK_NUGET_SUMMARY_TAG@
         @_CPACK_NUGET_RELEASENOTES_TAG@
         @_CPACK_NUGET_COPYRIGHT_TAG@
         @_CPACK_NUGET_LANGUAGE_TAG@
         @_CPACK_NUGET_TAGS_TAG@
+        @_CPACK_NUGET_REPOSITORY_TAG@
         @_CPACK_NUGET_DEPENDENCIES_TAG@
     </metadata>
     @_CPACK_NUGET_FILES_TAG@
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 38e32c2..55a621c 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -710,7 +710,7 @@
         "${CPACK_DEBIAN_PACKAGE_NAME}-dbgsym_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.ddeb")
     else()
       if(NOT CPACK_DEBIAN_FILE_NAME MATCHES ".*\\.(deb|ipk)")
-        message(FATAL_ERROR "'${CPACK_DEBIAN_FILE_NAME}' is not a valid DEB package file name as it must end with '.deb' or '.ipk'!")
+        set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
       endif()
 
       set(CPACK_OUTPUT_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}")
diff --git a/Modules/Internal/CPack/CPackNuGet.cmake b/Modules/Internal/CPack/CPackNuGet.cmake
index 056d025..67f318a 100644
--- a/Modules/Internal/CPack/CPackNuGet.cmake
+++ b/Modules/Internal/CPack/CPackNuGet.cmake
@@ -294,6 +294,27 @@
     # attributes: "type", "url", "branch", and "commit". While all fields are
     # considered optional, they are not independent. Currently unsupported.
 
+    # NuGet >= 5.10
+    _cpack_nuget_variable_fallback_and_wrap_into_element(readme README)
+
+    set(_CPACK_NUGET_REPOSITORY_TAG)
+    _cpack_nuget_variable_fallback(_repo_type REPOSITORY_TYPE)
+    _cpack_nuget_variable_fallback(_repo_url REPOSITORY_URL)
+    if(_repo_type AND _repo_url)
+        set(_CPACK_NUGET_REPOSITORY_TAG "<repository type=\"${_repo_type}\" url=\"${_repo_url}\"")
+        _cpack_nuget_variable_fallback(_repo_br REPOSITORY_BRANCH)
+        if(_repo_br)
+            string(APPEND _CPACK_NUGET_REPOSITORY_TAG " branch=\"${_repo_br}\"")
+        endif()
+        _cpack_nuget_variable_fallback(_repo_commit REPOSITORY_COMMIT)
+        if(_repo_commit)
+            string(APPEND _CPACK_NUGET_REPOSITORY_TAG " commit=\"${_repo_commit}\"")
+        endif()
+        string(APPEND _CPACK_NUGET_REPOSITORY_TAG " />")
+    else()
+        message(AUTHOR_WARNING "Skip adding the `<repository .../>` element due to missing URL or type")
+    endif()
+
     # Handle dependencies
     _cpack_nuget_variable_fallback(_deps DEPENDENCIES)
     set(_collected_deps)
diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake
index ace2c6b..5766045 100644
--- a/Modules/Internal/CPack/CPackRPM.cmake
+++ b/Modules/Internal/CPack/CPackRPM.cmake
@@ -1041,7 +1041,11 @@
        set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.lzdio")
      endif()
      if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "xz")
-       set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7.xzdio")
+       if(CPACK_THREADS GREATER "0")
+         set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T${CPACK_THREADS}.xzdio")
+       else()
+         set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w7T.xzdio")
+       endif()
      endif()
      if(CPACK_RPM_COMPRESSION_TYPE STREQUAL "bzip2")
        set(CPACK_RPM_COMPRESSION_TYPE_TMP "%define _binary_payload w9.bzdio")
@@ -1577,7 +1581,7 @@
   if(NOT CPACK_RPM_FILE_NAME STREQUAL "RPM-DEFAULT")
     if(CPACK_RPM_FILE_NAME)
       if(NOT CPACK_RPM_FILE_NAME MATCHES ".*\\.rpm")
-        message(FATAL_ERROR "'${CPACK_RPM_FILE_NAME}' is not a valid RPM package file name as it must end with '.rpm'!")
+        set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
       endif()
     else()
       # old file name format for back compatibility
diff --git a/Modules/Internal/CPack/CPackWIX.cmake b/Modules/Internal/CPack/CPackWIX.cmake
index d1875f2..5fe772e 100644
--- a/Modules/Internal/CPack/CPackWIX.cmake
+++ b/Modules/Internal/CPack/CPackWIX.cmake
@@ -18,3 +18,7 @@
 if(NOT CPACK_WIX_LIGHT_EXECUTABLE)
   message(FATAL_ERROR "Could not find the WiX light executable.")
 endif()
+
+if(NOT DEFINED CPACK_WIX_INSTALL_SCOPE)
+  set(CPACK_WIX_INSTALL_SCOPE "perMachine")
+endif()
diff --git a/Modules/Internal/CPack/WIX.template.in b/Modules/Internal/CPack/WIX.template.in
index c0bf935..95ba7fa 100644
--- a/Modules/Internal/CPack/WIX.template.in
+++ b/Modules/Internal/CPack/WIX.template.in
@@ -12,7 +12,12 @@
         Manufacturer="$(var.CPACK_PACKAGE_VENDOR)"
         UpgradeCode="$(var.CPACK_WIX_UPGRADE_GUID)">
 
+
+        <?if $(var.CPACK_WIX_INSTALL_SCOPE) = "NONE" ?>
         <Package InstallerVersion="301" Compressed="yes"/>
+        <?else?>
+        <Package InstallerVersion="301" Compressed="yes" InstallScope="$(var.CPACK_WIX_INSTALL_SCOPE)"/>
+        <?endif?>
 
         <Media Id="1" Cabinet="media1.cab" EmbedCab="yes"/>
 
@@ -40,6 +45,7 @@
         <FeatureRef Id="ProductFeature"/>
 
         <UIRef Id="$(var.CPACK_WIX_UI_REF)" />
+        <UIRef Id="WixUI_ErrorProgressText" />
 
         <?include "properties.wxi"?>
         <?include "product_fragment.wxi"?>
diff --git a/Modules/Platform/ADSP-Common.cmake b/Modules/Platform/ADSP-Common.cmake
index 2ba90b2..7ec8a6f 100644
--- a/Modules/Platform/ADSP-Common.cmake
+++ b/Modules/Platform/ADSP-Common.cmake
@@ -9,9 +9,9 @@
     set(CMAKE_ADSP_PROCESSOR "ADSP-${CMAKE_SYSTEM_PROCESSOR}")
     string(TOUPPER "${CMAKE_ADSP_PROCESSOR}" CMAKE_ADSP_PROCESSOR)
 
-    set(CMAKE_ADSP_COMPILER_NAME cc21k.exe)
+    set(CMAKE_ADSP_COMPILER_NAME "cc21k${CMAKE_EXECUTABLE_SUFFIX}")
     if(CMAKE_ADSP_PROCESSOR MATCHES "^ADSP-BF")
-      set(CMAKE_ADSP_COMPILER_NAME ccblkfn.exe)
+      set(CMAKE_ADSP_COMPILER_NAME "ccblkfn${CMAKE_EXECUTABLE_SUFFIX}")
     endif()
 
     set(CMAKE_ADSP_PLATFORM_INITIALIZED TRUE)
@@ -20,7 +20,12 @@
 
 macro(__platform_adsp lang)
   __platform_adsp_init()
-  set(CMAKE_${lang}_COMPILER "${CMAKE_ADSP_ROOT}/${CMAKE_ADSP_COMPILER_NAME}")
+  find_program(
+    CMAKE_${lang}_COMPILER
+    "${CMAKE_ADSP_COMPILER_NAME}"
+    PATHS "${CMAKE_ADSP_ROOT}"
+    REQUIRED
+  )
 
   execute_process(
     COMMAND "${CMAKE_${lang}_COMPILER}" "-proc=${CMAKE_ADSP_PROCESSOR}" "-version"
diff --git a/Modules/Platform/ADSP-Determine.cmake b/Modules/Platform/ADSP-Determine.cmake
index 6ccf1ea..1588c92 100644
--- a/Modules/Platform/ADSP-Determine.cmake
+++ b/Modules/Platform/ADSP-Determine.cmake
@@ -21,6 +21,6 @@
 if(NOT CMAKE_ADSP_ROOT)
   _find_adsp_root("C:/Program Files (x86)/Analog Devices/VisualDSP *")
 endif()
-if(NOT IS_DIRECTORY "${CMAKE_ADSP_ROOT}")
-  message(FATAL_ERROR "ADSP: could not find CCES/VDSP++ install directory ${CMAKE_ADSP_ROOT}")
+if(NOT CMAKE_ADSP_ROOT)
+  _find_adsp_root("/opt/analog/cces *")
 endif()
diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake
index a9aa8e0..55a6680 100644
--- a/Modules/Platform/AIX-GNU.cmake
+++ b/Modules/Platform/AIX-GNU.cmake
@@ -14,8 +14,11 @@
   string(APPEND CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS " -Wl,-bnoipath")
   set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-Wl,-bexpall") # CMP0065 old behavior
   set(CMAKE_${lang}_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH 1)
+  set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v")
 
   set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath")
+  set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL)
+
   if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1)
     unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY)
   endif()
diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake
index 902cbb3..c225de9 100644
--- a/Modules/Platform/AIX-XL.cmake
+++ b/Modules/Platform/AIX-XL.cmake
@@ -17,6 +17,7 @@
   set(CMAKE_SHARED_MODULE_${lang}_FLAGS  " ")
 
   set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath")
+  set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL)
 
   set(_OBJECTS " <OBJECTS>")
   if(DEFINED CMAKE_XL_CreateExportList AND CMAKE_XL_CreateExportList STREQUAL "")
diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake
index 61a6cd2..bd5ba9a 100644
--- a/Modules/Platform/Apple-Clang.cmake
+++ b/Modules/Platform/Apple-Clang.cmake
@@ -15,9 +15,18 @@
     set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ")
   endif()
 
+  set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=ALL)
+
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
 
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld")
+  set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "-fuse-ld=ld" "LINKER:-ld_classic")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+  set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold")
+  set(CMAKE_${lang}_USING_LINKER_SOLD "-fuse-ld=sold")
+
   if(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneOS")
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-miphoneos-version-min=")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneSimulator")
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake
index 823c790..15f6a71 100644
--- a/Modules/Platform/Apple-GNU.cmake
+++ b/Modules/Platform/Apple-GNU.cmake
@@ -17,6 +17,9 @@
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic")
 endmacro()
 
 macro(cmake_gnu_set_sysroot_flag lang)
diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake
index ef64012..070b24d 100644
--- a/Modules/Platform/CYGWIN-GNU.cmake
+++ b/Modules/Platform/CYGWIN-GNU.cmake
@@ -52,6 +52,8 @@
     "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--out-implib,<TARGET_IMPLIB> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>")
   set(CMAKE_${lang}_CREATE_WIN32_EXE "-mwindows")
 
+  set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v")
+
    # No -fPIC on cygwin
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index d614182..533b9ce 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -125,17 +125,17 @@
 set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK_SUPPORTED TRUE)
 
 # Defines LINK_LIBRARY features for libraries
-set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library <LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}")
+set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library,<LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE)
 
-set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library <LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}")
+set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library,<LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY_SUPPORTED TRUE)
 
-set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library <LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}")
+set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,<LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE)
 
 # Defines LINK_LIBRARY feature to Force loading of all members of an archive
-set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load <LIB_ITEM>")
+set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,<LIB_ITEM>")
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
 
 # default to searching for frameworks first
diff --git a/Modules/Platform/Linux-Clang-CUDA.cmake b/Modules/Platform/Linux-Clang-CUDA.cmake
new file mode 100644
index 0000000..4a9337e
--- /dev/null
+++ b/Modules/Platform/Linux-Clang-CUDA.cmake
@@ -0,0 +1,2 @@
+include(Platform/Linux-GNU)
+__linux_compiler_gnu(CUDA)
diff --git a/Modules/Platform/Linux-GNU.cmake b/Modules/Platform/Linux-GNU.cmake
index 6878254..c3878eb 100644
--- a/Modules/Platform/Linux-GNU.cmake
+++ b/Modules/Platform/Linux-GNU.cmake
@@ -12,4 +12,13 @@
   # We pass this for historical reasons.  Projects may have
   # executables that use dlopen but do not set ENABLE_EXPORTS.
   set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "-rdynamic")
+
+  set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v")
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+  set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd")
+  set(CMAKE_${lang}_USING_LINKER_GOLD "-fuse-ld=gold")
+  set(CMAKE_${lang}_USING_LINKER_MOLD "-fuse-ld=mold")
 endmacro()
diff --git a/Modules/Platform/Linux-LLVMFlang-Fortran.cmake b/Modules/Platform/Linux-LLVMFlang-Fortran.cmake
new file mode 100644
index 0000000..ceecc2f
--- /dev/null
+++ b/Modules/Platform/Linux-LLVMFlang-Fortran.cmake
@@ -0,0 +1 @@
+include(Platform/Linux-GNU-Fortran)
diff --git a/Modules/Platform/Linux-NVIDIA-CUDA.cmake b/Modules/Platform/Linux-NVIDIA-CUDA.cmake
new file mode 100644
index 0000000..f383720
--- /dev/null
+++ b/Modules/Platform/Linux-NVIDIA-CUDA.cmake
@@ -0,0 +1,9 @@
+
+set(CMAKE_CUDA_VERBOSE_LINK_FLAG "-Wl,-v")
+
+# linker selection
+set(CMAKE_CUDA_USING_LINKER_SYSTEM "")
+set(CMAKE_CUDA_USING_LINKER_LLD "-fuse-ld=lld")
+set(CMAKE_CUDA_USING_LINKER_BFD "-fuse-ld=bfd")
+set(CMAKE_CUDA_USING_LINKER_GOLD "-fuse-ld=gold")
+set(CMAKE_CUDA_USING_LINKER_MOLD "-fuse-ld=mold")
diff --git a/Modules/Platform/Windows-Clang-C.cmake b/Modules/Platform/Windows-Clang-C.cmake
index 322e3fb..a90e4b7 100644
--- a/Modules/Platform/Windows-Clang-C.cmake
+++ b/Modules/Platform/Windows-Clang-C.cmake
@@ -1,7 +1,7 @@
 include(Platform/Windows-Clang)
 __windows_compiler_clang(C)
 
-if("x${MAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_C)
diff --git a/Modules/Platform/Windows-Clang-CUDA.cmake b/Modules/Platform/Windows-Clang-CUDA.cmake
new file mode 100644
index 0000000..c37df3b
--- /dev/null
+++ b/Modules/Platform/Windows-Clang-CUDA.cmake
@@ -0,0 +1,16 @@
+include(Platform/Windows-Clang)
+__windows_compiler_clang(CUDA)
+
+# Tell Clang where to find the CUDA libraries.
+set(__IMPLICIT_LINKS)
+foreach(dir ${CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES})
+  string(APPEND __IMPLICIT_LINKS " -L\"${dir}\"")
+endforeach()
+string(APPEND CMAKE_CUDA_LINK_EXECUTABLE "${__IMPLICIT_LINKS}")
+string(APPEND CMAKE_CUDA_CREATE_SHARED_LIBRARY "${__IMPLICIT_LINKS}")
+string(APPEND CMAKE_CUDA_CREATE_SHARED_MODULE "${__IMPLICIT_LINKS}")
+unset(__IMPLICIT_LINKS)
+
+# Device linking is just regular linking so these are the same.
+set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG ${CMAKE_CUDA_LINKER_WRAPPER_FLAG})
+set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG_SEP ${CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP})
diff --git a/Modules/Platform/Windows-Clang-CXX.cmake b/Modules/Platform/Windows-Clang-CXX.cmake
index b4aaf1e..af2a48f 100644
--- a/Modules/Platform/Windows-Clang-CXX.cmake
+++ b/Modules/Platform/Windows-Clang-CXX.cmake
@@ -2,7 +2,7 @@
 set(_COMPILE_CXX_MSVC " -TP")
 __windows_compiler_clang(CXX)
 
-if("x${MAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_CXX)
diff --git a/Modules/Platform/Windows-Clang-OBJC.cmake b/Modules/Platform/Windows-Clang-OBJC.cmake
new file mode 100644
index 0000000..7babb98
--- /dev/null
+++ b/Modules/Platform/Windows-Clang-OBJC.cmake
@@ -0,0 +1,18 @@
+include(Platform/Windows-Clang)
+__windows_compiler_clang(OBJC)
+
+if("x${CMAKE_OBJC_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_OBJC)
+    set(CMAKE_OBJC_DEPENDS_USE_COMPILER TRUE)
+  endif()
+elseif("x${CMAKE_OBJC_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_OBJC)
+    # dependencies are computed by the compiler itself
+    set(CMAKE_OBJC_DEPFILE_FORMAT gcc)
+    set(CMAKE_OBJC_DEPENDS_USE_COMPILER TRUE)
+  endif()
+endif()
diff --git a/Modules/Platform/Windows-Clang-OBJCXX.cmake b/Modules/Platform/Windows-Clang-OBJCXX.cmake
new file mode 100644
index 0000000..3bc1673
--- /dev/null
+++ b/Modules/Platform/Windows-Clang-OBJCXX.cmake
@@ -0,0 +1,18 @@
+include(Platform/Windows-Clang)
+__windows_compiler_clang(OBJCXX)
+
+if("x${CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_OBJCXX)
+    set(CMAKE_OBJCXX_DEPENDS_USE_COMPILER TRUE)
+  endif()
+elseif("x${CMAKE_OBJCXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_OBJCXX)
+    # dependencies are computed by the compiler itself
+    set(CMAKE_OBJCXX_DEPFILE_FORMAT gcc)
+    set(CMAKE_OBJCXX_DEPENDS_USE_COMPILER TRUE)
+  endif()
+endif()
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index 33d271d..b9e6394 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -45,6 +45,7 @@
     math(EXPR MSVC_VERSION "${CMAKE_MATCH_1}*100 + ${CMAKE_MATCH_2}")
   endif()
 
+  set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-v")
   # No -fPIC on Windows
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
@@ -52,6 +53,15 @@
   set(CMAKE_${lang}_LINK_OPTIONS_PIE "")
   set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
   set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "")
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared")
+
+  set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL)
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_DEFAULT "-fuse-ld=lld-link")
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=link")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld-link")
+  set(CMAKE_${lang}_USING_LINKER_MSVC "-fuse-ld=link")
 
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1)
   set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
@@ -74,10 +84,10 @@
   set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>")
+    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>")
   set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>")
+    "<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>")
 
   set(CMAKE_${lang}_CREATE_WIN32_EXE "-Xlinker /subsystem:windows")
   set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Xlinker /subsystem:console")
@@ -157,36 +167,32 @@
   endif()
 endmacro()
 
-macro(__verify_same_language_values variable)
-  foreach(lang "C" "CXX" "HIP")
-    if(DEFINED CMAKE_${lang}_${variable})
-      list(APPEND __LANGUAGE_VALUES_${variable} "${CMAKE_${lang}_${variable}}")
-    endif()
+function(__verify_same_language_values variable langs)
+  foreach(lang IN LISTS langs)
+    list(APPEND __LANGUAGE_VALUES_${variable} ${CMAKE_${lang}_${variable}})
   endforeach()
   list(REMOVE_DUPLICATES __LANGUAGE_VALUES_${variable})
   list(LENGTH __LANGUAGE_VALUES_${variable} __NUM_VALUES)
-
   if(__NUM_VALUES GREATER 1)
     message(FATAL_ERROR ${ARGN})
   endif()
-  unset(__NUM_VALUES)
-  unset(__LANGUAGE_VALUES_${variable})
-endmacro()
+endfunction()
 
 if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"
+    OR "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC"
     OR "x${CMAKE_HIP_SIMULATE_ID}" STREQUAL "xMSVC")
 
-  __verify_same_language_values(COMPILER_ID
+  __verify_same_language_values(COMPILER_ID "C;CXX;HIP"
                                 "The current configuration mixes Clang and MSVC or "
                                 "some other CL compatible compiler tool. This is not supported. "
-                                "Use either clang or MSVC as both C, C++ and/or HIP compilers.")
+                                "Use either Clang or MSVC as the compiler for all of C, C++, and/or HIP.")
 
-  __verify_same_language_values(COMPILER_FRONTEND_VARIANT
+  __verify_same_language_values(COMPILER_FRONTEND_VARIANT "C;CXX;CUDA;HIP"
                                 "The current configuration uses the Clang compiler "
                                 "tool with mixed frontend variants, both the GNU and in MSVC CL "
                                 "like variants. This is not supported. Use either clang/clang++ "
-                                "or clang-cl as both C, C++ and/or HIP compilers.")
+                                "or clang-cl as all C, C++, CUDA, and/or HIP compilers.")
 
   if(NOT CMAKE_RC_COMPILER_INIT)
     # Check if rc is already in the path
@@ -208,6 +214,7 @@
 
   if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
       OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
+      OR "x${CMAKE_CUDA_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
       OR "x${CMAKE_HIP_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
 
     include(Platform/Windows-MSVC)
diff --git a/Modules/Platform/Windows-GNU-OBJC-ABI.cmake b/Modules/Platform/Windows-GNU-OBJC-ABI.cmake
new file mode 100644
index 0000000..c8b2ea6
--- /dev/null
+++ b/Modules/Platform/Windows-GNU-OBJC-ABI.cmake
@@ -0,0 +1 @@
+__windows_compiler_gnu_abi(OBJC)
diff --git a/Modules/Platform/Windows-GNU-OBJC.cmake b/Modules/Platform/Windows-GNU-OBJC.cmake
new file mode 100644
index 0000000..42cf3f8
--- /dev/null
+++ b/Modules/Platform/Windows-GNU-OBJC.cmake
@@ -0,0 +1,2 @@
+include(Platform/Windows-GNU)
+__windows_compiler_gnu(OBJC)
diff --git a/Modules/Platform/Windows-GNU-OBJCXX-ABI.cmake b/Modules/Platform/Windows-GNU-OBJCXX-ABI.cmake
new file mode 100644
index 0000000..9a11514
--- /dev/null
+++ b/Modules/Platform/Windows-GNU-OBJCXX-ABI.cmake
@@ -0,0 +1 @@
+__windows_compiler_gnu_abi(OBJCXX)
diff --git a/Modules/Platform/Windows-GNU-OBJCXX.cmake b/Modules/Platform/Windows-GNU-OBJCXX.cmake
new file mode 100644
index 0000000..072cf28
--- /dev/null
+++ b/Modules/Platform/Windows-GNU-OBJCXX.cmake
@@ -0,0 +1,2 @@
+include(Platform/Windows-GNU)
+__windows_compiler_gnu(OBJCXX)
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index 412af6b..9f81882 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -112,6 +112,13 @@
     set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
   endforeach()
 
+  set(CMAKE_${lang}_VERBOSE_LINK_FLAG "-Wl,-v")
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
+  set(CMAKE_${lang}_USING_LINKER_BFD "-fuse-ld=bfd")
+  set(CMAKE_${lang}_USING_LINKER_LLD "-fuse-ld=lld")
+
   # No -fPIC on Windows
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "")
diff --git a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
index 57e36c6..10e3b2c 100644
--- a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
+++ b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
@@ -5,17 +5,22 @@
   include(Platform/Windows-MSVC)
   __windows_compiler_msvc(Fortran)
 
-  # FIXME(LLVMFlang): It does not provides MSVC runtime library selection flags.
-  # It should be given a flag like classic Flang's `-Xclang --dependent-lib=`, or a
-  # dedicated flag to select among multiple `Fortran*.lib` runtime library variants
-  # that each depend on a different MSVC runtime library.  For now, LLVMFlang's
-  # `Fortran*.lib` runtime libraries hard-code use of msvcrt (MultiThreadedDLL),
-  # so we link to it ourselves.
-  set(_LLVMFlang_LINK_RUNTIME "-defaultlib:msvcrt")
-  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
-  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
-  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")
-  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "")
+  if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 18.0)
+    set(_LLVMFlang_LINK_RUNTIME "")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "-fms-runtime-lib=static")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "-fms-runtime-lib=dll")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "-fms-runtime-lib=static_dbg")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "-fms-runtime-lib=dll_dbg")
+  else()
+    # LLVMFlang < 18.0 does not have MSVC runtime library selection flags.
+    # The official distrubtion's `Fortran*.lib` runtime libraries hard-code
+    # use of msvcrt (MultiThreadedDLL), so we link to it ourselves.
+    set(_LLVMFlang_LINK_RUNTIME "-defaultlib:msvcrt")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")
+    set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "")
+  endif()
 
   # LLVMFlang, like Clang, does not provide all debug information format flags.
   # In order to provide easy integration with C and C++ projects that use the
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index 829ab9b..48e7c4e 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -512,6 +512,14 @@
     set(CMAKE_DEPFILE_FLAGS_${lang} "/showIncludes")
     set(CMAKE_${lang}_DEPFILE_FORMAT msvc)
   endif()
+
+  set(CMAKE_${lang}_LINK_LIBRARIES_PROCESSING ORDER=FORWARD UNICITY=ALL)
+
+  # linker selection
+  set(CMAKE_${lang}_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}")
+  set(CMAKE_${lang}_USING_LINKER_LLD "${CMAKE_LINKER_LLD}")
+  set(CMAKE_${lang}_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}")
+  set(CMAKE_${lang}_USING_LINKER_MODE TOOL)
 endmacro()
 
 macro(__windows_compiler_msvc_enable_rc flags)
diff --git a/Modules/Platform/Windows-NVIDIA-CUDA.cmake b/Modules/Platform/Windows-NVIDIA-CUDA.cmake
index 326e715..6489841 100644
--- a/Modules/Platform/Windows-NVIDIA-CUDA.cmake
+++ b/Modules/Platform/Windows-NVIDIA-CUDA.cmake
@@ -47,6 +47,12 @@
   "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> ${_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS} -shared -dlink <OBJECTS> -o <TARGET> <LINK_LIBRARIES> -Xcompiler=-Fd<TARGET_COMPILE_PDB>,-FS${__IMPLICIT_DLINK_FLAGS}")
 unset(__IMPLICIT_DLINK_FLAGS)
 
+# linker selection
+set(CMAKE_CUDA_USING_LINKER_SYSTEM "${CMAKE_LINKER_LINK}")
+set(CMAKE_CUDA_USING_LINKER_LLD "${CMAKE_LINKER_LLD}")
+set(CMAKE_CUDA_USING_LINKER_MSVC "${CMAKE_LINKER_LINK}")
+set(CMAKE_CUDA_USING_LINKER_MODE TOOL)
+
 string(REPLACE "/D" "-D" _PLATFORM_DEFINES_CUDA "${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_CXX}")
 
 if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
diff --git a/Modules/Platform/Windows.cmake b/Modules/Platform/Windows.cmake
index 1bf39cf..af7335b 100644
--- a/Modules/Platform/Windows.cmake
+++ b/Modules/Platform/Windows.cmake
@@ -13,8 +13,15 @@
 set(CMAKE_DL_LIBS "")
 set(CMAKE_EXTRA_LINK_EXTENSIONS ".targets")
 
-set(CMAKE_FIND_LIBRARY_PREFIXES "")
-set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
+set(CMAKE_FIND_LIBRARY_PREFIXES
+  "" # static or import library from MSVC tooling
+  "lib" # static library from Meson with MSVC tooling
+  )
+set(CMAKE_FIND_LIBRARY_SUFFIXES
+  ".dll.lib" # import library from Rust toolchain for MSVC ABI
+  ".lib" # static or import library from MSVC tooling
+  ".a" # static library from Meson with MSVC tooling
+  )
 
 # for borland make long command lines are redirected to a file
 # with the following syntax, see Windows-bcc32.cmake for use
diff --git a/Modules/UseEcos.cmake b/Modules/UseEcos.cmake
index 83c9b20..5e6f606 100644
--- a/Modules/UseEcos.cmake
+++ b/Modules/UseEcos.cmake
@@ -8,21 +8,31 @@
 This module defines variables and macros required to build eCos application.
 
 This file contains the following macros:
-ECOS_ADD_INCLUDE_DIRECTORIES() - add the eCos include dirs
-ECOS_ADD_EXECUTABLE(name source1 ...  sourceN ) - create an eCos
-executable ECOS_ADJUST_DIRECTORY(VAR source1 ...  sourceN ) - adjusts
-the path of the source files and puts the result into VAR
 
-Macros for selecting the toolchain: ECOS_USE_ARM_ELF_TOOLS() - enable
-the ARM ELF toolchain for the directory where it is called
-ECOS_USE_I386_ELF_TOOLS() - enable the i386 ELF toolchain for the
-directory where it is called ECOS_USE_PPC_EABI_TOOLS() - enable the
-PowerPC toolchain for the directory where it is called
+``ECOS_ADD_INCLUDE_DIRECTORIES()``
+  add the eCos include dirs
+``ECOS_ADD_EXECUTABLE(name source1 ...  sourceN )``
+  create an eCos executable
+``ECOS_ADJUST_DIRECTORY(VAR source1 ...  sourceN )``
+  adjusts the path of the source files and puts the result into ``VAR``
 
-It contains the following variables: ECOS_DEFINITIONS
-ECOSCONFIG_EXECUTABLE ECOS_CONFIG_FILE - defaults to ecos.ecc, if your
-eCos configuration file has a different name, adjust this variable for
-internal use only:
+Macros for selecting the toolchain:
+
+``ECOS_USE_ARM_ELF_TOOLS()``
+  enable the ARM ELF toolchain for the directory where it is called
+``ECOS_USE_I386_ELF_TOOLS()``
+  enable the i386 ELF toolchain for the directory where it is called
+``ECOS_USE_PPC_EABI_TOOLS()``
+  enable the PowerPC toolchain for the directory where it is called
+
+It contains the following variables:
+
+``ECOS_DEFINITIONS``
+
+``ECOSCONFIG_EXECUTABLE``
+
+``ECOS_CONFIG_FILE``
+  defaults to ecos.ecc, if your eCos configuration file has a different name, adjust this variable for internal use only:
 
 ::
 
diff --git a/Modules/UseJava/javaTargets.cmake.in b/Modules/UseJava/javaTargets.cmake.in
index 6e14256..dc20c82 100644
--- a/Modules/UseJava/javaTargets.cmake.in
+++ b/Modules/UseJava/javaTargets.cmake.in
@@ -1,6 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
 cmake_policy(PUSH)
-cmake_policy(VERSION 2.8)
+cmake_policy(VERSION 2.8.12...3.28)
 
 #----------------------------------------------------------------
 # Generated CMake Java target import file.
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake
index cece973..f264fb6 100644
--- a/Modules/UseSWIG.cmake
+++ b/Modules/UseSWIG.cmake
@@ -657,7 +657,7 @@
     if(NOT ("-dllimport" IN_LIST swig_source_file_flags OR "-dllimport" IN_LIST SWIG_MODULE_${name}_EXTRA_FLAGS))
       # This makes sure that the name used in the generated DllImport
       # matches the library name created by CMake
-      list (APPEND SWIG_MODULE_${name}_EXTRA_FLAGS "-dllimport" "$<TARGET_FILE_PREFIX:${target_name}>$<TARGET_FILE_BASE_NAME:${target_name}>")
+      list (APPEND SWIG_MODULE_${name}_EXTRA_FLAGS "-dllimport" "$<TARGET_FILE_BASE_NAME:${target_name}>")
     endif()
   endif()
   if (SWIG_MODULE_${name}_LANGUAGE STREQUAL "PYTHON" AND NOT SWIG_MODULE_${name}_NOPROXY)
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 1bc855e..8c57762 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -450,7 +450,6 @@
   cmUVProcessChain.h
   cmUVStream.h
   cmUVStreambuf.h
-  cmUVSignalHackRAII.h
   cmVariableWatch.cxx
   cmVariableWatch.h
   cmVersion.cxx
@@ -558,6 +557,8 @@
   cmFindLibraryCommand.h
   cmFindPackageCommand.cxx
   cmFindPackageCommand.h
+  cmFindPackageStack.cxx
+  cmFindPackageStack.h
   cmFindPathCommand.cxx
   cmFindPathCommand.h
   cmFindProgramCommand.cxx
@@ -1090,6 +1091,9 @@
   CTest/cmCTestP4.cxx
   CTest/cmCTestP4.h
 
+  CTest/cmUVJobServerClient.cxx
+  CTest/cmUVJobServerClient.h
+
   LexerParser/cmCTestResourceGroupsLexer.cxx
   LexerParser/cmCTestResourceGroupsLexer.h
   LexerParser/cmCTestResourceGroupsLexer.in.l
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index d022679..974111a 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
-set(CMake_VERSION_MINOR 28)
-set(CMake_VERSION_PATCH 3)
+set(CMake_VERSION_MINOR 29)
+set(CMake_VERSION_PATCH 20240219)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
index 5724175..8047729 100644
--- a/Source/CPack/IFW/cmCPackIFWGenerator.cxx
+++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
@@ -55,7 +55,6 @@
 std::vector<std::string> cmCPackIFWGenerator::BuildRepogenCommand()
 {
   std::vector<std::string> ifwCmd;
-  std::string ifwArg;
 
   ifwCmd.emplace_back(this->RepoGen);
 
@@ -104,7 +103,7 @@
   if (!this->OnlineOnly && !this->DownloadedPackages.empty()) {
     ifwCmd.emplace_back("-i");
     auto it = this->DownloadedPackages.begin();
-    ifwArg = (*it)->Name;
+    std::string ifwArg = (*it)->Name;
     ++it;
     while (it != this->DownloadedPackages.end()) {
       ifwArg += "," + (*it)->Name;
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 5077596..6918b5e 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -359,6 +359,7 @@
                     GetOption("CPACK_PACKAGE_NAME"));
   CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER");
   CopyDefinition(includeFile, "CPACK_WIX_UI_REF");
+  CopyDefinition(includeFile, "CPACK_WIX_INSTALL_SCOPE");
 }
 
 void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile()
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx
index c9c069c..b7b6785 100644
--- a/Source/CPack/cmCPackArchiveGenerator.cxx
+++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -5,6 +5,8 @@
 #include <cstring>
 #include <map>
 #include <ostream>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -17,6 +19,121 @@
 #include "cmValue.h"
 #include "cmWorkingDirectory.h"
 
+enum class DeduplicateStatus
+{
+  Skip,
+  Add,
+  Error
+};
+
+/**
+ * @class cmCPackArchiveGenerator::Deduplicator
+ * @brief A utility class for deduplicating files, folders, and symlinks.
+ *
+ * This class is responsible for identifying duplicate files, folders, and
+ * symlinks when generating an archive. It keeps track of the paths that have
+ * been processed and helps in deciding whether a new path should be added,
+ * skipped, or flagged as an error.
+ */
+class cmCPackArchiveGenerator::Deduplicator
+{
+private:
+  /**
+   * @brief Compares a file with already processed files.
+   *
+   * @param path The path of the file to compare.
+   * @param localTopLevel The top-level directory for the file.
+   * @return DeduplicateStatus indicating whether to add, skip, or flag an
+   * error for the file.
+   */
+  DeduplicateStatus CompareFile(const std::string& path,
+                                const std::string& localTopLevel)
+  {
+    auto fileItr = this->Files.find(path);
+    if (fileItr != this->Files.end()) {
+      return cmSystemTools::FilesDiffer(path, fileItr->second)
+        ? DeduplicateStatus::Error
+        : DeduplicateStatus::Skip;
+    }
+
+    this->Files[path] = cmStrCat(localTopLevel, "/", path);
+    return DeduplicateStatus::Add;
+  }
+
+  /**
+   * @brief Compares a folder with already processed folders.
+   *
+   * @param path The path of the folder to compare.
+   * @return DeduplicateStatus indicating whether to add or skip the folder.
+   */
+  DeduplicateStatus CompareFolder(const std::string& path)
+  {
+    if (this->Folders.find(path) != this->Folders.end()) {
+      return DeduplicateStatus::Skip;
+    }
+
+    this->Folders.emplace(path);
+    return DeduplicateStatus::Add;
+  }
+
+  /**
+   * @brief Compares a symlink with already processed symlinks.
+   *
+   * @param path The path of the symlink to compare.
+   * @return DeduplicateStatus indicating whether to add, skip, or flag an
+   * error for the symlink.
+   */
+  DeduplicateStatus CompareSymlink(const std::string& path)
+  {
+    auto symlinkItr = this->Symlink.find(path);
+    std::string symlinkValue;
+    auto status = cmSystemTools::ReadSymlink(path, symlinkValue);
+    if (!status.IsSuccess()) {
+      return DeduplicateStatus::Error;
+    }
+
+    if (symlinkItr != this->Symlink.end()) {
+      return symlinkValue == symlinkItr->second ? DeduplicateStatus::Skip
+                                                : DeduplicateStatus::Error;
+    }
+
+    this->Symlink[path] = symlinkValue;
+    return DeduplicateStatus::Add;
+  }
+
+public:
+  /**
+   * @brief Determines the deduplication status of a given path.
+   *
+   * This method identifies whether the given path is a file, folder, or
+   * symlink and then delegates to the appropriate comparison method.
+   *
+   * @param path The path to check for deduplication.
+   * @param localTopLevel The top-level directory for the path.
+   * @return DeduplicateStatus indicating the action to take for the given
+   * path.
+   */
+  DeduplicateStatus IsDeduplicate(const std::string& path,
+                                  const std::string& localTopLevel)
+  {
+    DeduplicateStatus status;
+    if (cmSystemTools::FileIsDirectory(path)) {
+      status = this->CompareFolder(path);
+    } else if (cmSystemTools::FileIsSymlink(path)) {
+      status = this->CompareSymlink(path);
+    } else {
+      status = this->CompareFile(path, localTopLevel);
+    }
+
+    return status;
+  }
+
+private:
+  std::unordered_map<std::string, std::string> Symlink;
+  std::unordered_set<std::string> Folders;
+  std::unordered_map<std::string, std::string> Files;
+};
+
 cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
 {
   return new cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip",
@@ -110,7 +227,8 @@
 }
 
 int cmCPackArchiveGenerator::addOneComponentToArchive(
-  cmArchiveWrite& archive, cmCPackComponent* component)
+  cmArchiveWrite& archive, cmCPackComponent* component,
+  Deduplicator* deduplicator)
 {
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "   - packaging component: " << component->Name << std::endl);
@@ -139,8 +257,25 @@
   }
   for (std::string const& file : component->Files) {
     std::string rp = filePrefix + file;
-    cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
-    archive.Add(rp, 0, nullptr, false);
+
+    DeduplicateStatus status = DeduplicateStatus::Add;
+    if (deduplicator != nullptr) {
+      status = deduplicator->IsDeduplicate(rp, localToplevel);
+    }
+
+    if (deduplicator == nullptr || status == DeduplicateStatus::Add) {
+      cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl);
+      archive.Add(rp, 0, nullptr, false);
+    } else if (status == DeduplicateStatus::Error) {
+      cmCPackLogger(cmCPackLog::LOG_ERROR,
+                    "ERROR The data in files with the "
+                    "same filename is different.");
+      return 0;
+    } else {
+      cmCPackLogger(cmCPackLog::LOG_DEBUG,
+                    "Passing file: " << rp << std::endl);
+    }
+
     if (!archive) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "ERROR while packaging files: " << archive.GetError()
@@ -197,6 +332,8 @@
       std::string packageFileName = std::string(this->toplevel) + "/" +
         this->GetArchiveComponentFileName(compG.first, true);
 
+      Deduplicator deduplicator;
+
       // open a block in order to automatically close archive
       // at the end of the block
       {
@@ -204,7 +341,7 @@
         // now iterate over the component of this group
         for (cmCPackComponent* comp : (compG.second).Components) {
           // Add the files of this component to the archive
-          this->addOneComponentToArchive(archive, comp);
+          this->addOneComponentToArchive(archive, comp, &deduplicator);
         }
       }
       // add the generated package to package file names list
@@ -231,7 +368,7 @@
         {
           DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
           // Add the files of this component to the archive
-          this->addOneComponentToArchive(archive, &(comp.second));
+          this->addOneComponentToArchive(archive, &(comp.second), nullptr);
         }
         // add the generated package to package file names list
         this->packageFileNames.push_back(std::move(packageFileName));
@@ -252,7 +389,7 @@
       {
         DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive);
         // Add the files of this component to the archive
-        this->addOneComponentToArchive(archive, &(comp.second));
+        this->addOneComponentToArchive(archive, &(comp.second), nullptr);
       }
       // add the generated package to package file names list
       this->packageFileNames.push_back(std::move(packageFileName));
@@ -282,10 +419,12 @@
                   << std::endl);
   DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive);
 
+  Deduplicator deduplicator;
+
   // The ALL COMPONENTS in ONE package case
   for (auto& comp : this->Components) {
     // Add the files of this component to the archive
-    this->addOneComponentToArchive(archive, &(comp.second));
+    this->addOneComponentToArchive(archive, &(comp.second), &deduplicator);
   }
 
   // archive goes out of scope so it will finalized and closed.
diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h
index 8a9bbc6..b8a1afa 100644
--- a/Source/CPack/cmCPackArchiveGenerator.h
+++ b/Source/CPack/cmCPackArchiveGenerator.h
@@ -47,6 +47,8 @@
   std::string GetArchiveComponentFileName(const std::string& component,
                                           bool isGroupName);
 
+  class Deduplicator;
+
 protected:
   int InitializeInternal() override;
   /**
@@ -54,9 +56,11 @@
    * to the provided (already opened) archive.
    * @param[in,out] archive the archive object
    * @param[in] component the component whose file will be added to archive
+   * @param[in] deduplicator file deduplicator utility.
    */
   int addOneComponentToArchive(cmArchiveWrite& archive,
-                               cmCPackComponent* component);
+                               cmCPackComponent* component,
+                               Deduplicator* deduplicator);
 
   /**
    * The main package file method.
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 11d90c0..f0ea690 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -360,7 +360,7 @@
       cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
       cmsys::Glob gl;
       std::string top = *it;
-      it++;
+      ++it;
       std::string subdir = *it;
       std::string findExpr = cmStrCat(top, "/*");
       cmCPackLogger(cmCPackLog::LOG_OUTPUT,
diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx
index b8bf070..fcd0a5d 100644
--- a/Source/CPack/cmCPackInnoSetupGenerator.cxx
+++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx
@@ -7,7 +7,6 @@
 #include <cctype>
 #include <cstdlib>
 #include <ostream>
-#include <stack>
 #include <utility>
 
 #include "cmsys/RegularExpression.hxx"
@@ -579,8 +578,9 @@
 
 bool cmCPackInnoSetupGenerator::ProcessComponents()
 {
-  codeIncludes.push_back("{ The following lines are required by CPack because "
-                         "this script uses components }");
+  codeIncludes.emplace_back(
+    "{ The following lines are required by CPack because "
+    "this script uses components }");
 
   // Installation types
   std::vector<cmCPackInstallationType*> types(InstallationTypes.size());
@@ -607,12 +607,11 @@
     "\"{code:CPackGetCustomInstallationMessage}\"";
   customTypeParams["Flags"] = "iscustom";
 
-  allTypes.push_back("custom");
+  allTypes.emplace_back("custom");
   typeInstructions.push_back(ISKeyValueLine(customTypeParams));
 
   // Components
   std::vector<cmCPackComponent*> downloadedComponents;
-  std::stack<cmCPackComponentGroup*> groups;
   for (auto& i : Components) {
     cmCPackInnoSetupKeyValuePairs params;
     cmCPackComponent* component = &i.second;
@@ -633,6 +632,7 @@
     } else if (!component->InstallationTypes.empty()) {
       std::vector<std::string> installationTypes;
 
+      installationTypes.reserve(component->InstallationTypes.size());
       for (cmCPackInstallationType* j : component->InstallationTypes) {
         installationTypes.push_back(j->Name);
       }
diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h
index 2ab2f8e..347b0f7 100644
--- a/Source/CPack/cmCPackLog.h
+++ b/Source/CPack/cmCPackLog.h
@@ -29,7 +29,7 @@
   cmCPackLog(const cmCPackLog&) = delete;
   cmCPackLog& operator=(const cmCPackLog&) = delete;
 
-  enum __log_tags
+  enum cm_log_tags
   {
     NOTAG = 0,
     LOG_OUTPUT = 0x1,
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx
index 6ad3755..2673fee 100644
--- a/Source/CPack/cmCPackSTGZGenerator.cxx
+++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -3,10 +3,11 @@
 #include "cmCPackSTGZGenerator.h"
 
 #include <cstdio>
-#include <sstream>
 #include <string>
 #include <vector>
 
+#include <fcntl.h>
+
 #include "cmsys/FStream.hxx"
 
 #include "cm_sys_stat.h"
@@ -69,7 +70,6 @@
 int cmCPackSTGZGenerator::GenerateHeader(std::ostream* os)
 {
   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Writing header" << std::endl);
-  std::ostringstream str;
   int counter = 0;
 
   std::string inLicFile = this->GetOption("CPACK_RESOURCE_FILE_LICENSE");
diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx
index 246e811..87081f0 100644
--- a/Source/CTest/cmCTestBZR.cxx
+++ b/Source/CTest/cmCTestBZR.cxx
@@ -135,14 +135,14 @@
 std::string cmCTestBZR::LoadInfo()
 {
   // Run "bzr info" to get the repository info from the work tree.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_info[] = { bzr, "info", nullptr };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_info = { bzr, "info" };
   InfoParser iout(this, "info-out> ");
   OutputLogger ierr(this->Log, "info-err> ");
   this->RunChild(bzr_info, &iout, &ierr);
 
   // Run "bzr revno" to get the repository revision number from the work tree.
-  const char* bzr_revno[] = { bzr, "revno", nullptr };
+  std::vector<std::string> bzr_revno = { bzr, "revno" };
   std::string rev;
   RevnoParser rout(this, "revno-out> ", rev);
   OutputLogger rerr(this->Log, "revno-err> ");
@@ -372,22 +372,18 @@
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
   // Use "bzr pull" to update the working tree.
-  std::vector<char const*> bzr_update;
-  bzr_update.push_back(this->CommandLineTool.c_str());
-  bzr_update.push_back("pull");
+  std::vector<std::string> bzr_update;
+  bzr_update.push_back(this->CommandLineTool);
+  bzr_update.emplace_back("pull");
 
-  for (std::string const& arg : args) {
-    bzr_update.push_back(arg.c_str());
-  }
+  cm::append(bzr_update, args);
 
-  bzr_update.push_back(this->URL.c_str());
-
-  bzr_update.push_back(nullptr);
+  bzr_update.push_back(this->URL);
 
   // For some reason bzr uses stderr to display the update status.
   OutputLogger out(this->Log, "pull-out> ");
   UpdateParser err(this, "pull-err> ");
-  return this->RunUpdateCommand(bzr_update.data(), &out, &err);
+  return this->RunUpdateCommand(bzr_update, &out, &err);
 }
 
 bool cmCTestBZR::LoadRevisions()
@@ -408,10 +404,9 @@
   }
 
   // Run "bzr log" to get all global revisions of interest.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_log[] = {
-    bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr
-  };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_log = { bzr,  "log",   "-v",     "-r",
+                                       revs, "--xml", this->URL };
   {
     LogParser out(this, "log-out> ");
     OutputLogger err(this->Log, "log-err> ");
@@ -467,8 +462,8 @@
 bool cmCTestBZR::LoadModifications()
 {
   // Run "bzr status" which reports local modifications.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_status[] = { bzr, "status", "-SV", nullptr };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_status = { bzr, "status", "-SV" };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(bzr_status, &out, &err);
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index 5feb953..bb6ccc3 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -7,8 +7,6 @@
 #include <cstring>
 #include <ratio>
 
-#include "cmsys/Process.h"
-
 #include "cmBuildOptions.h"
 #include "cmCTest.h"
 #include "cmCTestTestHandler.h"
@@ -308,12 +306,11 @@
     return 1;
   }
 
-  std::vector<const char*> testCommand;
-  testCommand.push_back(fullPath.c_str());
+  std::vector<std::string> testCommand;
+  testCommand.push_back(fullPath);
   for (std::string const& testCommandArg : this->TestCommandArgs) {
-    testCommand.push_back(testCommandArg.c_str());
+    testCommand.push_back(testCommandArg);
   }
-  testCommand.push_back(nullptr);
   std::string outs;
   int retval = 0;
   // run the test from the this->BuildRunDir if set
@@ -349,10 +346,10 @@
     }
   }
 
-  int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
-                                        remainingTime, nullptr);
+  bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
+                                         remainingTime, nullptr);
 
-  if (runTestRes != cmsysProcess_State_Exited || retval != 0) {
+  if (!runTestRes || retval != 0) {
     out << "Test command failed: " << testCommand[0] << "\n";
     retval = 1;
   }
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index 00ecf42..859798e 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -3,15 +3,17 @@
 #include "cmCTestBuildHandler.h"
 
 #include <cstdlib>
+#include <memory>
 #include <ratio>
 #include <set>
 #include <utility>
 
 #include <cmext/algorithm>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestLaunchReporter.h"
@@ -24,6 +26,9 @@
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -420,7 +425,7 @@
   cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
   this->ColorRemover = &colorRemover;
   int retVal = 0;
-  int res = cmsysProcess_State_Exited;
+  bool res = true;
   if (!this->CTest->GetShowOnly()) {
     res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
                                ofs);
@@ -475,7 +480,7 @@
   }
   this->GenerateXMLFooter(xml, elapsed_build_time);
 
-  if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) {
+  if (!res || retVal || this->TotalErrors > 0) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Error(s) when building project" << std::endl);
   }
@@ -764,10 +769,10 @@
   }
 }
 
-int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
-                                        int* retVal, const char* dir,
-                                        int timeout, std::ostream& ofs,
-                                        Encoding encoding)
+bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
+                                         int* retVal, const char* dir,
+                                         int timeout, std::ostream& ofs,
+                                         Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -776,19 +781,9 @@
     return false;
   }
 
-  std::vector<const char*> argv;
-  argv.reserve(args.size() + 1);
-  for (std::string const& arg : args) {
-    argv.push_back(arg.c_str());
-  }
-  argv.push_back(nullptr);
-
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Run command:", this->Quiet);
-  for (char const* arg : argv) {
-    if (!arg) {
-      break;
-    }
+  for (auto const& arg : args) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        " \"" << arg << "\"", this->Quiet);
   }
@@ -800,21 +795,20 @@
   static_cast<void>(launchHelper);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  cmsysProcess_SetTimeout(cp, timeout);
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
+  }
+  auto chain = builder.Start();
 
   // Initialize tick's
   std::string::size_type tick = 0;
-  const std::string::size_type tick_len = 1024;
+  static constexpr std::string::size_type tick_len = 1024;
 
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
   cmCTestOptionalLog(
     this->CTest, HANDLER_PROGRESS_OUTPUT,
     "   Each symbol represents "
@@ -836,39 +830,65 @@
   this->WarningQuotaReached = false;
   this->ErrorQuotaReached = false;
 
+  cm::uv_timer_ptr timer;
+  bool timedOut = false;
+  timer.init(chain.GetLoop(), &timedOut);
+  if (timeout > 0) {
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      timeout * 1000, 0);
+  }
+
   // For every chunk of data
-  int res;
-  while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    // Replace '\0' with '\n', since '\0' does not really make sense. This is
-    // for Visual Studio output
-    for (int cc = 0; cc < length; ++cc) {
-      if (data[cc] == 0) {
-        data[cc] = '\n';
-      }
-    }
+  cm::uv_pipe_ptr outputStream;
+  bool outFinished = false;
+  cm::uv_pipe_ptr errorStream;
+  bool errFinished = false;
+  auto startRead = [this, &chain, &processOutput, &tick,
+                    &ofs](cm::uv_pipe_ptr& pipe, int stream,
+                          t_BuildProcessingQueueType& queue, bool& finished,
+                          int id) -> std::unique_ptr<cmUVStreamReadHandle> {
+    pipe.init(chain.GetLoop(), 0);
+    uv_pipe_open(pipe, stream);
+    return cmUVStreamRead(
+      pipe,
+      [this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) {
+        // Replace '\0' with '\n', since '\0' does not really make sense. This
+        // is for Visual Studio output
+        for (auto& c : data) {
+          if (c == 0) {
+            c = '\n';
+          }
+        }
 
-    // Process the chunk of data
-    if (res == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 1);
-      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                          &this->BuildProcessingErrorQueue);
-    } else {
-      processOutput.DecodeText(data, length, strdata, 2);
-      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                          &this->BuildProcessingQueue);
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata, 1);
-  if (!strdata.empty()) {
-    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                        &this->BuildProcessingErrorQueue);
-  }
-  processOutput.DecodeText(std::string(), strdata, 2);
-  if (!strdata.empty()) {
-    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                        &this->BuildProcessingQueue);
-  }
+        // Process the chunk of data
+        std::string strdata;
+        processOutput.DecodeText(data.data(), data.size(), strdata, id);
+        this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
+                            ofs, &queue);
+      },
+      [this, &processOutput, &queue, id, &tick, &ofs, &finished]() {
+        std::string strdata;
+        processOutput.DecodeText(std::string(), strdata, id);
+        if (!strdata.empty()) {
+          this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
+                              ofs, &queue);
+        }
+        finished = true;
+      });
+  };
+  auto outputHandle = startRead(outputStream, chain.OutputStream(),
+                                this->BuildProcessingQueue, outFinished, 1);
+  auto errorHandle =
+    startRead(errorStream, chain.ErrorStream(),
+              this->BuildProcessingErrorQueue, errFinished, 2);
 
+  while (!timedOut && !(outFinished && errFinished && chain.Finished())) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
                       &this->BuildProcessingQueue);
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@@ -879,90 +899,93 @@
                        << std::endl,
                      this->Quiet);
 
-  // Properly handle output of the build command
-  cmsysProcess_WaitForExit(cp, nullptr);
-  int result = cmsysProcess_GetState(cp);
-
-  if (result == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-                         "Command exited with the value: " << *retVal
-                                                           << std::endl,
-                         this->Quiet);
-      // if a non zero return value
-      if (*retVal) {
-        // If there was an error running command, report that on the
-        // dashboard.
-        if (this->UseCTestLaunch) {
-          // For launchers, do not record this top-level error if other
-          // more granular build errors have already been captured.
-          bool launcherXMLFound = false;
-          cmsys::Directory launchDir;
-          launchDir.Load(this->CTestLaunchDir);
-          unsigned long n = launchDir.GetNumberOfFiles();
-          for (unsigned long i = 0; i < n; ++i) {
-            const char* fname = launchDir.GetFile(i);
-            if (cmHasLiteralSuffix(fname, ".xml")) {
-              launcherXMLFound = true;
-              break;
+  if (chain.Finished()) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                             "Command exited with the value: " << *retVal
+                                                               << std::endl,
+                             this->Quiet);
+          // if a non zero return value
+          if (*retVal) {
+            // If there was an error running command, report that on the
+            // dashboard.
+            if (this->UseCTestLaunch) {
+              // For launchers, do not record this top-level error if other
+              // more granular build errors have already been captured.
+              bool launcherXMLFound = false;
+              cmsys::Directory launchDir;
+              launchDir.Load(this->CTestLaunchDir);
+              unsigned long n = launchDir.GetNumberOfFiles();
+              for (unsigned long i = 0; i < n; ++i) {
+                const char* fname = launchDir.GetFile(i);
+                if (cmHasLiteralSuffix(fname, ".xml")) {
+                  launcherXMLFound = true;
+                  break;
+                }
+              }
+              if (!launcherXMLFound) {
+                cmCTestLaunchReporter reporter;
+                reporter.RealArgs = args;
+                reporter.ComputeFileNames();
+                reporter.ExitCode = *retVal;
+                reporter.Status = status;
+                // Use temporary BuildLog file to populate this error for
+                // CDash.
+                ofs.flush();
+                reporter.LogOut = this->LogFileNames["Build"];
+                reporter.LogOut += ".tmp";
+                reporter.WriteXML();
+              }
+            } else {
+              cmCTestBuildErrorWarning errorwarning;
+              errorwarning.LineNumber = 0;
+              errorwarning.LogLine = 1;
+              errorwarning.Text = cmStrCat(
+                "*** WARNING non-zero return value in ctest from: ", args[0]);
+              errorwarning.PreContext.clear();
+              errorwarning.PostContext.clear();
+              errorwarning.Error = false;
+              this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+              this->TotalWarnings++;
             }
           }
-          if (!launcherXMLFound) {
-            cmCTestLaunchReporter reporter;
-            reporter.RealArgs = args;
-            reporter.ComputeFileNames();
-            reporter.ExitCode = *retVal;
-            reporter.Process = cp;
-            // Use temporary BuildLog file to populate this error for CDash.
-            ofs.flush();
-            reporter.LogOut = this->LogFileNames["Build"];
-            reporter.LogOut += ".tmp";
-            reporter.WriteXML();
-          }
-        } else {
-          cmCTestBuildErrorWarning errorwarning;
-          errorwarning.LineNumber = 0;
-          errorwarning.LogLine = 1;
-          errorwarning.Text = cmStrCat(
-            "*** WARNING non-zero return value in ctest from: ", argv[0]);
-          errorwarning.PreContext.clear();
-          errorwarning.PostContext.clear();
-          errorwarning.Error = false;
-          this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-          this->TotalWarnings++;
         }
-      }
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn: {
+        // If there was an error running command, report that on the dashboard.
+        cmCTestBuildErrorWarning errorwarning;
+        errorwarning.LineNumber = 0;
+        errorwarning.LogLine = 1;
+        errorwarning.Text =
+          cmStrCat("*** ERROR executing: ", exception.second);
+        errorwarning.PreContext.clear();
+        errorwarning.PostContext.clear();
+        errorwarning.Error = true;
+        this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+        this->TotalErrors++;
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "There was an error: " << exception.second << std::endl);
+      } break;
+      default:
+        if (retVal) {
+          *retVal = status.TermSignal;
+          cmCTestOptionalLog(
+            this->CTest, WARNING,
+            "There was an exception: " << *retVal << std::endl, this->Quiet);
+        }
+        break;
     }
-  } else if (result == cmsysProcess_State_Exception) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitException(cp);
-      cmCTestOptionalLog(this->CTest, WARNING,
-                         "There was an exception: " << *retVal << std::endl,
-                         this->Quiet);
-    }
-  } else if (result == cmsysProcess_State_Expired) {
+  } else {
     cmCTestOptionalLog(this->CTest, WARNING,
                        "There was a timeout" << std::endl, this->Quiet);
-  } else if (result == cmsysProcess_State_Error) {
-    // If there was an error running command, report that on the dashboard.
-    cmCTestBuildErrorWarning errorwarning;
-    errorwarning.LineNumber = 0;
-    errorwarning.LogLine = 1;
-    errorwarning.Text =
-      cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
-    errorwarning.PreContext.clear();
-    errorwarning.PostContext.clear();
-    errorwarning.Error = true;
-    this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-    this->TotalErrors++;
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "There was an error: " << cmsysProcess_GetErrorString(cp)
-                                      << std::endl);
   }
 
-  cmsysProcess_Delete(cp);
-  return result;
+  return true;
 }
 
 // ######################################################################
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h
index e33294d..90945b1 100644
--- a/Source/CTest/cmCTestBuildHandler.h
+++ b/Source/CTest/cmCTestBuildHandler.h
@@ -53,9 +53,9 @@
 
   //! Run command specialized for make and configure. Returns process status
   // and retVal is return value or exception.
-  int RunMakeCommand(const std::string& command, int* retVal, const char* dir,
-                     int timeout, std::ostream& ofs,
-                     Encoding encoding = cmProcessOutput::Auto);
+  bool RunMakeCommand(const std::string& command, int* retVal, const char* dir,
+                      int timeout, std::ostream& ofs,
+                      Encoding encoding = cmProcessOutput::Auto);
 
   enum
   {
diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx
index 95e898c..badd43e 100644
--- a/Source/CTest/cmCTestCVS.cxx
+++ b/Source/CTest/cmCTestCVS.cxx
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include <cm/string_view>
+#include <cmext/algorithm>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
@@ -89,18 +90,15 @@
   }
 
   // Run "cvs update" to update the work tree.
-  std::vector<char const*> cvs_update;
-  cvs_update.push_back(this->CommandLineTool.c_str());
-  cvs_update.push_back("-z3");
-  cvs_update.push_back("update");
-  for (std::string const& arg : args) {
-    cvs_update.push_back(arg.c_str());
-  }
-  cvs_update.push_back(nullptr);
+  std::vector<std::string> cvs_update;
+  cvs_update.push_back(this->CommandLineTool);
+  cvs_update.emplace_back("-z3");
+  cvs_update.emplace_back("update");
+  cm::append(cvs_update, args);
 
   UpdateParser out(this, "up-out> ");
   UpdateParser err(this, "up-err> ");
-  return this->RunUpdateCommand(cvs_update.data(), &out, &err);
+  return this->RunUpdateCommand(cvs_update, &out, &err);
 }
 
 class cmCTestCVS::LogParser : public cmCTestVC::LineParser
@@ -221,10 +219,8 @@
   cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
 
   // Run "cvs log" to get revisions of this file on this branch.
-  const char* cvs = this->CommandLineTool.c_str();
-  const char* cvs_log[] = {
-    cvs, "log", "-N", branchFlag, file.c_str(), nullptr
-  };
+  std::string cvs = this->CommandLineTool;
+  std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file };
 
   LogParser out(this, "log-out> ", revisions);
   OutputLogger err(this->Log, "log-err> ");
diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx
index 914930e..dd8952f 100644
--- a/Source/CTest/cmCTestConfigureHandler.cxx
+++ b/Source/CTest/cmCTestConfigureHandler.cxx
@@ -45,7 +45,7 @@
   auto elapsed_time_start = std::chrono::steady_clock::now();
   std::string output;
   int retVal = 0;
-  int res = 0;
+  bool res = false;
   if (!this->CTest->GetShowOnly()) {
     cmGeneratedFileStream os;
     if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index 2874be7..f9f9add 100644
--- a/Source/CTest/cmCTestCoverageHandler.cxx
+++ b/Source/CTest/cmCTestCoverageHandler.cxx
@@ -9,6 +9,7 @@
 #include <cstring>
 #include <iomanip>
 #include <iterator>
+#include <memory>
 #include <ratio>
 #include <sstream>
 #include <type_traits>
@@ -18,7 +19,6 @@
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTest.h"
@@ -33,6 +33,7 @@
 #include "cmParsePHPCoverage.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 
@@ -40,85 +41,6 @@
 
 #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
 
-class cmCTestRunProcess
-{
-public:
-  cmCTestRunProcess()
-  {
-    this->Process = cmsysProcess_New();
-    this->PipeState = -1;
-    this->TimeOut = cmDuration(-1);
-  }
-  ~cmCTestRunProcess()
-  {
-    if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None &&
-        this->PipeState != cmsysProcess_Pipe_Timeout) {
-      this->WaitForExit();
-    }
-    cmsysProcess_Delete(this->Process);
-  }
-  cmCTestRunProcess(const cmCTestRunProcess&) = delete;
-  cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;
-  void SetCommand(const char* command)
-  {
-    this->CommandLineStrings.clear();
-    this->CommandLineStrings.emplace_back(command);
-  }
-  void AddArgument(const char* arg)
-  {
-    if (arg) {
-      this->CommandLineStrings.emplace_back(arg);
-    }
-  }
-  void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }
-  void SetTimeout(cmDuration t) { this->TimeOut = t; }
-  bool StartProcess()
-  {
-    std::vector<const char*> args;
-    args.reserve(this->CommandLineStrings.size());
-    for (std::string const& cl : this->CommandLineStrings) {
-      args.push_back(cl.c_str());
-    }
-    args.push_back(nullptr); // null terminate
-    cmsysProcess_SetCommand(this->Process, args.data());
-    if (!this->WorkingDirectory.empty()) {
-      cmsysProcess_SetWorkingDirectory(this->Process,
-                                       this->WorkingDirectory.c_str());
-    }
-
-    cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);
-    if (this->TimeOut >= cmDuration::zero()) {
-      cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());
-    }
-    cmsysProcess_Execute(this->Process);
-    this->PipeState = cmsysProcess_GetState(this->Process);
-    // if the process is running or exited return true
-    return this->PipeState == cmsysProcess_State_Executing ||
-      this->PipeState == cmsysProcess_State_Exited;
-  }
-  void SetStdoutFile(const char* fname)
-  {
-    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
-  }
-  void SetStderrFile(const char* fname)
-  {
-    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
-  }
-  int WaitForExit(double* timeout = nullptr)
-  {
-    this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);
-    return this->PipeState;
-  }
-  int GetProcessState() const { return this->PipeState; }
-
-private:
-  int PipeState;
-  cmsysProcess* Process;
-  std::vector<std::string> CommandLineStrings;
-  std::string WorkingDirectory;
-  cmDuration TimeOut;
-};
-
 cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
 
 void cmCTestCoverageHandler::Initialize()
@@ -1940,34 +1862,35 @@
     cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
     return 0;
   }
+  std::vector<std::string> args{ cmd };
   if (arg) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << " " << arg << "\n", this->Quiet);
+    args.emplace_back(arg);
   } else {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << "\n", this->Quiet);
   }
   // create a process object and start it
-  cmCTestRunProcess runCoverageSrc;
-  runCoverageSrc.SetCommand(program.c_str());
-  runCoverageSrc.AddArgument(arg);
+  cmUVProcessChainBuilder builder;
   std::string stdoutFile =
     cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
              this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
   std::string stderrFile = stdoutFile;
   stdoutFile += ".stdout";
   stderrFile += ".stderr";
-  runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
-  runCoverageSrc.SetStderrFile(stderrFile.c_str());
-  if (!runCoverageSrc.StartProcess()) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "Could not run : " << program << " " << arg << "\n"
-                                  << "kwsys process state : "
-                                  << runCoverageSrc.GetProcessState());
-    return 0;
-  }
+  std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle(
+    cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose);
+  std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle(
+    cmsys::SystemTools::Fopen(stderrFile, "w"), fclose);
+  builder.AddCommand(args)
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                       stdoutHandle.get())
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                       stderrHandle.get());
   // since we set the output file names wait for it to end
-  runCoverageSrc.WaitForExit();
+  auto chain = builder.Start();
+  chain.Wait();
   outputFile = stdoutFile;
   return 1;
 }
diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
index af495bb..2c92d77 100644
--- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
+++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.cxx
@@ -2,25 +2,27 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestEmptyBinaryDirectoryCommand.h"
 
-#include <sstream>
-
 #include "cmCTestScriptHandler.h"
-
-class cmExecutionStatus;
+#include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 
 bool cmCTestEmptyBinaryDirectoryCommand::InitialPass(
-  std::vector<std::string> const& args, cmExecutionStatus& /*unused*/)
+  std::vector<std::string> const& args, cmExecutionStatus& status)
 {
   if (args.size() != 1) {
     this->SetError("called with incorrect number of arguments");
     return false;
   }
 
-  if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0])) {
-    std::ostringstream ostr;
-    ostr << "problem removing the binary directory: " << args[0];
-    this->SetError(ostr.str());
-    return false;
+  std::string err;
+  if (!cmCTestScriptHandler::EmptyBinaryDirectory(args[0], err)) {
+    status.GetMakefile().IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Did not remove the binary directory:\n ", args[0],
+               "\nbecause:\n ", err));
+    return true;
   }
 
   return true;
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index 5f8cb74..99c5a2b 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -2,15 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestGIT.h"
 
-#include <cctype>
 #include <cstdio>
 #include <cstdlib>
 #include <ctime>
 #include <utility>
 #include <vector>
 
+#include <cmext/algorithm>
+
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
@@ -18,6 +18,7 @@
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 
 static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
@@ -58,9 +59,9 @@
 std::string cmCTestGIT::GetWorkingRevision()
 {
   // Run plumbing "git rev-list" to get work tree revision.
-  const char* git = this->CommandLineTool.c_str();
-  const char* git_rev_list[] = { git,    "rev-list", "-n",   "1",
-                                 "HEAD", "--",       nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_list = { git, "rev-list", "-n",
+                                            "1", "HEAD",     "--" };
   std::string rev;
   OneLineParser out(this, "rl-out> ", rev);
   OutputLogger err(this->Log, "rl-err> ");
@@ -92,13 +93,13 @@
   std::string git_dir;
 
   // Run "git rev-parse --git-dir" to locate the real .git directory.
-  const char* git = this->CommandLineTool.c_str();
-  char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" };
   std::string git_dir_line;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
-                     cmProcessOutput::UTF8)) {
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err,
+                     std::string{}, cmProcessOutput::UTF8)) {
     git_dir = git_dir_line;
   }
   if (git_dir.empty()) {
@@ -117,11 +118,10 @@
     std::string cygpath_exe =
       cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
     if (cmSystemTools::FileExists(cygpath_exe)) {
-      char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
-                                0 };
+      std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir };
       OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
       OutputLogger cygpath_err(this->Log, "cygpath-err> ");
-      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr,
+      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{},
                          cmProcessOutput::UTF8)) {
         git_dir = git_dir_line;
       }
@@ -136,12 +136,12 @@
   std::string top_dir = this->SourceDirectory;
 
   // Run "git rev-parse --show-cdup" to locate the top of the tree.
-  const char* git = this->CommandLineTool.c_str();
-  char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" };
   std::string cdup;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "",
                      cmProcessOutput::UTF8) &&
       !cdup.empty()) {
     top_dir += "/";
@@ -153,12 +153,12 @@
 
 bool cmCTestGIT::UpdateByFetchAndReset()
 {
-  const char* git = this->CommandLineTool.c_str();
+  std::string git = this->CommandLineTool;
 
   // Use "git fetch" to get remote commits.
-  std::vector<char const*> git_fetch;
+  std::vector<std::string> git_fetch;
   git_fetch.push_back(git);
-  git_fetch.push_back("fetch");
+  git_fetch.emplace_back("fetch");
 
   // Add user-specified update options.
   std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
@@ -166,17 +166,12 @@
     opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    git_fetch.push_back(arg.c_str());
-  }
-
-  // Sentinel argument.
-  git_fetch.push_back(nullptr);
+  cm::append(git_fetch, args);
 
   // Fetch upstream refs.
   OutputLogger fetch_out(this->Log, "fetch-out> ");
   OutputLogger fetch_err(this->Log, "fetch-err> ");
-  if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) {
+  if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) {
     return false;
   }
 
@@ -207,25 +202,22 @@
   }
 
   // Reset the local branch to point at that tracked from upstream.
-  char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr };
+  std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 };
   OutputLogger reset_out(this->Log, "reset-out> ");
   OutputLogger reset_err(this->Log, "reset-err> ");
-  return this->RunChild(&git_reset[0], &reset_out, &reset_err);
+  return this->RunChild(git_reset, &reset_out, &reset_err);
 }
 
 bool cmCTestGIT::UpdateByCustom(std::string const& custom)
 {
   cmList git_custom_command{ custom, cmList::EmptyElements::Yes };
-  std::vector<char const*> git_custom;
-  git_custom.reserve(git_custom_command.size() + 1);
-  for (std::string const& i : git_custom_command) {
-    git_custom.push_back(i.c_str());
-  }
-  git_custom.push_back(nullptr);
+  std::vector<std::string> git_custom;
+  git_custom.reserve(git_custom_command.size());
+  cm::append(git_custom, git_custom_command);
 
   OutputLogger custom_out(this->Log, "custom-out> ");
   OutputLogger custom_err(this->Log, "custom-err> ");
-  return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err);
+  return this->RunUpdateCommand(git_custom, &custom_out, &custom_err);
 }
 
 bool cmCTestGIT::UpdateInternal()
@@ -244,13 +236,14 @@
   }
 
   std::string top_dir = this->FindTopDir();
-  const char* git = this->CommandLineTool.c_str();
-  const char* recursive = "--recursive";
-  const char* sync_recursive = "--recursive";
+  std::string git = this->CommandLineTool;
+  std::string recursive = "--recursive";
+  std::string sync_recursive = "--recursive";
 
   // Git < 1.6.5 did not support submodule --recursive
+  bool support_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
-    recursive = nullptr;
+    support_recursive = false;
     // No need to require >= 1.6.5 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
@@ -258,8 +251,9 @@
   }
 
   // Git < 1.8.1 did not support sync --recursive
+  bool support_sync_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
-    sync_recursive = nullptr;
+    support_sync_recursive = false;
     // No need to require >= 1.8.1 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
@@ -274,35 +268,39 @@
   std::string init_submodules =
     this->CTest->GetCTestConfiguration("GITInitSubmodules");
   if (cmIsOn(init_submodules)) {
-    char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
+    std::vector<std::string> git_submodule_init = { git, "submodule", "init" };
     ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
-                         top_dir.c_str());
+                         top_dir);
 
     if (!ret) {
       return false;
     }
   }
 
-  char const* git_submodule_sync[] = { git, "submodule", "sync",
-                                       sync_recursive, nullptr };
+  std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" };
+  if (support_sync_recursive) {
+    git_submodule_sync.push_back(sync_recursive);
+  }
   ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
-                       top_dir.c_str());
+                       top_dir);
 
   if (!ret) {
     return false;
   }
 
-  char const* git_submodule[] = { git, "submodule", "update", recursive,
-                                  nullptr };
+  std::vector<std::string> git_submodule = { git, "submodule", "update" };
+  if (support_recursive) {
+    git_submodule.push_back(recursive);
+  }
   return this->RunChild(git_submodule, &submodule_out, &submodule_err,
-                        top_dir.c_str());
+                        top_dir);
 }
 
 unsigned int cmCTestGIT::GetGitVersion()
 {
   if (!this->CurrentGitVersion) {
-    const char* git = this->CommandLineTool.c_str();
-    char const* git_version[] = { git, "--version", nullptr };
+    std::string git = this->CommandLineTool;
+    std::vector<std::string> git_version = { git, "--version" };
     std::string version;
     OneLineParser version_out(this, "version-out> ", version);
     OutputLogger version_err(this->Log, "version-err> ");
@@ -415,14 +413,14 @@
 
   const char* ConsumeSpace(const char* c)
   {
-    while (*c && isspace(*c)) {
+    while (*c && cmIsSpace(*c)) {
       ++c;
     }
     return c;
   }
   const char* ConsumeField(const char* c)
   {
-    while (*c && !isspace(*c)) {
+    while (*c && !cmIsSpace(*c)) {
       ++c;
     }
     return c;
@@ -482,7 +480,7 @@
   {
     // Person Name <person@domain.com> 1234567890 +0000
     const char* c = str;
-    while (*c && isspace(*c)) {
+    while (*c && cmIsSpace(*c)) {
       ++c;
     }
 
@@ -491,7 +489,7 @@
       ++c;
     }
     const char* name_last = c;
-    while (name_last != name_first && isspace(*(name_last - 1))) {
+    while (name_last != name_first && cmIsSpace(*(name_last - 1))) {
       --name_last;
     }
     person.Name.assign(name_first, name_last - name_first);
@@ -605,50 +603,49 @@
 {
   // Use 'git rev-list ... | git diff-tree ...' to get revisions.
   std::string range = this->OldRevision + ".." + this->NewRevision;
-  const char* git = this->CommandLineTool.c_str();
-  const char* git_rev_list[] = { git,           "rev-list", "--reverse",
-                                 range.c_str(), "--",       nullptr };
-  const char* git_diff_tree[] = {
-    git,  "diff-tree",    "--stdin",          "--always", "-z",
-    "-r", "--pretty=raw", "--encoding=utf-8", nullptr
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse",
+                                            range, "--" };
+  std::vector<std::string> git_diff_tree = {
+    git,  "diff-tree", "--stdin",      "--always",
+    "-z", "-r",        "--pretty=raw", "--encoding=utf-8"
   };
   this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | "
             << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n";
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_AddCommand(cp, git_rev_list);
-  cmsysProcess_AddCommand(cp, git_diff_tree);
-  cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(git_rev_list)
+    .AddCommand(git_diff_tree)
+    .SetWorkingDirectory(this->SourceDirectory);
 
   CommitParser out(this, "dt-out> ");
   OutputLogger err(this->Log, "dt-err> ");
-  cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8);
+  cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8);
 
   // Send one extra zero-byte to terminate the last record.
   out.Process("", 1);
 
-  cmsysProcess_Delete(cp);
   return true;
 }
 
 bool cmCTestGIT::LoadModifications()
 {
-  const char* git = this->CommandLineTool.c_str();
+  std::string git = this->CommandLineTool;
 
   // Use 'git update-index' to refresh the index w.r.t. the work tree.
-  const char* git_update_index[] = { git, "update-index", "--refresh",
-                                     nullptr };
+  std::vector<std::string> git_update_index = { git, "update-index",
+                                                "--refresh" };
   OutputLogger ui_out(this->Log, "ui-out> ");
   OutputLogger ui_err(this->Log, "ui-err> ");
-  this->RunChild(git_update_index, &ui_out, &ui_err, nullptr,
+  this->RunChild(git_update_index, &ui_out, &ui_err, "",
                  cmProcessOutput::UTF8);
 
   // Use 'git diff-index' to get modified files.
-  const char* git_diff_index[] = { git,    "diff-index", "-z",
-                                   "HEAD", "--",         nullptr };
+  std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD",
+                                              "--" };
   DiffParser out(this, "di-out> ");
   OutputLogger err(this->Log, "di-err> ");
-  this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8);
+  this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8);
 
   for (Change const& c : out.Changes) {
     this->DoModification(PathModified, c.Path);
diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx
index 02837ba..3d56be0 100644
--- a/Source/CTest/cmCTestHG.cxx
+++ b/Source/CTest/cmCTestHG.cxx
@@ -95,8 +95,8 @@
 std::string cmCTestHG::GetWorkingRevision()
 {
   // Run plumbing "hg identify" to get work tree revision.
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hg_identify[] = { hg, "identify", "-i", nullptr };
+  std::string hg = this->CommandLineTool;
+  std::vector<std::string> hg_identify = { hg, "identify", "-i" };
   std::string rev;
   IdentifyParser out(this, "rev-out> ", rev);
   OutputLogger err(this->Log, "rev-err> ");
@@ -127,19 +127,19 @@
 {
   // Use "hg pull" followed by "hg update" to update the working tree.
   {
-    const char* hg = this->CommandLineTool.c_str();
-    const char* hg_pull[] = { hg, "pull", "-v", nullptr };
+    std::string hg = this->CommandLineTool;
+    std::vector<std::string> hg_pull = { hg, "pull", "-v" };
     OutputLogger out(this->Log, "pull-out> ");
     OutputLogger err(this->Log, "pull-err> ");
-    this->RunChild(&hg_pull[0], &out, &err);
+    this->RunChild(hg_pull, &out, &err);
   }
 
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
-  std::vector<char const*> hg_update;
-  hg_update.push_back(this->CommandLineTool.c_str());
-  hg_update.push_back("update");
-  hg_update.push_back("-v");
+  std::vector<std::string> hg_update;
+  hg_update.emplace_back(this->CommandLineTool);
+  hg_update.emplace_back("update");
+  hg_update.emplace_back("-v");
 
   // Add user-specified update options.
   std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
@@ -147,16 +147,11 @@
     opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    hg_update.push_back(arg.c_str());
-  }
-
-  // Sentinel argument.
-  hg_update.push_back(nullptr);
+  cm::append(hg_update, args);
 
   OutputLogger out(this->Log, "update-out> ");
   OutputLogger err(this->Log, "update-err> ");
-  return this->RunUpdateCommand(hg_update.data(), &out, &err);
+  return this->RunUpdateCommand(hg_update, &out, &err);
 }
 
 class cmCTestHG::LogParser
@@ -277,8 +272,8 @@
   // the project has spaces in the path.  Also, they may not have
   // proper XML escapes.
   std::string range = this->OldRevision + ":" + this->NewRevision;
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hgXMLTemplate = "<logentry\n"
+  std::string hg = this->CommandLineTool;
+  std::string hgXMLTemplate = "<logentry\n"
                               "   revision=\"{node|short}\">\n"
                               "  <author>{author|person}</author>\n"
                               "  <email>{author|email}</email>\n"
@@ -288,10 +283,8 @@
                               "  <file_adds>{file_adds}</file_adds>\n"
                               "  <file_dels>{file_dels}</file_dels>\n"
                               "</logentry>\n";
-  const char* hg_log[] = {
-    hg,           "log",         "--removed", "-r", range.c_str(),
-    "--template", hgXMLTemplate, nullptr
-  };
+  std::vector<std::string> hg_log = { hg,    "log",        "--removed",  "-r",
+                                      range, "--template", hgXMLTemplate };
 
   LogParser out(this, "log-out> ");
   out.Process("<?xml version=\"1.0\"?>\n"
@@ -305,8 +298,8 @@
 bool cmCTestHG::LoadModifications()
 {
   // Use 'hg status' to get modified files.
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hg_status[] = { hg, "status", nullptr };
+  std::string hg = this->CommandLineTool;
+  std::vector<std::string> hg_status = { hg, "status" };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(hg_status, &out, &err);
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 4a33869..9669d76 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -2,11 +2,15 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunch.h"
 
+#include <cstdio>
 #include <cstring>
 #include <iostream>
+#include <memory>
+#include <utility>
+
+#include <cm3p/uv.h>
 
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTestLaunchReporter.h"
@@ -17,6 +21,9 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmake.h"
 
 #ifdef _WIN32
@@ -28,8 +35,6 @@
 
 cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
 {
-  this->Process = nullptr;
-
   if (!this->ParseArguments(argc, argv)) {
     return;
   }
@@ -40,13 +45,9 @@
   this->ScrapeRulesLoaded = false;
   this->HaveOut = false;
   this->HaveErr = false;
-  this->Process = cmsysProcess_New();
 }
 
-cmCTestLaunch::~cmCTestLaunch()
-{
-  cmsysProcess_Delete(this->Process);
-}
+cmCTestLaunch::~cmCTestLaunch() = default;
 
 bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
 {
@@ -113,15 +114,12 @@
 
   // Extract the real command line.
   if (arg0) {
-    this->RealArgC = argc - arg0;
-    this->RealArgV = argv + arg0;
-    for (int i = 0; i < this->RealArgC; ++i) {
-      this->HandleRealArg(this->RealArgV[i]);
+    for (int i = 0; i < argc - arg0; ++i) {
+      this->RealArgV.emplace_back((argv + arg0)[i]);
+      this->HandleRealArg((argv + arg0)[i]);
     }
     return true;
   }
-  this->RealArgC = 0;
-  this->RealArgV = nullptr;
   std::cerr << "No launch/command separator ('--') found!\n";
   return false;
 }
@@ -151,17 +149,19 @@
   }
 
   // Prepare to run the real command.
-  cmsysProcess* cp = this->Process;
-  cmsysProcess_SetCommand(cp, this->RealArgV);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(this->RealArgV);
 
   cmsys::ofstream fout;
   cmsys::ofstream ferr;
   if (this->Reporter.Passthru) {
     // In passthru mode we just share the output pipes.
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
   } else {
     // In full mode we record the child output pipes to log files.
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
     fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary);
     ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
   }
@@ -174,51 +174,65 @@
 #endif
 
   // Run the real command.
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
 
   // Record child stdout and stderr if necessary.
+  cm::uv_pipe_ptr outPipe;
+  cm::uv_pipe_ptr errPipe;
+  bool outFinished = true;
+  bool errFinished = true;
+  cmProcessOutput processOutput;
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
   if (!this->Reporter.Passthru) {
-    char* data = nullptr;
-    int length = 0;
-    cmProcessOutput processOutput;
-    std::string strdata;
-    while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-      if (p == cmsysProcess_Pipe_STDOUT) {
-        processOutput.DecodeText(data, length, strdata, 1);
-        fout.write(strdata.c_str(), strdata.size());
-        std::cout.write(strdata.c_str(), strdata.size());
-        this->HaveOut = true;
-      } else if (p == cmsysProcess_Pipe_STDERR) {
-        processOutput.DecodeText(data, length, strdata, 2);
-        ferr.write(strdata.c_str(), strdata.size());
-        std::cerr.write(strdata.c_str(), strdata.size());
-        this->HaveErr = true;
-      }
-    }
-    processOutput.DecodeText(std::string(), strdata, 1);
-    if (!strdata.empty()) {
-      fout.write(strdata.c_str(), strdata.size());
-      std::cout.write(strdata.c_str(), strdata.size());
-    }
-    processOutput.DecodeText(std::string(), strdata, 2);
-    if (!strdata.empty()) {
-      ferr.write(strdata.c_str(), strdata.size());
-      std::cerr.write(strdata.c_str(), strdata.size());
-    }
+    auto beginRead = [&chain, &processOutput](
+                       cm::uv_pipe_ptr& pipe, int stream, std::ostream& out,
+                       cmsys::ofstream& file, bool& haveData, bool& finished,
+                       int id) -> std::unique_ptr<cmUVStreamReadHandle> {
+      pipe.init(chain.GetLoop(), 0);
+      uv_pipe_open(pipe, stream);
+      finished = false;
+      return cmUVStreamRead(
+        pipe,
+        [&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
+          std::string strdata;
+          processOutput.DecodeText(data.data(), data.size(), strdata, id);
+          file.write(strdata.c_str(), strdata.size());
+          out.write(strdata.c_str(), strdata.size());
+          haveData = true;
+        },
+        [&processOutput, &out, &file, &finished, id]() {
+          std::string strdata;
+          processOutput.DecodeText(std::string(), strdata, id);
+          if (!strdata.empty()) {
+            file.write(strdata.c_str(), strdata.size());
+            out.write(strdata.c_str(), strdata.size());
+          }
+          finished = true;
+        });
+    };
+    outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout,
+                             this->HaveOut, outFinished, 1);
+    errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr,
+                            this->HaveErr, errFinished, 2);
   }
 
   // Wait for the real command to finish.
-  cmsysProcess_WaitForExit(cp, nullptr);
-  this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp);
+  while (!(chain.Finished() && outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
+  this->Reporter.Status = chain.GetStatus(0);
+  if (this->Reporter.Status.GetException().first ==
+      cmUVProcessChain::ExceptionCode::Spawn) {
+    this->Reporter.ExitCode = 1;
+  } else {
+    this->Reporter.ExitCode =
+      static_cast<int>(this->Reporter.Status.ExitStatus);
+  }
 }
 
 int cmCTestLaunch::Run()
 {
-  if (!this->Process) {
-    std::cerr << "Could not allocate cmsysProcess instance!\n";
-    return -1;
-  }
-
   this->RunChild();
 
   if (this->CheckResults()) {
@@ -226,7 +240,6 @@
   }
 
   this->LoadConfig();
-  this->Reporter.Process = this->Process;
   this->Reporter.WriteXML();
 
   return this->Reporter.ExitCode;
diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h
index c5a6476..ef21a26 100644
--- a/Source/CTest/cmCTestLaunch.h
+++ b/Source/CTest/cmCTestLaunch.h
@@ -43,15 +43,12 @@
   bool ParseArguments(int argc, const char* const* argv);
 
   // The real command line appearing after launcher arguments.
-  int RealArgC;
-  const char* const* RealArgV;
+  std::vector<std::string> RealArgV;
 
   // The real command line after response file expansion.
   std::vector<std::string> RealArgs;
   void HandleRealArg(const char* arg);
 
-  struct cmsysProcess_s* Process;
-
   // Whether or not any data have been written to stdout or stderr.
   bool HaveOut;
   bool HaveErr;
diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx
index 149ba5d..4b4e5c5 100644
--- a/Source/CTest/cmCTestLaunchReporter.cxx
+++ b/Source/CTest/cmCTestLaunchReporter.cxx
@@ -2,8 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunchReporter.h"
 
+#include <utility>
+
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCryptoHash.h"
@@ -22,6 +23,7 @@
 cmCTestLaunchReporter::cmCTestLaunchReporter()
 {
   this->Passthru = true;
+  this->Status.Finished = true;
   this->ExitCode = 1;
   this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
 
@@ -231,35 +233,23 @@
 
   // ExitCondition
   cmXMLElement e4(e3, "ExitCondition");
-  cmsysProcess* cp = this->Process;
-  switch (cmsysProcess_GetState(cp)) {
-    case cmsysProcess_State_Starting:
-      e4.Content("No process has been executed");
-      break;
-    case cmsysProcess_State_Executing:
-      e4.Content("The process is still executing");
-      break;
-    case cmsysProcess_State_Disowned:
-      e4.Content("Disowned");
-      break;
-    case cmsysProcess_State_Killed:
-      e4.Content("Killed by parent");
-      break;
-
-    case cmsysProcess_State_Expired:
-      e4.Content("Killed when timeout expired");
-      break;
-    case cmsysProcess_State_Exited:
-      e4.Content(this->ExitCode);
-      break;
-    case cmsysProcess_State_Exception:
-      e4.Content("Terminated abnormally: ");
-      e4.Content(cmsysProcess_GetExceptionString(cp));
-      break;
-    case cmsysProcess_State_Error:
-      e4.Content("Error administrating child process: ");
-      e4.Content(cmsysProcess_GetErrorString(cp));
-      break;
+  if (this->Status.Finished) {
+    auto exception = this->Status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        e4.Content(this->ExitCode);
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        e4.Content("Error administrating child process: ");
+        e4.Content(exception.second);
+        break;
+      default:
+        e4.Content("Terminated abnormally: ");
+        e4.Content(exception.second);
+        break;
+    }
+  } else {
+    e4.Content("Killed when timeout expired");
   }
 }
 
diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h
index 4be0d9b..2bb78f8 100644
--- a/Source/CTest/cmCTestLaunchReporter.h
+++ b/Source/CTest/cmCTestLaunchReporter.h
@@ -10,6 +10,8 @@
 
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmUVProcessChain.h"
+
 class cmXMLElement;
 
 /** \class cmCTestLaunchReporter
@@ -48,7 +50,7 @@
   void ComputeFileNames();
 
   bool Passthru;
-  struct cmsysProcess_s* Process;
+  cmUVProcessChain::Status Status;
   int ExitCode;
 
   // Temporary log files for stdout and stderr of real command.
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index ca07a08..34f9d43 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -40,7 +40,7 @@
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
+#include "cmUVJobServerClient.h"
 #include "cmWorkingDirectory.h"
 
 namespace cmsys {
@@ -83,27 +83,23 @@
 cmCTestMultiProcessHandler::~cmCTestMultiProcessHandler() = default;
 
 // Set the tests
-void cmCTestMultiProcessHandler::SetTests(TestMap& tests,
-                                          PropertiesMap& properties)
+bool cmCTestMultiProcessHandler::SetTests(TestMap tests,
+                                          PropertiesMap properties)
 {
-  this->Tests = tests;
-  this->Properties = properties;
-  this->Total = this->Tests.size();
-  // set test run map to false for all
-  for (auto const& t : this->Tests) {
-    this->TestRunningMap[t.first] = false;
-    this->TestFinishMap[t.first] = false;
-  }
+  this->PendingTests = std::move(tests);
+  this->Properties = std::move(properties);
+  this->Total = this->PendingTests.size();
   if (!this->CTest->GetShowOnly()) {
     this->ReadCostData();
     this->HasCycles = !this->CheckCycles();
     this->HasInvalidGeneratedResourceSpec =
       !this->CheckGeneratedResourceSpec();
     if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
-      return;
+      return false;
     }
     this->CreateTestCostList();
   }
+  return true;
 }
 
 // Set the max number of tests that can be run at the same time.
@@ -126,25 +122,50 @@
   }
 }
 
+bool cmCTestMultiProcessHandler::Complete()
+{
+  return this->Completed == this->Total;
+}
+
+void cmCTestMultiProcessHandler::InitializeLoop()
+{
+  this->Loop.init();
+  this->StartNextTestsOnIdle_.init(*this->Loop, this);
+  this->StartNextTestsOnTimer_.init(*this->Loop, this);
+
+  this->JobServerClient = cmUVJobServerClient::Connect(
+    *this->Loop, /*onToken=*/[this]() { this->JobServerReceivedToken(); },
+    /*onDisconnect=*/nullptr);
+  if (this->JobServerClient) {
+    cmCTestLog(this->CTest, OUTPUT,
+               "Connected to MAKE jobserver" << std::endl);
+  }
+}
+
+void cmCTestMultiProcessHandler::FinalizeLoop()
+{
+  this->JobServerClient.reset();
+  this->StartNextTestsOnTimer_.reset();
+  this->StartNextTestsOnIdle_.reset();
+  this->Loop.reset();
+}
+
 void cmCTestMultiProcessHandler::RunTests()
 {
   this->CheckResume();
   if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
     return;
   }
-#ifdef CMAKE_UV_SIGNAL_HACK
-  cmUVSignalHackRAII hackRAII;
-#endif
   this->TestHandler->SetMaxIndex(this->FindMaxIndex());
 
-  uv_loop_init(&this->Loop);
-  this->StartNextTests();
-  uv_run(&this->Loop, UV_RUN_DEFAULT);
-  uv_loop_close(&this->Loop);
+  this->InitializeLoop();
+  this->StartNextTestsOnIdle();
+  uv_run(this->Loop, UV_RUN_DEFAULT);
+  this->FinalizeLoop();
 
   if (!this->StopTimePassed && !this->CheckStopOnFailure()) {
-    assert(this->Completed == this->Total);
-    assert(this->Tests.empty());
+    assert(this->Complete());
+    assert(this->PendingTests.empty());
   }
   assert(this->AllResourcesAvailable());
 
@@ -152,38 +173,17 @@
   this->UpdateCostData();
 }
 
-bool cmCTestMultiProcessHandler::StartTestProcess(int test)
+void cmCTestMultiProcessHandler::StartTestProcess(int test)
 {
-  if (this->HaveAffinity && this->Properties[test]->WantAffinity) {
-    size_t needProcessors = this->GetProcessorsUsed(test);
-    if (needProcessors > this->ProcessorsAvailable.size()) {
-      return false;
-    }
-    std::vector<size_t> affinity;
-    affinity.reserve(needProcessors);
-    for (size_t i = 0; i < needProcessors; ++i) {
-      auto p = this->ProcessorsAvailable.begin();
-      affinity.push_back(*p);
-      this->ProcessorsAvailable.erase(p);
-    }
-    this->Properties[test]->Affinity = std::move(affinity);
-  }
-
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "test " << test << "\n", this->Quiet);
-  this->TestRunningMap[test] = true; // mark the test as running
-  // now remove the test itself
-  this->EraseTest(test);
-  this->RunningCount += this->GetProcessorsUsed(test);
 
-  auto testRun = cm::make_unique<cmCTestRunTest>(*this);
+  auto testRun = cm::make_unique<cmCTestRunTest>(*this, test);
 
   if (this->RepeatMode != cmCTest::Repeat::Never) {
     testRun->SetRepeatMode(this->RepeatMode);
     testRun->SetNumberOfRuns(this->RepeatCount);
   }
-  testRun->SetIndex(test);
-  testRun->SetTestProperties(this->Properties[test]);
   if (this->UseResourceSpec) {
     testRun->SetUseAllocatedResources(true);
     testRun->SetAllocatedResources(this->AllocatedResources[test]);
@@ -197,22 +197,18 @@
     }
   }
 
-  // Always lock the resources we'll be using, even if we fail to set the
-  // working directory because FinishTestProcess() will try to unlock them
-  this->LockResources(test);
-
-  if (!this->ResourceAllocationErrors[test].empty()) {
+  if (!this->ResourceAvailabilityErrors[test].empty()) {
     std::ostringstream e;
     e << "Insufficient resources for test " << this->Properties[test]->Name
       << ":\n\n";
-    for (auto const& it : this->ResourceAllocationErrors[test]) {
+    for (auto const& it : this->ResourceAvailabilityErrors[test]) {
       switch (it.second) {
-        case ResourceAllocationError::NoResourceType:
+        case ResourceAvailabilityError::NoResourceType:
           e << "  Test requested resources of type '" << it.first
             << "' which does not exist\n";
           break;
 
-        case ResourceAllocationError::InsufficientResources:
+        case ResourceAvailabilityError::InsufficientResources:
           e << "  Test requested resources of type '" << it.first
             << "' in the following amounts:\n";
           for (auto const& group : this->Properties[test]->ResourceGroups) {
@@ -236,7 +232,7 @@
     e << "Resource spec file:\n\n  " << this->ResourceSpecFile;
     cmCTestRunTest::StartFailure(std::move(testRun), this->Total, e.str(),
                                  "Insufficient resources");
-    return false;
+    return;
   }
 
   cmWorkingDirectory workdir(this->Properties[test]->Directory);
@@ -246,13 +242,12 @@
                                    this->Properties[test]->Directory + " : " +
                                    std::strerror(workdir.GetLastResult()),
                                  "Failed to change working directory");
-    return false;
+    return;
   }
 
   // Ownership of 'testRun' has moved to another structure.
   // When the test finishes, FinishTestProcess will be called.
-  return cmCTestRunTest::StartTest(std::move(testRun), this->Completed,
-                                   this->Total);
+  cmCTestRunTest::StartTest(std::move(testRun), this->Completed, this->Total);
 }
 
 bool cmCTestMultiProcessHandler::AllocateResources(int index)
@@ -261,6 +256,12 @@
     return true;
   }
 
+  // If the test needs unavailable resources then do not allocate anything
+  // because it will never run.  We will issue the recorded errors instead.
+  if (!this->ResourceAvailabilityErrors[index].empty()) {
+    return true;
+  }
+
   std::map<std::string, std::vector<cmCTestBinPackerAllocation>> allocations;
   if (!this->TryAllocateResources(index, allocations)) {
     return false;
@@ -285,7 +286,7 @@
 bool cmCTestMultiProcessHandler::TryAllocateResources(
   int index,
   std::map<std::string, std::vector<cmCTestBinPackerAllocation>>& allocations,
-  std::map<std::string, ResourceAllocationError>* errors)
+  std::map<std::string, ResourceAvailabilityError>* errors)
 {
   allocations.clear();
 
@@ -305,7 +306,7 @@
   for (auto& it : allocations) {
     if (!availableResources.count(it.first)) {
       if (errors) {
-        (*errors)[it.first] = ResourceAllocationError::NoResourceType;
+        (*errors)[it.first] = ResourceAvailabilityError::NoResourceType;
         result = false;
       } else {
         return false;
@@ -313,7 +314,7 @@
     } else if (!cmAllocateCTestResourcesRoundRobin(
                  availableResources.at(it.first), it.second)) {
       if (errors) {
-        (*errors)[it.first] = ResourceAllocationError::InsufficientResources;
+        (*errors)[it.first] = ResourceAvailabilityError::InsufficientResources;
         result = false;
       } else {
         return false;
@@ -360,14 +361,14 @@
   return true;
 }
 
-void cmCTestMultiProcessHandler::CheckResourcesAvailable()
+void cmCTestMultiProcessHandler::CheckResourceAvailability()
 {
   if (this->UseResourceSpec) {
-    for (auto test : this->SortedTests) {
+    for (auto const& t : this->PendingTests) {
       std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
         allocations;
-      this->TryAllocateResources(test, allocations,
-                                 &this->ResourceAllocationErrors[test]);
+      this->TryAllocateResources(t.first, allocations,
+                                 &this->ResourceAvailabilityErrors[t.first]);
     }
   }
 }
@@ -403,30 +404,49 @@
 
 void cmCTestMultiProcessHandler::LockResources(int index)
 {
-  this->LockedResources.insert(
-    this->Properties[index]->LockedResources.begin(),
-    this->Properties[index]->LockedResources.end());
+  this->RunningCount += this->GetProcessorsUsed(index);
 
-  if (this->Properties[index]->RunSerial) {
+  auto* properties = this->Properties[index];
+
+  this->ProjectResourcesLocked.insert(properties->ProjectResources.begin(),
+                                      properties->ProjectResources.end());
+
+  if (properties->RunSerial) {
     this->SerialTestRunning = true;
   }
+
+  if (this->HaveAffinity && properties->WantAffinity) {
+    size_t needProcessors = this->GetProcessorsUsed(index);
+    assert(needProcessors <= this->ProcessorsAvailable.size());
+    std::vector<size_t> affinity;
+    affinity.reserve(needProcessors);
+    for (size_t i = 0; i < needProcessors; ++i) {
+      auto p = this->ProcessorsAvailable.begin();
+      affinity.push_back(*p);
+      this->ProcessorsAvailable.erase(p);
+    }
+    properties->Affinity = std::move(affinity);
+  }
 }
 
 void cmCTestMultiProcessHandler::UnlockResources(int index)
 {
-  for (std::string const& i : this->Properties[index]->LockedResources) {
-    this->LockedResources.erase(i);
+  auto* properties = this->Properties[index];
+
+  for (auto p : properties->Affinity) {
+    this->ProcessorsAvailable.insert(p);
   }
-  if (this->Properties[index]->RunSerial) {
+  properties->Affinity.clear();
+
+  for (std::string const& i : properties->ProjectResources) {
+    this->ProjectResourcesLocked.erase(i);
+  }
+
+  if (properties->RunSerial) {
     this->SerialTestRunning = false;
   }
-}
 
-void cmCTestMultiProcessHandler::EraseTest(int test)
-{
-  this->Tests.erase(test);
-  this->SortedTests.erase(
-    std::find(this->SortedTests.begin(), this->SortedTests.end(), test));
+  this->RunningCount -= this->GetProcessorsUsed(index);
 }
 
 inline size_t cmCTestMultiProcessHandler::GetProcessorsUsed(int test)
@@ -450,50 +470,40 @@
   return this->Properties[test]->Name;
 }
 
-bool cmCTestMultiProcessHandler::StartTest(int test)
+void cmCTestMultiProcessHandler::StartTest(int test)
 {
-  // Check for locked resources
-  for (std::string const& i : this->Properties[test]->LockedResources) {
-    if (cm::contains(this->LockedResources, i)) {
-      return false;
-    }
+  if (this->JobServerClient) {
+    // There is a job server.  Request a token and queue the test to run
+    // when a token is received.  Note that if we do not get a token right
+    // away it's possible that the system load will be higher when the
+    // token is received and we may violate the test-load limit.  However,
+    // this is unlikely because if we do not get a token right away, some
+    // other job that's currently running must finish before we get one.
+    this->JobServerClient->RequestToken();
+    this->JobServerQueuedTests.emplace_back(test);
+  } else {
+    // There is no job server.  Start the test now.
+    this->StartTestProcess(test);
   }
+}
 
-  // Allocate resources
-  if (this->ResourceAllocationErrors[test].empty() &&
-      !this->AllocateResources(test)) {
-    this->DeallocateResources(test);
-    return false;
-  }
-
-  // if there are no depends left then run this test
-  if (this->Tests[test].empty()) {
-    return this->StartTestProcess(test);
-  }
-  // This test was not able to start because it is waiting
-  // on depends to run
-  this->DeallocateResources(test);
-  return false;
+void cmCTestMultiProcessHandler::JobServerReceivedToken()
+{
+  assert(!this->JobServerQueuedTests.empty());
+  int test = this->JobServerQueuedTests.front();
+  this->JobServerQueuedTests.pop_front();
+  this->StartTestProcess(test);
 }
 
 void cmCTestMultiProcessHandler::StartNextTests()
 {
-  if (this->TestLoadRetryTimer.get() != nullptr) {
-    // This timer may be waiting to call StartNextTests again.
-    // Since we have been called it is no longer needed.
-    uv_timer_stop(this->TestLoadRetryTimer);
-  }
+  // One or more events may be scheduled to call this method again.
+  // Since this method has been called they are no longer needed.
+  this->StartNextTestsOnIdle_.stop();
+  this->StartNextTestsOnTimer_.stop();
 
-  if (this->Tests.empty()) {
-    this->TestLoadRetryTimer.reset();
-    return;
-  }
-
-  if (this->CheckStopTimePassed()) {
-    return;
-  }
-
-  if (this->CheckStopOnFailure() && !this->Failed->empty()) {
+  if (this->PendingTests.empty() || this->CheckStopTimePassed() ||
+      (this->CheckStopOnFailure() && !this->Failed->empty())) {
     return;
   }
 
@@ -545,50 +555,79 @@
     }
   }
 
-  TestList copy = this->SortedTests;
-  for (auto const& test : copy) {
-    // Take a nap if we're currently performing a RUN_SERIAL test.
-    if (this->SerialTestRunning) {
-      break;
-    }
+  // Start tests in the preferred order, each subject to readiness checks.
+  auto ti = this->OrderedTests.begin();
+  while (numToStart > 0 && !this->SerialTestRunning &&
+         ti != this->OrderedTests.end()) {
+    // Increment the test iterator now because the current list
+    // entry may be deleted below.
+    auto cti = ti++;
+    int test = *cti;
+
     // We can only start a RUN_SERIAL test if no other tests are also
     // running.
     if (this->Properties[test]->RunSerial && this->RunningCount > 0) {
       continue;
     }
 
+    // Exclude tests that depend on unfinished tests.
+    if (!this->PendingTests[test].Depends.empty()) {
+      continue;
+    }
+
     size_t processors = this->GetProcessorsUsed(test);
-    bool testLoadOk = true;
     if (this->TestLoad > 0) {
-      if (processors <= spareLoad) {
-        cmCTestLog(this->CTest, DEBUG,
-                   "OK to run " << this->GetName(test) << ", it requires "
-                                << processors << " procs & system load is: "
-                                << systemLoad << std::endl);
-        allTestsFailedTestLoadCheck = false;
-      } else {
-        testLoadOk = false;
+      // Exclude tests that are too big to fit in the spare load.
+      if (processors > spareLoad) {
+        // Keep track of the smallest excluded test to report in message below.
+        if (processors <= minProcessorsRequired) {
+          minProcessorsRequired = processors;
+          testWithMinProcessors = this->GetName(test);
+        }
+        continue;
+      }
+
+      // We found a test that fits in the spare load.
+      allTestsFailedTestLoadCheck = false;
+      cmCTestLog(this->CTest, DEBUG,
+                 "OK to run "
+                   << this->GetName(test) << ", it requires " << processors
+                   << " procs & system load is: " << systemLoad << std::endl);
+    }
+
+    // Exclude tests that are too big to fit in the concurrency limit.
+    if (processors > numToStart) {
+      continue;
+    }
+
+    // Exclude tests that depend on currently-locked project resources.
+    for (std::string const& i : this->Properties[test]->ProjectResources) {
+      if (cm::contains(this->ProjectResourcesLocked, i)) {
+        continue;
       }
     }
 
-    if (processors <= minProcessorsRequired) {
-      minProcessorsRequired = processors;
-      testWithMinProcessors = this->GetName(test);
+    // Allocate system resources needed by this test.
+    if (!this->AllocateResources(test)) {
+      continue;
     }
 
-    if (testLoadOk && processors <= numToStart && this->StartTest(test)) {
-      numToStart -= processors;
-    } else if (numToStart == 0) {
-      break;
-    }
+    // Lock resources needed by this test.
+    this->LockResources(test);
+
+    // The test is ready to run.
+    numToStart -= processors;
+    this->OrderedTests.erase(cti);
+    this->PendingTests.erase(test);
+    this->StartTest(test);
   }
 
   if (allTestsFailedTestLoadCheck) {
     // Find out whether there are any non RUN_SERIAL tests left, so that the
     // correct warning may be displayed.
     bool onlyRunSerialTestsLeft = true;
-    for (auto const& test : copy) {
-      if (!this->Properties[test]->RunSerial) {
+    for (auto const& t : this->PendingTests) {
+      if (!this->Properties[t.first]->RunSerial) {
         onlyRunSerialTestsLeft = false;
       }
     }
@@ -600,7 +639,7 @@
     } else if (onlyRunSerialTestsLeft) {
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                  "Only RUN_SERIAL tests remain, awaiting available slot.");
-    } else {
+    } else if (!testWithMinProcessors.empty()) {
       /* clang-format off */
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                  "System Load: " << systemLoad << ", "
@@ -608,26 +647,43 @@
                  "Smallest test " << testWithMinProcessors <<
                  " requires " << minProcessorsRequired);
       /* clang-format on */
+    } else {
+      /* clang-format off */
+      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                 "System Load: " << systemLoad << ", "
+                 "Max Allowed Load: " << this->TestLoad);
+      /* clang-format on */
     }
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "*****" << std::endl);
 
-    // Wait between 1 and 5 seconds before trying again.
-    unsigned int milliseconds = (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
-    if (this->FakeLoadForTesting) {
-      milliseconds = 10;
-    }
-    if (this->TestLoadRetryTimer.get() == nullptr) {
-      this->TestLoadRetryTimer.init(this->Loop, this);
-    }
-    this->TestLoadRetryTimer.start(
-      &cmCTestMultiProcessHandler::OnTestLoadRetryCB, milliseconds, 0);
+    // Try again later when the load might be lower.
+    this->StartNextTestsOnTimer();
   }
 }
 
-void cmCTestMultiProcessHandler::OnTestLoadRetryCB(uv_timer_t* timer)
+void cmCTestMultiProcessHandler::StartNextTestsOnIdle()
 {
-  auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
-  self->StartNextTests();
+  // Start more tests on the next loop iteration.
+  this->StartNextTestsOnIdle_.start([](uv_idle_t* idle) {
+    uv_idle_stop(idle);
+    auto* self = static_cast<cmCTestMultiProcessHandler*>(idle->data);
+    self->StartNextTests();
+  });
+}
+
+void cmCTestMultiProcessHandler::StartNextTestsOnTimer()
+{
+  // Wait between 1 and 5 seconds before trying again.
+  unsigned int const milliseconds = this->FakeLoadForTesting
+    ? 10
+    : (cmSystemTools::RandomSeed() % 5 + 1) * 1000;
+  this->StartNextTestsOnTimer_.start(
+    [](uv_timer_t* timer) {
+      uv_timer_stop(timer);
+      auto* self = static_cast<cmCTestMultiProcessHandler*>(timer->data);
+      self->StartNextTests();
+    },
+    milliseconds, 0);
 }
 
 void cmCTestMultiProcessHandler::FinishTestProcess(
@@ -657,26 +713,20 @@
     this->Failed->push_back(properties->Name);
   }
 
-  for (auto& t : this->Tests) {
-    t.second.erase(test);
+  for (auto& t : this->PendingTests) {
+    t.second.Depends.erase(test);
   }
 
-  this->TestFinishMap[test] = true;
-  this->TestRunningMap[test] = false;
   this->WriteCheckpoint(test);
   this->DeallocateResources(test);
   this->UnlockResources(test);
-  this->RunningCount -= this->GetProcessorsUsed(test);
-
-  for (auto p : properties->Affinity) {
-    this->ProcessorsAvailable.insert(p);
-  }
-  properties->Affinity.clear();
 
   runner.reset();
-  if (started) {
-    this->StartNextTests();
+
+  if (this->JobServerClient) {
+    this->JobServerClient->ReleaseToken();
   }
+  this->StartNextTestsOnIdle();
 }
 
 void cmCTestMultiProcessHandler::UpdateCostData()
@@ -807,7 +857,7 @@
 
 void cmCTestMultiProcessHandler::CreateParallelTestCostList()
 {
-  TestSet alreadySortedTests;
+  TestSet alreadyOrderedTests;
 
   std::list<TestSet> priorityStack;
   priorityStack.emplace_back();
@@ -815,11 +865,11 @@
 
   // In parallel test runs add previously failed tests to the front
   // of the cost list and queue other tests for further sorting
-  for (auto const& t : this->Tests) {
+  for (auto const& t : this->PendingTests) {
     if (cm::contains(this->LastTestsFailed, this->Properties[t.first]->Name)) {
       // If the test failed last time, it should be run first.
-      this->SortedTests.push_back(t.first);
-      alreadySortedTests.insert(t.first);
+      this->OrderedTests.push_back(t.first);
+      alreadyOrderedTests.insert(t.first);
     } else {
       topLevel.insert(t.first);
     }
@@ -834,7 +884,7 @@
     TestSet& currentSet = priorityStack.back();
 
     for (auto const& i : previousSet) {
-      TestSet const& dependencies = this->Tests[i];
+      TestSet const& dependencies = this->PendingTests[i].Depends;
       currentSet.insert(dependencies.begin(), dependencies.end());
     }
 
@@ -855,9 +905,9 @@
                      TestComparator(this));
 
     for (auto const& j : sortedCopy) {
-      if (!cm::contains(alreadySortedTests, j)) {
-        this->SortedTests.push_back(j);
-        alreadySortedTests.insert(j);
+      if (!cm::contains(alreadyOrderedTests, j)) {
+        this->OrderedTests.push_back(j);
+        alreadyOrderedTests.insert(j);
       }
     }
   }
@@ -866,7 +916,7 @@
 void cmCTestMultiProcessHandler::GetAllTestDependencies(int test,
                                                         TestList& dependencies)
 {
-  TestSet const& dependencySet = this->Tests[test];
+  TestSet const& dependencySet = this->PendingTests[test].Depends;
   for (int i : dependencySet) {
     this->GetAllTestDependencies(i, dependencies);
     dependencies.push_back(i);
@@ -877,17 +927,17 @@
 {
   TestList presortedList;
 
-  for (auto const& i : this->Tests) {
+  for (auto const& i : this->PendingTests) {
     presortedList.push_back(i.first);
   }
 
   std::stable_sort(presortedList.begin(), presortedList.end(),
                    TestComparator(this));
 
-  TestSet alreadySortedTests;
+  TestSet alreadyOrderedTests;
 
   for (int test : presortedList) {
-    if (cm::contains(alreadySortedTests, test)) {
+    if (cm::contains(alreadyOrderedTests, test)) {
       continue;
     }
 
@@ -895,14 +945,14 @@
     this->GetAllTestDependencies(test, dependencies);
 
     for (int testDependency : dependencies) {
-      if (!cm::contains(alreadySortedTests, testDependency)) {
-        alreadySortedTests.insert(testDependency);
-        this->SortedTests.push_back(testDependency);
+      if (!cm::contains(alreadyOrderedTests, testDependency)) {
+        alreadyOrderedTests.insert(testDependency);
+        this->OrderedTests.push_back(testDependency);
       }
     }
 
-    alreadySortedTests.insert(test);
-    this->SortedTests.push_back(test);
+    alreadyOrderedTests.insert(test);
+    this->OrderedTests.push_back(test);
   }
 }
 
@@ -1089,9 +1139,9 @@
     properties.append(DumpCTestProperty(
       "REQUIRED_FILES", DumpToJsonArray(testProperties.RequiredFiles)));
   }
-  if (!testProperties.LockedResources.empty()) {
+  if (!testProperties.ProjectResources.empty()) {
     properties.append(DumpCTestProperty(
-      "RESOURCE_LOCK", DumpToJsonArray(testProperties.LockedResources)));
+      "RESOURCE_LOCK", DumpToJsonArray(testProperties.ProjectResources)));
   }
   if (testProperties.RunSerial) {
     properties.append(
@@ -1259,9 +1309,7 @@
     // Don't worry if this fails, we are only showing the test list, not
     // running the tests
     cmWorkingDirectory workdir(p.Directory);
-    cmCTestRunTest testRun(*this);
-    testRun.SetIndex(p.Index);
-    testRun.SetTestProperties(&p);
+    cmCTestRunTest testRun(*this, p.Index);
     testRun.ComputeArguments();
 
     // Skip tests not available in this configuration.
@@ -1298,9 +1346,7 @@
     // running the tests
     cmWorkingDirectory workdir(p.Directory);
 
-    cmCTestRunTest testRun(*this);
-    testRun.SetIndex(p.Index);
-    testRun.SetTestProperties(&p);
+    cmCTestRunTest testRun(*this, p.Index);
     testRun.ComputeArguments(); // logs the command in verbose mode
 
     if (!p.Labels.empty()) // print the labels
@@ -1394,17 +1440,17 @@
 
 void cmCTestMultiProcessHandler::RemoveTest(int index)
 {
-  this->EraseTest(index);
+  this->OrderedTests.erase(
+    std::find(this->OrderedTests.begin(), this->OrderedTests.end(), index));
+  this->PendingTests.erase(index);
   this->Properties.erase(index);
-  this->TestRunningMap[index] = false;
-  this->TestFinishMap[index] = true;
   this->Completed++;
 }
 
 int cmCTestMultiProcessHandler::FindMaxIndex()
 {
   int max = 0;
-  for (auto const& i : this->Tests) {
+  for (auto const& i : this->PendingTests) {
     if (i.first > max) {
       max = i.first;
     }
@@ -1418,7 +1464,7 @@
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Checking test dependency graph..." << std::endl,
                      this->Quiet);
-  for (auto const& it : this->Tests) {
+  for (auto const& it : this->PendingTests) {
     // DFS from each element to itself
     int root = it.first;
     std::set<int> visited;
@@ -1428,7 +1474,7 @@
       int test = s.top();
       s.pop();
       if (visited.insert(test).second) {
-        for (auto const& d : this->Tests[test]) {
+        for (auto const& d : this->PendingTests[test].Depends) {
           if (d == root) {
             // cycle exists
             cmCTestLog(
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index 3b4e9c5..d66c348 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -5,6 +5,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <cstddef>
+#include <list>
 #include <map>
 #include <memory>
 #include <set>
@@ -13,13 +14,12 @@
 
 #include <cm/optional>
 
-#include <cm3p/uv.h>
-
 #include "cmCTest.h"
 #include "cmCTestResourceAllocator.h"
 #include "cmCTestResourceSpec.h"
 #include "cmCTestTestHandler.h"
 #include "cmUVHandlePtr.h"
+#include "cmUVJobServerClient.h"
 
 struct cmCTestBinPackerAllocation;
 class cmCTestRunTest;
@@ -38,7 +38,11 @@
   struct TestSet : public std::set<int>
   {
   };
-  struct TestMap : public std::map<int, TestSet>
+  struct TestInfo
+  {
+    TestSet Depends;
+  };
+  struct TestMap : public std::map<int, TestInfo>
   {
   };
   struct TestList : public std::vector<int>
@@ -57,7 +61,7 @@
   cmCTestMultiProcessHandler();
   virtual ~cmCTestMultiProcessHandler();
   // Set the tests
-  void SetTests(TestMap& tests, PropertiesMap& properties);
+  bool SetTests(TestMap tests, PropertiesMap properties);
   // Set the max number of tests that can be run at the same time.
   void SetParallelLevel(size_t);
   void SetTestLoad(unsigned long load);
@@ -99,14 +103,14 @@
 
   void SetQuiet(bool b) { this->Quiet = b; }
 
-  void CheckResourcesAvailable();
+  void CheckResourceAvailability();
 
 protected:
   // Start the next test or tests as many as are allowed by
   // ParallelLevel
   void StartNextTests();
-  bool StartTestProcess(int test);
-  bool StartTest(int test);
+  void StartTestProcess(int test);
+  void StartTest(int test);
   // Mark the checkpoint for the given test
   void WriteCheckpoint(int index);
 
@@ -124,10 +128,10 @@
 
   // Removes the checkpoint file
   void MarkFinished();
-  void EraseTest(int index);
   void FinishTestProcess(std::unique_ptr<cmCTestRunTest> runner, bool started);
 
-  static void OnTestLoadRetryCB(uv_timer_t* timer);
+  void StartNextTestsOnIdle();
+  void StartNextTestsOnTimer();
 
   void RemoveTest(int index);
   // Check if we need to resume an interrupted test set
@@ -143,21 +147,25 @@
   bool CheckStopTimePassed();
   void SetStopTimePassed();
 
+  void InitializeLoop();
+  void FinalizeLoop();
+
   void LockResources(int index);
   void UnlockResources(int index);
 
-  enum class ResourceAllocationError
+  enum class ResourceAvailabilityError
   {
     NoResourceType,
     InsufficientResources,
   };
 
+  bool Complete();
   bool AllocateResources(int index);
   bool TryAllocateResources(
     int index,
     std::map<std::string, std::vector<cmCTestBinPackerAllocation>>&
       allocations,
-    std::map<std::string, ResourceAllocationError>* errors = nullptr);
+    std::map<std::string, ResourceAvailabilityError>* errors = nullptr);
   void DeallocateResources(int index);
   bool AllResourcesAvailable();
   bool InitResourceAllocator(std::string& error);
@@ -170,9 +178,10 @@
   cm::optional<std::size_t> ResourceSpecSetupTest;
   bool HasInvalidGeneratedResourceSpec;
 
-  // map from test number to set of depend tests
-  TestMap Tests;
-  TestList SortedTests;
+  // Tests pending selection to start.  They may have dependencies.
+  TestMap PendingTests;
+  // List of pending test indexes, ordered by cost.
+  std::list<int> OrderedTests;
   // Total number of tests we'll be running
   size_t Total;
   // Number of tests that are complete
@@ -183,25 +192,33 @@
   bool StopTimePassed = false;
   // list of test properties (indices concurrent to the test map)
   PropertiesMap Properties;
-  std::map<int, bool> TestRunningMap;
-  std::map<int, bool> TestFinishMap;
   std::map<int, std::string> TestOutput;
   std::vector<std::string>* Passed;
   std::vector<std::string>* Failed;
   std::vector<std::string> LastTestsFailed;
-  std::set<std::string> LockedResources;
+  std::set<std::string> ProjectResourcesLocked;
   std::map<int,
            std::vector<std::map<std::string, std::vector<ResourceAllocation>>>>
     AllocatedResources;
-  std::map<int, std::map<std::string, ResourceAllocationError>>
-    ResourceAllocationErrors;
+  std::map<int, std::map<std::string, ResourceAvailabilityError>>
+    ResourceAvailabilityErrors;
   cmCTestResourceAllocator ResourceAllocator;
   std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults;
   size_t ParallelLevel; // max number of process that can be run at once
+
+  // 'make' jobserver client.  If connected, we acquire a token
+  // for each test before running its process.
+  cm::optional<cmUVJobServerClient> JobServerClient;
+  // List of tests that are queued to run when a token is available.
+  std::list<int> JobServerQueuedTests;
+  // Callback invoked when a token is received.
+  void JobServerReceivedToken();
+
   unsigned long TestLoad;
   unsigned long FakeLoadForTesting;
-  uv_loop_t Loop;
-  cm::uv_timer_ptr TestLoadRetryTimer;
+  cm::uv_loop_ptr Loop;
+  cm::uv_idle_ptr StartNextTestsOnIdle_;
+  cm::uv_timer_ptr StartNextTestsOnTimer_;
   cmCTestTestHandler* TestHandler;
   cmCTest* CTest;
   bool HasCycles;
diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx
index 0e002b9..20bd0ec 100644
--- a/Source/CTest/cmCTestP4.cxx
+++ b/Source/CTest/cmCTestP4.cxx
@@ -149,17 +149,16 @@
   auto it = this->Users.find(username);
 
   if (it == this->Users.end()) {
-    std::vector<char const*> p4_users;
+    std::vector<std::string> p4_users;
     this->SetP4Options(p4_users);
-    p4_users.push_back("users");
-    p4_users.push_back("-m");
-    p4_users.push_back("1");
-    p4_users.push_back(username.c_str());
-    p4_users.push_back(nullptr);
+    p4_users.emplace_back("users");
+    p4_users.emplace_back("-m");
+    p4_users.emplace_back("1");
+    p4_users.push_back(username);
 
     UserParser out(this, "users-out> ");
     OutputLogger err(this->Log, "users-err> ");
-    this->RunChild(p4_users.data(), &out, &err);
+    this->RunChild(p4_users, &out, &err);
 
     // The user should now be added to the map. Search again.
     it = this->Users.find(username);
@@ -303,10 +302,10 @@
   }
 };
 
-void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
+void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
 {
   if (this->P4Options.empty()) {
-    const char* p4 = this->CommandLineTool.c_str();
+    std::string p4 = this->CommandLineTool;
     this->P4Options.emplace_back(p4);
 
     // The CTEST_P4_CLIENT variable sets the P4 client used when issuing
@@ -328,31 +327,27 @@
     cm::append(this->P4Options, cmSystemTools::ParseArguments(opts));
   }
 
-  CommandOptions.clear();
-  for (std::string const& o : this->P4Options) {
-    CommandOptions.push_back(o.c_str());
-  }
+  CommandOptions = this->P4Options;
 }
 
 std::string cmCTestP4::GetWorkingRevision()
 {
-  std::vector<char const*> p4_identify;
+  std::vector<std::string> p4_identify;
   this->SetP4Options(p4_identify);
 
-  p4_identify.push_back("changes");
-  p4_identify.push_back("-m");
-  p4_identify.push_back("1");
-  p4_identify.push_back("-t");
+  p4_identify.emplace_back("changes");
+  p4_identify.emplace_back("-m");
+  p4_identify.emplace_back("1");
+  p4_identify.emplace_back("-t");
 
   std::string source = this->SourceDirectory + "/...#have";
-  p4_identify.push_back(source.c_str());
-  p4_identify.push_back(nullptr);
+  p4_identify.push_back(source);
 
   std::string rev;
   IdentifyParser out(this, "p4_changes-out> ", rev);
   OutputLogger err(this->Log, "p4_changes-err> ");
 
-  bool result = this->RunChild(p4_identify.data(), &out, &err);
+  bool result = this->RunChild(p4_identify, &out, &err);
 
   // If there was a problem contacting the server return "<unknown>"
   if (!result) {
@@ -388,7 +383,7 @@
 
 bool cmCTestP4::LoadRevisions()
 {
-  std::vector<char const*> p4_changes;
+  std::vector<std::string> p4_changes;
   this->SetP4Options(p4_changes);
 
   // Use 'p4 changes ...@old,new' to get a list of changelists
@@ -408,52 +403,49 @@
     .append(",")
     .append(this->NewRevision);
 
-  p4_changes.push_back("changes");
-  p4_changes.push_back(range.c_str());
-  p4_changes.push_back(nullptr);
+  p4_changes.emplace_back("changes");
+  p4_changes.push_back(range);
 
   ChangesParser out(this, "p4_changes-out> ");
   OutputLogger err(this->Log, "p4_changes-err> ");
 
   this->ChangeLists.clear();
-  this->RunChild(p4_changes.data(), &out, &err);
+  this->RunChild(p4_changes, &out, &err);
 
   if (this->ChangeLists.empty()) {
     return true;
   }
 
   // p4 describe -s ...@1111111,2222222
-  std::vector<char const*> p4_describe;
+  std::vector<std::string> p4_describe;
   for (std::string const& i : cmReverseRange(this->ChangeLists)) {
     this->SetP4Options(p4_describe);
-    p4_describe.push_back("describe");
-    p4_describe.push_back("-s");
-    p4_describe.push_back(i.c_str());
-    p4_describe.push_back(nullptr);
+    p4_describe.emplace_back("describe");
+    p4_describe.emplace_back("-s");
+    p4_describe.push_back(i);
 
     DescribeParser outDescribe(this, "p4_describe-out> ");
     OutputLogger errDescribe(this->Log, "p4_describe-err> ");
-    this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
+    this->RunChild(p4_describe, &outDescribe, &errDescribe);
   }
   return true;
 }
 
 bool cmCTestP4::LoadModifications()
 {
-  std::vector<char const*> p4_diff;
+  std::vector<std::string> p4_diff;
   this->SetP4Options(p4_diff);
 
-  p4_diff.push_back("diff");
+  p4_diff.emplace_back("diff");
 
   // Ideally we would use -Od but not all clients support it
-  p4_diff.push_back("-dn");
+  p4_diff.emplace_back("-dn");
   std::string source = this->SourceDirectory + "/...";
-  p4_diff.push_back(source.c_str());
-  p4_diff.push_back(nullptr);
+  p4_diff.push_back(source);
 
   DiffParser out(this, "p4_diff-out> ");
   OutputLogger err(this->Log, "p4_diff-err> ");
-  this->RunChild(p4_diff.data(), &out, &err);
+  this->RunChild(p4_diff, &out, &err);
   return true;
 }
 
@@ -461,17 +453,14 @@
 {
   cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
 
-  std::vector<char const*> p4_custom;
-  p4_custom.reserve(p4_custom_command.size() + 1);
-  for (std::string const& i : p4_custom_command) {
-    p4_custom.push_back(i.c_str());
-  }
-  p4_custom.push_back(nullptr);
+  std::vector<std::string> p4_custom;
+  p4_custom.reserve(p4_custom_command.size());
+  cm::append(p4_custom, p4_custom_command);
 
   OutputLogger custom_out(this->Log, "p4_customsync-out> ");
   OutputLogger custom_err(this->Log, "p4_customsync-err> ");
 
-  return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err);
+  return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err);
 }
 
 bool cmCTestP4::UpdateImpl()
@@ -488,10 +477,10 @@
     return false;
   }
 
-  std::vector<char const*> p4_sync;
+  std::vector<std::string> p4_sync;
   this->SetP4Options(p4_sync);
 
-  p4_sync.push_back("sync");
+  p4_sync.emplace_back("sync");
 
   // Get user-specified update options.
   std::string opts = this->CTest->GetCTestConfiguration("UpdateOptions");
@@ -499,9 +488,7 @@
     opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    p4_sync.push_back(arg.c_str());
-  }
+  cm::append(p4_sync, args);
 
   std::string source = this->SourceDirectory + "/...";
 
@@ -515,11 +502,10 @@
     source.append("@\"").append(date).append("\"");
   }
 
-  p4_sync.push_back(source.c_str());
-  p4_sync.push_back(nullptr);
+  p4_sync.push_back(source);
 
   OutputLogger out(this->Log, "p4_sync-out> ");
   OutputLogger err(this->Log, "p4_sync-err> ");
 
-  return this->RunUpdateCommand(p4_sync.data(), &out, &err);
+  return this->RunUpdateCommand(p4_sync, &out, &err);
 }
diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h
index 1889520..827caa1 100644
--- a/Source/CTest/cmCTestP4.h
+++ b/Source/CTest/cmCTestP4.h
@@ -39,7 +39,7 @@
   std::vector<std::string> P4Options;
 
   User GetUserData(const std::string& username);
-  void SetP4Options(std::vector<char const*>& options);
+  void SetP4Options(std::vector<std::string>& options);
 
   std::string GetWorkingRevision();
   bool NoteOldRevision() override;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 8ceb9db..483b3b4 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -25,13 +25,17 @@
 #include "cmProcess.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
 #include "cmWorkingDirectory.h"
 
-cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler)
+cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler,
+                               int index)
   : MultiTestHandler(multiHandler)
+  , Index(index)
+  , CTest(MultiTestHandler.CTest)
+  , TestHandler(MultiTestHandler.TestHandler)
+  , TestProperties(MultiTestHandler.Properties[Index])
 {
-  this->CTest = multiHandler.CTest;
-  this->TestHandler = multiHandler.TestHandler;
 }
 
 void cmCTestRunTest::CheckOutput(std::string const& line)
@@ -161,7 +165,7 @@
       reason = "Invalid resource spec file";
       forceFail = true;
     } else {
-      this->MultiTestHandler.CheckResourcesAvailable();
+      this->MultiTestHandler.CheckResourceAvailability();
     }
   }
   std::ostringstream outputStream;
@@ -526,7 +530,7 @@
   return outputStream.str();
 }
 
-bool cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
+void cmCTestRunTest::StartTest(std::unique_ptr<cmCTestRunTest> runner,
                                size_t completed, size_t total)
 {
   auto* testRun = runner.get();
@@ -535,10 +539,7 @@
 
   if (!testRun->StartTest(completed, total)) {
     testRun->FinalizeTest(false);
-    return false;
   }
-
-  return true;
 }
 
 // Starts the execution of a test.  Returns once it has started
@@ -887,7 +888,7 @@
   this->TestResult.Environment.erase(this->TestResult.Environment.length() -
                                      1);
 
-  return this->TestProcess->StartProcess(this->MultiTestHandler.Loop,
+  return this->TestProcess->StartProcess(*this->MultiTestHandler.Loop,
                                          &this->TestProperties->Affinity);
 }
 
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index 34f23c4..71d0865 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -24,7 +24,7 @@
 class cmCTestRunTest
 {
 public:
-  explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler);
+  explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler, int index);
 
   void SetNumberOfRuns(int n)
   {
@@ -33,18 +33,12 @@
   }
 
   void SetRepeatMode(cmCTest::Repeat r) { this->RepeatMode = r; }
-  void SetTestProperties(cmCTestTestHandler::cmCTestTestProperties* prop)
-  {
-    this->TestProperties = prop;
-  }
 
   cmCTestTestHandler::cmCTestTestProperties* GetTestProperties()
   {
     return this->TestProperties;
   }
 
-  void SetIndex(int i) { this->Index = i; }
-
   int GetIndex() { return this->Index; }
 
   void AddFailedDependency(const std::string& failedTest)
@@ -62,7 +56,7 @@
   // Read and store output.  Returns true if it must be called again.
   void CheckOutput(std::string const& line);
 
-  static bool StartTest(std::unique_ptr<cmCTestRunTest> runner,
+  static void StartTest(std::unique_ptr<cmCTestRunTest> runner,
                         size_t completed, size_t total);
   static bool StartAgain(std::unique_ptr<cmCTestRunTest> runner,
                          size_t completed);
@@ -124,16 +118,15 @@
   // Returns "completed/total Test #Index: "
   std::string GetTestPrefix(size_t completed, size_t total) const;
 
-  cmCTestTestHandler::cmCTestTestProperties* TestProperties;
-  // Pointer back to the "parent"; the handler that invoked this test run
-  cmCTestTestHandler* TestHandler;
-  cmCTest* CTest;
-  std::unique_ptr<cmProcess> TestProcess;
-  std::string ProcessOutput;
-  // The test results
-  cmCTestTestHandler::cmCTestTestResult TestResult;
   cmCTestMultiProcessHandler& MultiTestHandler;
   int Index;
+  cmCTest* CTest;
+  cmCTestTestHandler* TestHandler;
+  cmCTestTestHandler::cmCTestTestProperties* TestProperties;
+
+  std::unique_ptr<cmProcess> TestProcess;
+  std::string ProcessOutput;
+  cmCTestTestHandler::cmCTestTestResult TestResult;
   std::set<std::string> FailedDependencies;
   std::string StartTime;
   std::string ActualCommand;
diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx
index 91a1177..fc7051c 100644
--- a/Source/CTest/cmCTestSVN.cxx
+++ b/Source/CTest/cmCTestSVN.cxx
@@ -33,8 +33,8 @@
 
 void cmCTestSVN::CleanupImpl()
 {
-  std::vector<const char*> svn_cleanup;
-  svn_cleanup.push_back("cleanup");
+  std::vector<std::string> svn_cleanup;
+  svn_cleanup.emplace_back("cleanup");
   OutputLogger out(this->Log, "cleanup-out> ");
   OutputLogger err(this->Log, "cleanup-err> ");
   this->RunSVNCommand(svn_cleanup, &out, &err);
@@ -88,9 +88,9 @@
 std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
 {
   // Run "svn info" to get the repository info from the work tree.
-  std::vector<const char*> svn_info;
-  svn_info.push_back("info");
-  svn_info.push_back(svninfo.LocalPath.c_str());
+  std::vector<std::string> svn_info;
+  svn_info.emplace_back("info");
+  svn_info.push_back(svninfo.LocalPath);
   std::string rev;
   InfoParser out(this, "info-out> ", rev, svninfo);
   OutputLogger err(this->Log, "info-err> ");
@@ -251,43 +251,37 @@
     args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
   }
 
-  std::vector<char const*> svn_update;
-  svn_update.push_back("update");
-  for (std::string const& arg : args) {
-    svn_update.push_back(arg.c_str());
-  }
+  std::vector<std::string> svn_update;
+  svn_update.emplace_back("update");
+  cm::append(svn_update, args);
 
   UpdateParser out(this, "up-out> ");
   OutputLogger err(this->Log, "up-err> ");
   return this->RunSVNCommand(svn_update, &out, &err);
 }
 
-bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
+bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
                                OutputParser* out, OutputParser* err)
 {
   if (parameters.empty()) {
     return false;
   }
 
-  std::vector<char const*> args;
-  args.push_back(this->CommandLineTool.c_str());
+  std::vector<std::string> args;
+  args.push_back(this->CommandLineTool);
   cm::append(args, parameters);
-  args.push_back("--non-interactive");
+  args.emplace_back("--non-interactive");
 
   std::string userOptions = this->CTest->GetCTestConfiguration("SVNOptions");
 
   std::vector<std::string> parsedUserOptions =
     cmSystemTools::ParseArguments(userOptions);
-  for (std::string const& opt : parsedUserOptions) {
-    args.push_back(opt.c_str());
-  }
+  cm::append(args, parsedUserOptions);
 
-  args.push_back(nullptr);
-
-  if (strcmp(parameters[0], "update") == 0) {
-    return this->RunUpdateCommand(args.data(), out, err);
+  if (parameters[0] == "update") {
+    return this->RunUpdateCommand(args, out, err);
   }
-  return this->RunChild(args.data(), out, err);
+  return this->RunChild(args, out, err);
 }
 
 class cmCTestSVN::LogParser
@@ -393,12 +387,12 @@
   }
 
   // Run "svn log" to get all global revisions of interest.
-  std::vector<const char*> svn_log;
-  svn_log.push_back("log");
-  svn_log.push_back("--xml");
-  svn_log.push_back("-v");
-  svn_log.push_back(revs.c_str());
-  svn_log.push_back(svninfo.LocalPath.c_str());
+  std::vector<std::string> svn_log;
+  svn_log.emplace_back("log");
+  svn_log.emplace_back("--xml");
+  svn_log.emplace_back("-v");
+  svn_log.emplace_back(revs);
+  svn_log.emplace_back(svninfo.LocalPath);
   LogParser out(this, "log-out> ", svninfo);
   OutputLogger err(this->Log, "log-err> ");
   return this->RunSVNCommand(svn_log, &out, &err);
@@ -472,8 +466,8 @@
 bool cmCTestSVN::LoadModifications()
 {
   // Run "svn status" which reports local modifications.
-  std::vector<const char*> svn_status;
-  svn_status.push_back("status");
+  std::vector<std::string> svn_status;
+  svn_status.emplace_back("status");
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunSVNCommand(svn_status, &out, &err);
@@ -534,8 +528,8 @@
   this->RootInfo = &(this->Repositories.back());
 
   // Run "svn status" to get the list of external repositories
-  std::vector<const char*> svn_status;
-  svn_status.push_back("status");
+  std::vector<std::string> svn_status;
+  svn_status.emplace_back("status");
   ExternalParser out(this, "external-out> ");
   OutputLogger err(this->Log, "external-err> ");
   return this->RunSVNCommand(svn_status, &out, &err);
diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h
index 370d176..1485dc0 100644
--- a/Source/CTest/cmCTestSVN.h
+++ b/Source/CTest/cmCTestSVN.h
@@ -33,7 +33,7 @@
   bool NoteNewRevision() override;
   bool UpdateImpl() override;
 
-  bool RunSVNCommand(std::vector<char const*> const& parameters,
+  bool RunSVNCommand(std::vector<std::string> const& parameters,
                      OutputParser* out, OutputParser* err);
 
   // Information about an SVN repository (root repository or external)
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index 461ad1a..2003ce6 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -11,8 +11,9 @@
 
 #include <cm/memory>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Directory.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
@@ -40,6 +41,8 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -148,66 +151,65 @@
   // now pass through all the other arguments
   std::vector<std::string>& initArgs =
     this->CTest->GetInitialCommandLineArguments();
-  //*** need to make sure this does not have the current script ***
-  for (size_t i = 1; i < initArgs.size(); ++i) {
-    argv.push_back(initArgs[i].c_str());
-  }
-  argv.push_back(nullptr);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  // cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  // cmsysProcess_SetTimeout(cp, timeout);
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(initArgs)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  auto process = builder.Start();
+  cm::uv_pipe_ptr outPipe;
+  outPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(outPipe, process.OutputStream());
+  cm::uv_pipe_ptr errPipe;
+  errPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(errPipe, process.ErrorStream());
 
   std::vector<char> out;
   std::vector<char> err;
   std::string line;
-  int pipe =
-    cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
-  while (pipe != cmsysProcess_Pipe_None) {
+  auto pipe =
+    cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                               std::chrono::seconds(100), out, err);
+  while (pipe != cmSystemTools::WaitForLineResult::None) {
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Output: " << line << "\n");
-    if (pipe == cmsysProcess_Pipe_STDERR) {
+    if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
       cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
-    } else if (pipe == cmsysProcess_Pipe_STDOUT) {
+    } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
     }
-    pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
-                                      err);
+    pipe =
+      cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                                 std::chrono::seconds(100), out, err);
   }
 
   // Properly handle output of the build command
-  cmsysProcess_WaitForExit(cp, nullptr);
-  int result = cmsysProcess_GetState(cp);
+  process.Wait();
+  auto const& status = process.GetStatus(0);
+  auto result = status.GetException();
   int retVal = 0;
   bool failed = false;
-  if (result == cmsysProcess_State_Exited) {
-    retVal = cmsysProcess_GetExitValue(cp);
-  } else if (result == cmsysProcess_State_Exception) {
-    retVal = cmsysProcess_GetExitException(cp);
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was an exception: "
-                 << cmsysProcess_GetExceptionString(cp) << " " << retVal
-                 << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Expired) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was a timeout" << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Error) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
-                                           << std::endl);
-    failed = true;
+  switch (result.first) {
+    case cmUVProcessChain::ExceptionCode::None:
+      retVal = static_cast<int>(status.ExitStatus);
+      break;
+    case cmUVProcessChain::ExceptionCode::Spawn:
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tError executing ctest: " << result.second << std::endl);
+      failed = true;
+      break;
+    default:
+      retVal = status.TermSignal;
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tThere was an exception: " << result.second << " " << retVal
+                                              << std::endl);
+      failed = true;
   }
-  cmsysProcess_Delete(cp);
   if (failed) {
     std::ostringstream message;
     message << "Error running command: [";
-    message << result << "] ";
+    message << static_cast<int>(result.first) << "] ";
     for (const char* arg : argv) {
       if (arg) {
         message << arg << " ";
@@ -561,10 +563,8 @@
 
 int cmCTestScriptHandler::CheckOutSourceDir()
 {
-  std::string command;
   std::string output;
   int retVal;
-  bool res;
 
   if (!cmSystemTools::FileExists(this->SourceDir) &&
       !this->CVSCheckOut.empty()) {
@@ -572,7 +572,7 @@
     output.clear();
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Run cvs: " << this->CVSCheckOut << std::endl);
-    res = cmSystemTools::RunSingleCommand(
+    bool res = cmSystemTools::RunSingleCommand(
       this->CVSCheckOut, &output, &output, &retVal, this->CTestRoot.c_str(),
       this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/);
     if (!res || retVal != 0) {
@@ -585,8 +585,6 @@
 
 int cmCTestScriptHandler::BackupDirectories()
 {
-  int retVal;
-
   // compute the backup names
   this->BackupSourceDir = cmStrCat(this->SourceDir, "_CMakeBackup");
   this->BackupBinaryDir = cmStrCat(this->BinaryDir, "_CMakeBackup");
@@ -606,7 +604,7 @@
     rename(this->BinaryDir.c_str(), this->BackupBinaryDir.c_str());
 
     // we must now checkout the src dir
-    retVal = this->CheckOutSourceDir();
+    int retVal = this->CheckOutSourceDir();
     if (retVal) {
       this->RestoreBackupDirectories();
       return retVal;
@@ -670,9 +668,11 @@
 
   // clear the binary directory?
   if (this->EmptyBinDir) {
-    if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir)) {
+    std::string err;
+    if (!cmCTestScriptHandler::EmptyBinaryDirectory(this->BinaryDir, err)) {
       cmCTestLog(this->CTest, ERROR_MESSAGE,
-                 "Problem removing the binary directory" << std::endl);
+                 "Problem removing the binary directory ("
+                   << err << "): " << this->BinaryDir << std::endl);
     }
   }
 
@@ -858,10 +858,12 @@
   return true;
 }
 
-bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname)
+bool cmCTestScriptHandler::EmptyBinaryDirectory(const std::string& sname,
+                                                std::string& err)
 {
   // try to avoid deleting root
   if (sname.size() < 2) {
+    err = "path too short";
     return false;
   }
 
@@ -874,20 +876,24 @@
   std::string check = cmStrCat(sname, "/CMakeCache.txt");
 
   if (!cmSystemTools::FileExists(check)) {
+    err = "path does not contain an existing CMakeCache.txt file";
     return false;
   }
 
+  cmsys::Status status;
   for (int i = 0; i < 5; ++i) {
-    if (TryToRemoveBinaryDirectoryOnce(sname)) {
+    status = TryToRemoveBinaryDirectoryOnce(sname);
+    if (status) {
       return true;
     }
     cmSystemTools::Delay(100);
   }
 
+  err = status.GetString();
   return false;
 }
 
-bool cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(
+cmsys::Status cmCTestScriptHandler::TryToRemoveBinaryDirectoryOnce(
   const std::string& directoryPath)
 {
   cmsys::Directory directory;
@@ -905,18 +911,18 @@
     bool isDirectory = cmSystemTools::FileIsDirectory(fullPath) &&
       !cmSystemTools::FileIsSymlink(fullPath);
 
+    cmsys::Status status;
     if (isDirectory) {
-      if (!cmSystemTools::RemoveADirectory(fullPath)) {
-        return false;
-      }
+      status = cmSystemTools::RemoveADirectory(fullPath);
     } else {
-      if (!cmSystemTools::RemoveFile(fullPath)) {
-        return false;
-      }
+      status = cmSystemTools::RemoveFile(fullPath);
+    }
+    if (!status) {
+      return status;
     }
   }
 
-  return static_cast<bool>(cmSystemTools::RemoveADirectory(directoryPath));
+  return cmSystemTools::RemoveADirectory(directoryPath);
 }
 
 cmDuration cmCTestScriptHandler::GetRemainingTimeAllowed()
diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h
index b7764b2..8aa07e7 100644
--- a/Source/CTest/cmCTestScriptHandler.h
+++ b/Source/CTest/cmCTestScriptHandler.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include "cmsys/Status.hxx"
+
 #include "cmCTestGenericHandler.h"
 #include "cmDuration.h"
 
@@ -80,7 +82,7 @@
   /*
    * Empty Binary Directory
    */
-  static bool EmptyBinaryDirectory(const std::string& dir);
+  static bool EmptyBinaryDirectory(const std::string& dir, std::string& err);
 
   /*
    * Write an initial CMakeCache.txt from the given contents.
@@ -139,7 +141,8 @@
                        std::unique_ptr<cmCTestCommand> command);
 
   // Try to remove the binary directory once
-  static bool TryToRemoveBinaryDirectoryOnce(const std::string& directoryPath);
+  static cmsys::Status TryToRemoveBinaryDirectoryOnce(
+    const std::string& directoryPath);
 
   std::vector<std::string> ConfigurationScripts;
   std::vector<bool> ScriptProcessScope;
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 77af889..db8a054 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -138,6 +138,19 @@
   this->Files.clear();
 }
 
+int cmCTestSubmitHandler::ProcessCommandLineArguments(
+  const std::string& currentArg, size_t& idx,
+  const std::vector<std::string>& allArgs)
+{
+  if (cmHasLiteralPrefix(currentArg, "--http-header") &&
+      idx < allArgs.size() - 1) {
+    ++idx;
+    this->HttpHeaders.push_back(allArgs[idx]);
+    this->CommandLineHttpHeaders.push_back(allArgs[idx]);
+  }
+  return 1;
+}
+
 bool cmCTestSubmitHandler::SubmitUsingHTTP(
   const std::string& localprefix, const std::vector<std::string>& files,
   const std::string& remoteprefix, const std::string& url)
diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h
index 0c7253c..e8eb38a 100644
--- a/Source/CTest/cmCTestSubmitHandler.h
+++ b/Source/CTest/cmCTestSubmitHandler.h
@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <iosfwd>
 #include <set>
 #include <string>
@@ -33,6 +34,11 @@
 
   void Initialize() override;
 
+  //! Set all the submit arguments
+  int ProcessCommandLineArguments(
+    const std::string& currentArg, size_t& idx,
+    const std::vector<std::string>& allArgs) override;
+
   /** Specify a set of parts (by name) to submit.  */
   void SelectParts(std::set<cmCTest::Part> const& parts);
 
@@ -44,7 +50,12 @@
 
   void SetHttpHeaders(std::vector<std::string> const& v)
   {
-    this->HttpHeaders = v;
+    if (this->CommandLineHttpHeaders.empty()) {
+      this->HttpHeaders = v;
+    } else {
+      this->HttpHeaders = this->CommandLineHttpHeaders;
+      this->HttpHeaders.insert(this->HttpHeaders.end(), v.begin(), v.end());
+    }
   }
 
 private:
@@ -75,5 +86,6 @@
   bool HasWarnings;
   bool HasErrors;
   std::set<std::string> Files;
+  std::vector<std::string> CommandLineHttpHeaders;
   std::vector<std::string> HttpHeaders;
 };
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index c717868..ed16354 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -26,6 +26,8 @@
   this->Bind("INCLUDE"_s, this->Include);
   this->Bind("EXCLUDE_LABEL"_s, this->ExcludeLabel);
   this->Bind("INCLUDE_LABEL"_s, this->IncludeLabel);
+  this->Bind("EXCLUDE_FROM_FILE"_s, this->ExcludeTestsFromFile);
+  this->Bind("INCLUDE_FROM_FILE"_s, this->IncludeTestsFromFile);
   this->Bind("EXCLUDE_FIXTURE"_s, this->ExcludeFixture);
   this->Bind("EXCLUDE_FIXTURE_SETUP"_s, this->ExcludeFixtureSetup);
   this->Bind("EXCLUDE_FIXTURE_CLEANUP"_s, this->ExcludeFixtureCleanup);
@@ -80,6 +82,14 @@
   if (!this->IncludeLabel.empty()) {
     handler->AddMultiOption("LabelRegularExpression", this->IncludeLabel);
   }
+
+  if (!this->ExcludeTestsFromFile.empty()) {
+    handler->SetOption("ExcludeTestListFile", this->ExcludeTestsFromFile);
+  }
+  if (!this->IncludeTestsFromFile.empty()) {
+    handler->SetOption("TestListFile", this->IncludeTestsFromFile);
+  }
+
   if (!this->ExcludeFixture.empty()) {
     handler->SetOption("ExcludeFixtureRegularExpression",
                        this->ExcludeFixture);
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index 24e74e2..ce054ed 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -51,6 +51,8 @@
   std::string Include;
   std::string ExcludeLabel;
   std::string IncludeLabel;
+  std::string IncludeTestsFromFile;
+  std::string ExcludeTestsFromFile;
   std::string ExcludeFixture;
   std::string ExcludeFixtureSetup;
   std::string ExcludeFixtureCleanup;
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index eb3b4dd..0a8d4b8 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -345,6 +345,8 @@
   this->ExcludeFixtureRegExp.clear();
   this->ExcludeFixtureSetupRegExp.clear();
   this->ExcludeFixtureCleanupRegExp.clear();
+  this->TestListFile.clear();
+  this->ExcludeTestListFile.clear();
 
   this->TestsToRunString.clear();
   this->UseUnion = false;
@@ -585,6 +587,14 @@
   if (val) {
     this->ResourceSpecFile = *val;
   }
+  val = this->GetOption("TestListFile");
+  if (val) {
+    this->TestListFile = val;
+  }
+  val = this->GetOption("ExcludeTestListFile");
+  if (val) {
+    this->ExcludeTestListFile = val;
+  }
   this->SetRerunFailed(cmIsOn(this->GetOption("RerunFailed")));
 
   return true;
@@ -910,7 +920,6 @@
   // Now create a final list of tests to run
   int cnt = 0;
   inREcnt = 0;
-  std::string last_directory;
   ListOfTests finalList;
   for (cmCTestTestProperties& tp : this->TestList) {
     cnt++;
@@ -933,6 +942,21 @@
         continue;
       }
     }
+
+    if (!this->TestsToRunByName.empty()) {
+      if (this->TestsToRunByName.find(tp.Name) ==
+          this->TestsToRunByName.end()) {
+        continue;
+      }
+    }
+
+    if (!this->TestsToExcludeByName.empty()) {
+      if (this->TestsToExcludeByName.find(tp.Name) !=
+          this->TestsToExcludeByName.end()) {
+        continue;
+      }
+    }
+
     tp.Index = cnt; // save the index into the test list for this test
     finalList.push_back(tp);
   }
@@ -1381,15 +1405,17 @@
         }
       }
     }
-    tests[p.Index] = depends;
+    tests[p.Index].Depends = depends;
     properties[p.Index] = &p;
   }
   parallel->SetResourceSpecFile(this->ResourceSpecFile);
-  parallel->SetTests(tests, properties);
+  if (!parallel->SetTests(std::move(tests), std::move(properties))) {
+    return false;
+  }
   parallel->SetPassFailVectors(&passed, &failed);
   this->TestResults.clear();
   parallel->SetTestResults(&this->TestResults);
-  parallel->CheckResourcesAvailable();
+  parallel->CheckResourceAvailability();
 
   if (this->CTest->ShouldPrintLabels()) {
     parallel->PrintLabels();
@@ -1818,6 +1844,15 @@
   if (this->ResourceSpecFile.empty() && specFile) {
     this->ResourceSpecFile = *specFile;
   }
+
+  if (!this->TestListFile.empty()) {
+    this->TestsToRunByName = this->ReadTestListFile(this->TestListFile);
+  }
+  if (!this->ExcludeTestListFile.empty()) {
+    this->TestsToExcludeByName =
+      this->ReadTestListFile(this->ExcludeTestListFile);
+  }
+
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Done constructing a list of tests" << std::endl,
                      this->Quiet);
@@ -1986,6 +2021,34 @@
   }
 }
 
+std::set<std::string> cmCTestTestHandler::ReadTestListFile(
+  const std::string& testListFileName) const
+{
+  std::set<std::string> testNames;
+
+  cmsys::ifstream ifs(testListFileName.c_str());
+  if (ifs) {
+    std::string line;
+    while (cmSystemTools::GetLineFromStream(ifs, line)) {
+      std::string trimmed = cmTrimWhitespace(line);
+      if (trimmed.empty() || (trimmed[0] == '#')) {
+        continue;
+      }
+
+      testNames.insert(trimmed);
+    }
+    ifs.close();
+  } else if (!this->CTest->GetShowOnly() &&
+             !this->CTest->ShouldPrintLabels()) {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Problem reading test list file: "
+                 << testListFileName
+                 << " while generating list of tests to run." << std::endl);
+  }
+
+  return testNames;
+}
+
 void cmCTestTestHandler::RecordCustomTestMeasurements(cmXMLWriter& xml,
                                                       std::string content)
 {
@@ -2233,7 +2296,7 @@
           } else if (key == "RESOURCE_LOCK"_s) {
             cmList lval{ val };
 
-            rt.LockedResources.insert(lval.begin(), lval.end());
+            rt.ProjectResources.insert(lval.begin(), lval.end());
           } else if (key == "FIXTURES_SETUP"_s) {
             cmList lval{ val };
 
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 23f0a76..84e6098 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -165,7 +165,7 @@
     std::vector<std::string> Environment;
     std::vector<std::string> EnvironmentModification;
     std::vector<std::string> Labels;
-    std::set<std::string> LockedResources;
+    std::set<std::string> ProjectResources; // RESOURCE_LOCK
     std::set<std::string> FixturesSetup;
     std::set<std::string> FixturesCleanup;
     std::set<std::string> FixturesRequired;
@@ -341,6 +341,8 @@
   std::string GetTestStatus(cmCTestTestResult const&);
   void ExpandTestsToRunInformation(size_t numPossibleTests);
   void ExpandTestsToRunInformationForRerunFailed();
+  std::set<std::string> ReadTestListFile(
+    const std::string& testListFileName) const;
 
   std::vector<std::string> CustomPreTest;
   std::vector<std::string> CustomPostTest;
@@ -359,6 +361,10 @@
   std::vector<cmsys::RegularExpression> ExcludeLabelRegularExpressions;
   cmsys::RegularExpression IncludeTestsRegularExpression;
   cmsys::RegularExpression ExcludeTestsRegularExpression;
+  std::string TestListFile;
+  std::string ExcludeTestListFile;
+  std::set<std::string> TestsToRunByName;
+  std::set<std::string> TestsToExcludeByName;
 
   std::string ResourceSpecFile;
 
diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx
index 609ccba..cbbb5a5 100644
--- a/Source/CTest/cmCTestVC.cxx
+++ b/Source/CTest/cmCTestVC.cxx
@@ -7,10 +7,9 @@
 #include <sstream>
 #include <vector>
 
-#include "cmsys/Process.h"
-
 #include "cmCTest.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -55,18 +54,12 @@
 
   // Construct the initial checkout command line.
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
-  std::vector<char const*> vc_co;
-  vc_co.reserve(args.size() + 1);
-  for (std::string const& arg : args) {
-    vc_co.push_back(arg.c_str());
-  }
-  vc_co.push_back(nullptr);
 
   // Run the initial checkout command and log its output.
   this->Log << "--- Begin Initial Checkout ---\n";
   OutputLogger out(this->Log, "co-out> ");
   OutputLogger err(this->Log, "co-err> ");
-  bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str());
+  bool result = this->RunChild(args, &out, &err, parent);
   this->Log << "--- End Initial Checkout ---\n";
   if (!result) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
@@ -75,35 +68,35 @@
   return result;
 }
 
-bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
-                         OutputParser* err, const char* workDir,
-                         Encoding encoding)
+bool cmCTestVC::RunChild(const std::vector<std::string>& cmd,
+                         OutputParser* out, OutputParser* err,
+                         std::string workDir, Encoding encoding)
 {
   this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n";
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, cmd);
-  workDir = workDir ? workDir : this->SourceDirectory.c_str();
-  cmsysProcess_SetWorkingDirectory(cp, workDir);
-  cmCTestVC::RunProcess(cp, out, err, encoding);
-  int result = cmsysProcess_GetExitValue(cp);
-  cmsysProcess_Delete(cp);
-  return result == 0;
+  cmUVProcessChainBuilder builder;
+  if (workDir.empty()) {
+    workDir = this->SourceDirectory;
+  }
+  builder.AddCommand(cmd).SetWorkingDirectory(workDir);
+  auto status = cmCTestVC::RunProcess(builder, out, err, encoding);
+  return status.front().SpawnResult == 0 && status.front().ExitStatus == 0;
 }
 
-std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
+std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd)
 {
   std::ostringstream line;
   const char* sep = "";
-  for (const char* const* arg = cmd; *arg; ++arg) {
-    line << sep << "\"" << *arg << "\"";
+  for (auto const& arg : cmd) {
+    line << sep << "\"" << arg << "\"";
     sep = " ";
   }
   return line.str();
 }
 
-bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
-                                 OutputParser* err, Encoding encoding)
+bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
+                                 OutputParser* out, OutputParser* err,
+                                 Encoding encoding)
 {
   // Report the command line.
   this->UpdateCommandLine = this->ComputeCommandLine(cmd);
@@ -113,7 +106,7 @@
   }
 
   // Run the command.
-  return this->RunChild(cmd, out, err, nullptr, encoding);
+  return this->RunChild(cmd, out, err, "", encoding);
 }
 
 std::string cmCTestVC::GetNightlyTime()
diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h
index 7b03d10..dd5456d 100644
--- a/Source/CTest/cmCTestVC.h
+++ b/Source/CTest/cmCTestVC.h
@@ -6,6 +6,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <vector>
 
 #include "cmProcessOutput.h"
 #include "cmProcessTools.h"
@@ -108,15 +109,15 @@
   };
 
   /** Convert a list of arguments to a human-readable command line.  */
-  static std::string ComputeCommandLine(char const* const* cmd);
+  static std::string ComputeCommandLine(const std::vector<std::string>& cmd);
 
   /** Run a command line and send output to given parsers.  */
-  bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
-                const char* workDir = nullptr,
+  bool RunChild(const std::vector<std::string>& cmd, OutputParser* out,
+                OutputParser* err, std::string workDir = {},
                 Encoding encoding = cmProcessOutput::Auto);
 
   /** Run VC update command line and send output to given parsers.  */
-  bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
+  bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out,
                         OutputParser* err = nullptr,
                         Encoding encoding = cmProcessOutput::Auto);
 
diff --git a/Source/CTest/cmUVJobServerClient.cxx b/Source/CTest/cmUVJobServerClient.cxx
new file mode 100644
index 0000000..d7d76c9
--- /dev/null
+++ b/Source/CTest/cmUVJobServerClient.cxx
@@ -0,0 +1,518 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmUVJobServerClient.h"
+
+#include <cassert>
+#include <utility>
+
+#ifndef _WIN32
+#  include <cstdio>
+#  include <string>
+#  include <vector>
+
+#  include <fcntl.h>
+#  include <unistd.h>
+#endif
+
+#include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
+
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+
+class cmUVJobServerClient::Impl
+{
+public:
+  uv_loop_t& Loop;
+
+  cm::uv_idle_ptr ImplicitToken;
+  std::function<void()> OnToken;
+  std::function<void(int)> OnDisconnect;
+
+  // The number of tokens held by this client.
+  unsigned int HeldTokens = 0;
+
+  // The number of tokens we need to receive from the job server.
+  unsigned int NeedTokens = 0;
+
+  Impl(uv_loop_t& loop);
+  virtual ~Impl();
+
+  virtual void SendToken() = 0;
+  virtual void StartReceivingTokens() = 0;
+  virtual void StopReceivingTokens() = 0;
+
+  void RequestToken();
+  void ReleaseToken();
+  void RequestExplicitToken();
+  void DecrementNeedTokens();
+  void HoldToken();
+  void RequestImplicitToken();
+  void ReleaseImplicitToken();
+  void ReceivedToken();
+  void Disconnected(int status);
+};
+
+cmUVJobServerClient::Impl::Impl(uv_loop_t& loop)
+  : Loop(loop)
+{
+  this->ImplicitToken.init(this->Loop, this);
+}
+
+cmUVJobServerClient::Impl::~Impl() = default;
+
+void cmUVJobServerClient::Impl::RequestToken()
+{
+  if (this->HeldTokens == 0 && !uv_is_active(this->ImplicitToken)) {
+    this->RequestImplicitToken();
+  } else {
+    this->RequestExplicitToken();
+  }
+}
+
+void cmUVJobServerClient::Impl::ReleaseToken()
+{
+  assert(this->HeldTokens > 0);
+  --this->HeldTokens;
+  if (this->HeldTokens == 0) {
+    // This was the token implicitly owned by our process.
+    this->ReleaseImplicitToken();
+  } else {
+    // This was a token we received from the job server.  Send it back.
+    this->SendToken();
+  }
+}
+
+void cmUVJobServerClient::Impl::RequestExplicitToken()
+{
+  ++this->NeedTokens;
+  this->StartReceivingTokens();
+}
+
+void cmUVJobServerClient::Impl::DecrementNeedTokens()
+{
+  assert(this->NeedTokens > 0);
+  --this->NeedTokens;
+  if (this->NeedTokens == 0) {
+    this->StopReceivingTokens();
+  }
+}
+
+void cmUVJobServerClient::Impl::HoldToken()
+{
+  ++this->HeldTokens;
+  if (this->OnToken) {
+    this->OnToken();
+  } else {
+    this->ReleaseToken();
+  }
+}
+
+void cmUVJobServerClient::Impl::RequestImplicitToken()
+{
+  assert(this->HeldTokens == 0);
+  this->ImplicitToken.start([](uv_idle_t* handle) {
+    uv_idle_stop(handle);
+    auto* self = static_cast<Impl*>(handle->data);
+    self->HoldToken();
+  });
+}
+
+void cmUVJobServerClient::Impl::ReleaseImplicitToken()
+{
+  assert(this->HeldTokens == 0);
+  // Use the implicit token in place of receiving one from the job server.
+  if (this->NeedTokens > 0) {
+    this->DecrementNeedTokens();
+    this->RequestImplicitToken();
+  }
+}
+
+void cmUVJobServerClient::Impl::ReceivedToken()
+{
+  this->DecrementNeedTokens();
+  this->HoldToken();
+}
+
+void cmUVJobServerClient::Impl::Disconnected(int status)
+{
+  if (this->OnDisconnect) {
+    this->OnDisconnect(status);
+  }
+}
+
+//---------------------------------------------------------------------------
+// Implementation on POSIX platforms.
+// https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html
+
+#ifndef _WIN32
+namespace {
+class ImplPosix : public cmUVJobServerClient::Impl
+{
+public:
+  enum class Connection
+  {
+    None,
+    FDs,
+    FIFO,
+  };
+  Connection Conn = Connection::None;
+
+  cm::uv_pipe_ptr ConnRead;
+  cm::uv_pipe_ptr ConnWrite;
+  cm::uv_pipe_ptr ConnFIFO;
+
+  std::shared_ptr<std::function<void(int)>> OnWrite;
+
+  void Connect();
+  void ConnectFDs(int rfd, int wfd);
+  void ConnectFIFO(const char* path);
+  void Disconnect(int status);
+
+  cm::uv_pipe_ptr OpenFD(int fd);
+
+  uv_stream_t* GetWriter() const;
+  uv_stream_t* GetReader() const;
+
+  static void OnAllocateCB(uv_handle_t* handle, size_t suggested_size,
+                           uv_buf_t* buf);
+  static void OnReadCB(uv_stream_t* stream, ssize_t nread,
+                       const uv_buf_t* buf);
+
+  void OnAllocate(size_t suggested_size, uv_buf_t* buf);
+  void OnRead(ssize_t nread, const uv_buf_t* buf);
+
+  char ReadBuf = '.';
+
+  bool ReceivingTokens = false;
+
+  bool IsConnected() const;
+
+  void SendToken() override;
+  void StartReceivingTokens() override;
+  void StopReceivingTokens() override;
+
+  ImplPosix(uv_loop_t& loop);
+  ~ImplPosix() override;
+};
+
+ImplPosix::ImplPosix(uv_loop_t& loop)
+  : Impl(loop)
+  , OnWrite(std::make_shared<std::function<void(int)>>([this](int status) {
+    if (status != 0) {
+      this->Disconnect(status);
+    }
+  }))
+{
+  this->Connect();
+}
+
+ImplPosix::~ImplPosix()
+{
+  this->Disconnect(0);
+}
+
+void ImplPosix::Connect()
+{
+  // --jobserver-auth= for gnu make versions >= 4.2
+  // --jobserver-fds= for gnu make versions < 4.2
+  // -J for bsd make
+  static const std::vector<cm::string_view> prefixes = {
+    "--jobserver-auth=", "--jobserver-fds=", "-J"
+  };
+
+  cm::optional<std::string> makeflags = cmSystemTools::GetEnvVar("MAKEFLAGS");
+  if (!makeflags) {
+    return;
+  }
+
+  // Look for the *last* occurrence of jobserver flags.
+  cm::optional<std::string> auth;
+  std::vector<std::string> args;
+  cmSystemTools::ParseUnixCommandLine(makeflags->c_str(), args);
+  for (cm::string_view arg : cmReverseRange(args)) {
+    for (cm::string_view prefix : prefixes) {
+      if (cmHasPrefix(arg, prefix)) {
+        auth = cmTrimWhitespace(arg.substr(prefix.length()));
+        break;
+      }
+    }
+    if (auth) {
+      break;
+    }
+  }
+
+  if (!auth) {
+    return;
+  }
+
+  // fifo:PATH
+  if (cmHasLiteralPrefix(*auth, "fifo:")) {
+    ConnectFIFO(auth->substr(cmStrLen("fifo:")).c_str());
+    return;
+  }
+
+  // reader,writer
+  int reader;
+  int writer;
+  if (std::sscanf(auth->c_str(), "%d,%d", &reader, &writer) == 2) {
+    ConnectFDs(reader, writer);
+  }
+}
+
+cm::uv_pipe_ptr ImplPosix::OpenFD(int fd)
+{
+  // Create a CLOEXEC duplicate so `uv_pipe_ptr` can close it
+  // without closing the original file descriptor, which our
+  // child processes might want to use too.
+  cm::uv_pipe_ptr p;
+  int fd_dup = dup(fd);
+  if (fd_dup < 0) {
+    return p;
+  }
+  if (fcntl(fd_dup, F_SETFD, FD_CLOEXEC) == -1) {
+    close(fd_dup);
+    return p;
+  }
+  p.init(this->Loop, 0, this);
+  if (uv_pipe_open(p, fd_dup) < 0) {
+    close(fd_dup);
+  }
+  return p;
+}
+
+void ImplPosix::ConnectFDs(int rfd, int wfd)
+{
+  cm::uv_pipe_ptr connRead = this->OpenFD(rfd);
+  cm::uv_pipe_ptr connWrite = this->OpenFD(wfd);
+
+  // Verify that the read end is readable and the write end is writable.
+  if (!connRead || !uv_is_readable(connRead) || //
+      !connWrite || !uv_is_writable(connWrite)) {
+    return;
+  }
+
+  this->ConnRead = std::move(connRead);
+  this->ConnWrite = std::move(connWrite);
+  this->Conn = Connection::FDs;
+}
+
+void ImplPosix::ConnectFIFO(const char* path)
+{
+  int fd = open(path, O_RDWR);
+  if (fd < 0) {
+    return;
+  }
+
+  cm::uv_pipe_ptr connFIFO;
+  connFIFO.init(this->Loop, 0, this);
+  if (uv_pipe_open(connFIFO, fd) != 0) {
+    close(fd);
+    return;
+  }
+
+  // Verify that the fifo is both readable and writable.
+  if (!connFIFO || !uv_is_readable(connFIFO) || !uv_is_writable(connFIFO)) {
+    return;
+  }
+
+  this->ConnFIFO = std::move(connFIFO);
+  this->Conn = Connection::FIFO;
+}
+
+void ImplPosix::Disconnect(int status)
+{
+  if (this->Conn == Connection::None) {
+    return;
+  }
+
+  this->StopReceivingTokens();
+
+  switch (this->Conn) {
+    case Connection::FDs:
+      this->ConnRead.reset();
+      this->ConnWrite.reset();
+      break;
+    case Connection::FIFO:
+      this->ConnFIFO.reset();
+      break;
+    default:
+      break;
+  }
+
+  this->Conn = Connection::None;
+  if (status != 0) {
+    this->Disconnected(status);
+  }
+}
+
+uv_stream_t* ImplPosix::GetWriter() const
+{
+  switch (this->Conn) {
+    case Connection::FDs:
+      return this->ConnWrite;
+    case Connection::FIFO:
+      return this->ConnFIFO;
+    default:
+      return nullptr;
+  }
+}
+
+uv_stream_t* ImplPosix::GetReader() const
+{
+  switch (this->Conn) {
+    case Connection::FDs:
+      return this->ConnRead;
+    case Connection::FIFO:
+      return this->ConnFIFO;
+    default:
+      return nullptr;
+  }
+}
+
+void ImplPosix::OnAllocateCB(uv_handle_t* handle, size_t suggested_size,
+                             uv_buf_t* buf)
+{
+  auto* self = static_cast<ImplPosix*>(handle->data);
+  self->OnAllocate(suggested_size, buf);
+}
+
+void ImplPosix::OnReadCB(uv_stream_t* stream, ssize_t nread,
+                         const uv_buf_t* buf)
+{
+  auto* self = static_cast<ImplPosix*>(stream->data);
+  self->OnRead(nread, buf);
+}
+
+void ImplPosix::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf)
+{
+  *buf = uv_buf_init(&this->ReadBuf, 1);
+}
+
+void ImplPosix::OnRead(ssize_t nread, const uv_buf_t* /*buf*/)
+{
+  if (nread == 0) {
+    return;
+  }
+
+  if (nread < 0) {
+    auto status = static_cast<int>(nread);
+    this->Disconnect(status);
+    return;
+  }
+
+  assert(nread == 1);
+  this->ReceivedToken();
+}
+
+bool ImplPosix::IsConnected() const
+{
+  return this->Conn != Connection::None;
+}
+
+void ImplPosix::SendToken()
+{
+  if (this->Conn == Connection::None) {
+    return;
+  }
+
+  static char token = '.';
+
+  uv_buf_t const buf = uv_buf_init(&token, sizeof(token));
+  int status = cm::uv_write(this->GetWriter(), &buf, 1, this->OnWrite);
+  if (status != 0) {
+    this->Disconnect(status);
+  }
+}
+
+void ImplPosix::StartReceivingTokens()
+{
+  if (this->Conn == Connection::None) {
+    return;
+  }
+  if (this->ReceivingTokens) {
+    return;
+  }
+
+  int status = uv_read_start(this->GetReader(), &ImplPosix::OnAllocateCB,
+                             &ImplPosix::OnReadCB);
+  if (status != 0) {
+    this->Disconnect(status);
+    return;
+  }
+
+  this->ReceivingTokens = true;
+}
+
+void ImplPosix::StopReceivingTokens()
+{
+  if (this->Conn == Connection::None) {
+    return;
+  }
+  if (!this->ReceivingTokens) {
+    return;
+  }
+
+  this->ReceivingTokens = false;
+  uv_read_stop(this->GetReader());
+}
+}
+#endif
+
+//---------------------------------------------------------------------------
+// Implementation of public interface.
+
+cmUVJobServerClient::cmUVJobServerClient(std::unique_ptr<Impl> impl)
+  : Impl_(std::move(impl))
+{
+}
+
+cmUVJobServerClient::~cmUVJobServerClient() = default;
+
+cmUVJobServerClient::cmUVJobServerClient(cmUVJobServerClient&&) noexcept =
+  default;
+cmUVJobServerClient& cmUVJobServerClient::operator=(
+  cmUVJobServerClient&&) noexcept = default;
+
+void cmUVJobServerClient::RequestToken()
+{
+  this->Impl_->RequestToken();
+}
+
+void cmUVJobServerClient::ReleaseToken()
+{
+  this->Impl_->ReleaseToken();
+}
+
+int cmUVJobServerClient::GetHeldTokens() const
+{
+  return this->Impl_->HeldTokens;
+}
+
+int cmUVJobServerClient::GetNeedTokens() const
+{
+  return this->Impl_->NeedTokens;
+}
+
+cm::optional<cmUVJobServerClient> cmUVJobServerClient::Connect(
+  uv_loop_t& loop, std::function<void()> onToken,
+  std::function<void(int)> onDisconnect)
+{
+#if defined(_WIN32)
+  // FIXME: Windows job server client not yet implemented.
+  static_cast<void>(loop);
+  static_cast<void>(onToken);
+  static_cast<void>(onDisconnect);
+#else
+  auto impl = cm::make_unique<ImplPosix>(loop);
+  if (impl && impl->IsConnected()) {
+    impl->OnToken = std::move(onToken);
+    impl->OnDisconnect = std::move(onDisconnect);
+    return cmUVJobServerClient(std::move(impl));
+  }
+#endif
+  return cm::nullopt;
+}
diff --git a/Source/CTest/cmUVJobServerClient.h b/Source/CTest/cmUVJobServerClient.h
new file mode 100644
index 0000000..bbb5f08
--- /dev/null
+++ b/Source/CTest/cmUVJobServerClient.h
@@ -0,0 +1,96 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <functional>
+#include <memory>
+
+#include <cm/optional>
+
+#include <cm3p/uv.h>
+
+/** \class cmUVJobServerClient
+ * \brief Job server client that can integrate with a libuv event loop.
+ *
+ * Use the \a Connect method to connect to an ambient job server as
+ * described by the MAKEFLAGS environment variable, if any.  Request
+ * a token using the \a RequestToken method.  The \a onToken callback
+ * will be invoked asynchronously when the token is received.  Act
+ * on the token, and then use \a ReleaseToken to release it.
+ *
+ * The job server protocol states that a client process implicitly
+ * has one free token available, corresponding to the token its
+ * parent used to start it.  \a cmUVJobServerClient will use the
+ * implicit token whenever it is available instead of requesting
+ * an explicit token from the job server.  However, clients of
+ * this class must still request and receive the token before
+ * acting on it, and cannot assume that it is always held.
+ *
+ * If the job server connection breaks, \a onDisconnect will be
+ * called with the libuv error.  No further tokens can be received
+ * from the job server, but progress can still be made serially
+ * using the implicit token.
+ */
+class cmUVJobServerClient
+{
+public:
+  class Impl;
+
+private:
+  std::unique_ptr<Impl> Impl_;
+
+  cmUVJobServerClient(std::unique_ptr<Impl> impl);
+
+public:
+  /**
+   * Disconnect from the job server.
+   */
+  ~cmUVJobServerClient();
+
+  cmUVJobServerClient(cmUVJobServerClient&&) noexcept;
+  cmUVJobServerClient(cmUVJobServerClient const&) = delete;
+  cmUVJobServerClient& operator=(cmUVJobServerClient&&) noexcept;
+  cmUVJobServerClient& operator=(cmUVJobServerClient const&) = delete;
+
+  /**
+   * Request a token from the job server.
+   * When the token is held, the \a onToken callback will be invoked.
+   */
+  void RequestToken();
+
+  /**
+   * Release a token to the job server.
+   * This may be called only after a corresponding \a onToken callback.
+   */
+  void ReleaseToken();
+
+  /**
+   * Get the number of implicit and explicit tokens currently held.
+   * This is the number of times \a onToken has been called but not
+   * yet followed by a call to \a ReleaseToken.
+   * This is meant for testing and debugging.
+   */
+  int GetHeldTokens() const;
+
+  /**
+   * Get the number of explicit tokens currently requested from the
+   * job server but not yet received.  If the implicit token becomes
+   * available, it is used in place of a requested token, and this
+   * is decremented without receiving an explicit token.
+   * This is meant for testing and debugging.
+   */
+  int GetNeedTokens() const;
+
+  /**
+   * Connect to an ambient job server, if any.
+   * \param loop          The libuv event loop on which to schedule events.
+   * \param onToken       Function to call when a new token is held.
+   * \param onDisconnect  Function to call on disconnect, with libuv error.
+   * \returns             Connected instance, or cm::nullopt.
+   */
+  static cm::optional<cmUVJobServerClient> Connect(
+    uv_loop_t& loop, std::function<void()> onToken,
+    std::function<void(int)> onDisconnect);
+};
diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt
index bc6b906..5d0e240 100644
--- a/Source/Checks/Curses/CMakeLists.txt
+++ b/Source/Checks/Curses/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR)
 project(CheckCurses C)
 
 set(CURSES_NEED_NCURSES TRUE)
diff --git a/Source/Checks/Curses/CheckCurses.c b/Source/Checks/Curses/CheckCurses.c
index 7d827e6..3264fa0 100644
--- a/Source/Checks/Curses/CheckCurses.c
+++ b/Source/Checks/Curses/CheckCurses.c
@@ -8,7 +8,7 @@
 #  include <curses.h>
 #endif
 
-int main()
+int main(void)
 {
   curses_version();
   return 0;
diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx
index c8df589..bdc7c6d 100644
--- a/Source/Checks/cm_cxx_filesystem.cxx
+++ b/Source/Checks/cm_cxx_filesystem.cxx
@@ -9,12 +9,13 @@
   std::filesystem::path p0(L"/a/b/c");
 
   std::filesystem::path p1("/a/b/c");
-  std::filesystem::path p2("/a/b/c");
-  if (p1 != p2) {
+  std::filesystem::path p2("/a/b//c");
+  if (p1 != p2.lexically_normal()) {
     return 1;
   }
 
 #if defined(_WIN32)
+  // "//host/" is not preserved in some environments like GNU under MinGW.
   std::filesystem::path p3("//host/a/b/../c");
   if (p3.lexically_normal().generic_string() != "//host/a/c") {
     return 1;
@@ -24,6 +25,12 @@
   if (p4.lexically_normal().generic_string() != "c:/a/") {
     return 1;
   }
+
+  std::filesystem::path b1("C:\\path\\y\\..\\");
+  if (std::filesystem::weakly_canonical("\\\\?\\C:\\path\\x\\..") !=
+      b1.lexically_normal()) {
+    return 1;
+  }
 #endif
 
   // If std::string is copy-on-write, the std::filesystem::path
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index 77a0048..72460f3 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -962,6 +962,11 @@
   if (r < 0) {
     return r;
   }
+
+  // Process presets before loading the cache
+  this->CMakeInstance->ProcessPresetVariables();
+  this->CMakeInstance->ProcessPresetEnvironment();
+
   this->CMakeInstance->SetCacheArgs(this->Args);
   this->CMakeInstance->PreLoadCMakeFiles();
   return r;
diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
index 85b379b..f1d351a 100644
--- a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
+++ b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
@@ -667,6 +667,10 @@
 
 #include <cstddef>
 
+#ifndef _WIN32
+#  include <termios.h>
+#endif
+
 /*--------------------------------------------------------------------------*/
 
 #define INITIAL 0
diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
index 2befa85..ac9cbaf 100644
--- a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
+++ b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
@@ -26,6 +26,10 @@
 
 #include <cstddef>
 
+#ifndef _WIN32
+#  include <termios.h>
+#endif
+
 /*--------------------------------------------------------------------------*/
 %}
 
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake
index 21f04e6..4e7f0fe 100644
--- a/Source/Modules/CMakeBuildUtilities.cmake
+++ b/Source/Modules/CMakeBuildUtilities.cmake
@@ -287,6 +287,8 @@
   set(ENABLE_CPIO_SHARED OFF)
   set(ENABLE_CAT OFF)
   set(ENABLE_CAT_SHARED OFF)
+  set(ENABLE_UNZIP OFF)
+  set(ENABLE_UNZIP_SHARED OFF)
   set(ENABLE_XATTR OFF)
   set(ENABLE_ACL OFF)
   set(ENABLE_ICONV OFF)
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index ab77818..2a6a831 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -875,7 +875,7 @@
     if (preset.setToolset) {
       dialog.setToolset(preset.toolset);
     }
-    dialog.setCompilerOption(CompilerOption::DefaultNative);
+    dialog.setCompilerOption(CompilerOption::DefaultPreset);
   }
 
   if (dialog.exec() == QDialog::Accepted) {
diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx
index a454cb6..2986e1f 100644
--- a/Source/QtDialog/FirstConfigure.cxx
+++ b/Source/QtDialog/FirstConfigure.cxx
@@ -159,6 +159,10 @@
 {
   std::size_t index = 0;
   switch (option) {
+    case CompilerOption::DefaultPreset:
+      this->CompilerSetupOptions[0]->setText(
+        tr("Use default preset compilers"));
+      CM_FALLTHROUGH;
     case CompilerOption::DefaultNative:
       index = 0;
       break;
diff --git a/Source/QtDialog/FirstConfigure.h b/Source/QtDialog/FirstConfigure.h
index 5844f3a..ea6fae6 100644
--- a/Source/QtDialog/FirstConfigure.h
+++ b/Source/QtDialog/FirstConfigure.h
@@ -24,6 +24,7 @@
 
 enum class CompilerOption
 {
+  DefaultPreset,
   DefaultNative,
   SpecifyNative,
   ToolchainFile,
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index f43f05f..8d63f6d 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -378,6 +378,54 @@
   this->CMakeInstance->SaveCache(this->BinaryDirectory.toStdString());
 }
 
+namespace {
+template <typename T>
+QCMakeProperty cache_to_property(const T& v)
+{
+  QCMakeProperty prop;
+  prop.Key = QString::fromStdString(v.first);
+  prop.Value = QString::fromStdString(v.second->Value);
+  prop.Type = QCMakeProperty::STRING;
+  if (!v.second->Type.empty()) {
+    auto type = cmState::StringToCacheEntryType(v.second->Type);
+    switch (type) {
+      case cmStateEnums::BOOL:
+        prop.Type = QCMakeProperty::BOOL;
+        prop.Value = cmIsOn(v.second->Value);
+        break;
+      case cmStateEnums::PATH:
+        prop.Type = QCMakeProperty::PATH;
+        break;
+      case cmStateEnums::FILEPATH:
+        prop.Type = QCMakeProperty::FILEPATH;
+        break;
+      default:
+        prop.Type = QCMakeProperty::STRING;
+        break;
+    }
+  }
+  return prop;
+}
+
+void add_to_property_list(QCMakePropertyList& list, QCMakeProperty&& prop)
+{
+  // QCMakeCacheModel prefers variables earlier in the list rather than
+  // later, so overwrite them if they already exist rather than simply
+  // appending
+  bool found = false;
+  for (auto& orig : list) {
+    if (orig.Key == prop.Key) {
+      orig = prop;
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    list.append(prop);
+  }
+}
+}
+
 QCMakePropertyList QCMake::properties() const
 {
   QCMakePropertyList ret;
@@ -423,47 +471,21 @@
     auto const& p =
       this->CMakePresetsGraph.ConfigurePresets.at(presetName).Expanded;
     if (p) {
+      if (!p->ToolchainFile.empty()) {
+        using CacheVariable = cmCMakePresetsGraph::CacheVariable;
+        CacheVariable var{ "FILEPATH", p->ToolchainFile };
+        std::pair<std::string, cm::optional<CacheVariable>> value = {
+          "CMAKE_TOOLCHAIN_FILE", var
+        };
+        auto prop = cache_to_property(value);
+        add_to_property_list(ret, std::move(prop));
+      }
       for (auto const& v : p->CacheVariables) {
         if (!v.second) {
           continue;
         }
-        QCMakeProperty prop;
-        prop.Key = QString::fromStdString(v.first);
-        prop.Value = QString::fromStdString(v.second->Value);
-        prop.Type = QCMakeProperty::STRING;
-        if (!v.second->Type.empty()) {
-          auto type = cmState::StringToCacheEntryType(v.second->Type);
-          switch (type) {
-            case cmStateEnums::BOOL:
-              prop.Type = QCMakeProperty::BOOL;
-              prop.Value = cmIsOn(v.second->Value);
-              break;
-            case cmStateEnums::PATH:
-              prop.Type = QCMakeProperty::PATH;
-              break;
-            case cmStateEnums::FILEPATH:
-              prop.Type = QCMakeProperty::FILEPATH;
-              break;
-            default:
-              prop.Type = QCMakeProperty::STRING;
-              break;
-          }
-        }
-
-        // QCMakeCacheModel prefers variables earlier in the list rather than
-        // later, so overwrite them if they already exist rather than simply
-        // appending
-        bool found = false;
-        for (auto& orig : ret) {
-          if (orig.Key == prop.Key) {
-            orig = prop;
-            found = true;
-            break;
-          }
-        }
-        if (!found) {
-          ret.append(prop);
-        }
+        auto prop = cache_to_property(v);
+        add_to_property_list(ret, std::move(prop));
       }
     }
   }
diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h
index 89068ab..549cca2 100644
--- a/Source/QtDialog/QCMakeCacheView.h
+++ b/Source/QtDialog/QCMakeCacheView.h
@@ -91,10 +91,6 @@
                       const QString& description, const QVariant& value,
                       bool advanced);
 
-  // set the view type
-  void setViewType(ViewType t);
-  ViewType viewType() const;
-
 public:
   // get the properties
   QCMakePropertyList properties() const;
@@ -112,6 +108,10 @@
   // get the data in the model for this property
   void getPropertyData(const QModelIndex& idx1, QCMakeProperty& prop) const;
 
+  // set the view type
+  void setViewType(ViewType t);
+  ViewType viewType() const;
+
 protected:
   bool EditEnabled;
   int NewPropertyCount;
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index b1589ff..ea97287 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -189,8 +189,8 @@
       } else if (copy == keyDEPFILE) {
         doing = doing_depfile;
         if (!mf.GetGlobalGenerator()->SupportsCustomCommandDepfile()) {
-          status.SetError("Option DEPFILE not supported by " +
-                          mf.GetGlobalGenerator()->GetName());
+          status.SetError(cmStrCat("Option DEPFILE not supported by ",
+                                   mf.GetGlobalGenerator()->GetName()));
           return false;
         }
       } else if (copy == keyJOB_POOL) {
diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx
index 42f1ad3..5bf7bed 100644
--- a/Source/cmBlockCommand.cxx
+++ b/Source/cmBlockCommand.cxx
@@ -127,6 +127,10 @@
       inStatus.SetContinueInvoked();
       return true;
     }
+    if (status.HasExitCode()) {
+      inStatus.SetExitCode(status.GetExitCode());
+      return true;
+    }
     if (cmSystemTools::GetFatalErrorOccurred()) {
       return true;
     }
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 0efb9a4..e4160a1 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -178,7 +178,7 @@
         if (std::isalpha(ch) || ch == '_') {
           key += ch;
           state = PARSE_KEY;
-        } else if (!std::isspace(ch)) {
+        } else if (!cmIsSpace(ch)) {
           state = IGNORE_REST;
         }
         break;
@@ -238,7 +238,7 @@
         break;
 
       case PARSE_VALUE:
-        if (ch == '#' || std::isspace(ch)) {
+        if (ch == '#' || cmIsSpace(ch)) {
           state = IGNORE_REST;
         } else {
           value += ch;
@@ -270,7 +270,7 @@
 
   std::map<std::string, std::string> data;
   // Based on
-  // https://www.freedesktop.org/software/systemd/man/os-release.html
+  // https://www.freedesktop.org/software/systemd/man/latest/os-release.html
   for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) {
     const auto& filename = cmStrCat(sysroot, name);
     if (cmSystemTools::FileExists(filename)) {
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index 329427c..9ffc363 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -398,6 +398,32 @@
   if (!moreArgs()) {
     return FatalError(status, "called with incorrect number of arguments");
   }
+  if (expArgs[expArg] == "EXIT"_s) {
+    ++expArg; // consume "EXIT".
+
+    if (!moreArgs()) {
+      return FatalError(status, "EXIT requires one argument");
+    }
+
+    auto workingMode =
+      status.GetMakefile().GetCMakeInstance()->GetWorkingMode();
+    if (workingMode != cmake::SCRIPT_MODE) {
+      return FatalError(status, "EXIT can be used only in SCRIPT mode");
+    }
+
+    long retCode = 0;
+
+    if (!cmStrToLong(expArgs[expArg], &retCode)) {
+      return FatalError(status,
+                        cmStrCat("EXIT requires one integral argument, got \"",
+                                 expArgs[expArg], '\"'));
+    }
+
+    if (workingMode == cmake::SCRIPT_MODE) {
+      status.SetExitCode(static_cast<int>(retCode));
+    }
+    return true;
+  }
 
   if (expArgs[expArg] == "SET_DEPENDENCY_PROVIDER"_s) {
     finishArgs();
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 988a4eb..7acee4b 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <cctype>
 #include <chrono>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -24,13 +25,13 @@
 #include <cmext/string_view>
 
 #include <cm3p/curl/curl.h>
+#include <cm3p/uv.h>
 #include <cm3p/zlib.h>
 
 #include "cmsys/Base64.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/SystemInformation.hxx"
 #if defined(_WIN32)
@@ -64,6 +65,9 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
@@ -1073,9 +1077,9 @@
 // ######################################################################
 // ######################################################################
 
-int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
-                            int* retVal, const char* dir, cmDuration timeout,
-                            std::ostream& ofs, Encoding encoding)
+bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
+                             int* retVal, const char* dir, cmDuration timeout,
+                             std::ostream& ofs, Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -1084,107 +1088,107 @@
     return false;
   }
 
-  std::vector<const char*> argv;
-  argv.reserve(args.size() + 1);
-  for (std::string const& a : args) {
-    argv.push_back(a.c_str());
-  }
-  argv.push_back(nullptr);
-
   output.clear();
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
-  for (char const* arg : argv) {
-    if (!arg) {
-      break;
-    }
+  for (auto const& arg : args) {
     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
   }
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args).SetMergedBuiltinStreams();
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
+  }
+  auto chain = builder.Start();
+  cm::uv_pipe_ptr outputStream;
+  outputStream.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputStream, chain.OutputStream());
 
   // Initialize tick's
   std::string::size_type tick = 0;
   std::string::size_type tick_len = 1024;
   std::string::size_type tick_line_len = 50;
 
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              "   Each . represents " << tick_len
                                      << " bytes of output\n"
                                         "    "
                                      << std::flush);
-  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-    processOutput.DecodeText(data, length, strdata);
-    for (char& cc : strdata) {
-      if (cc == 0) {
-        cc = '\n';
+  auto outputHandle = cmUVStreamRead(
+    outputStream,
+    [this, &processOutput, &output, &tick, &tick_len, &tick_line_len,
+     &ofs](std::vector<char> data) {
+      std::string strdata;
+      processOutput.DecodeText(data.data(), data.size(), strdata);
+      for (char& cc : strdata) {
+        if (cc == 0) {
+          cc = '\n';
+        }
       }
-    }
-    output.append(strdata);
-    while (output.size() > (tick * tick_len)) {
-      tick++;
-      cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
-      if (tick % tick_line_len == 0 && tick > 0) {
-        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
-                   "  Size: " << int((double(output.size()) / 1024.0) + 1)
-                              << "K\n    " << std::flush);
+      output.append(strdata);
+      while (output.size() > (tick * tick_len)) {
+        tick++;
+        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
+        if (tick % tick_line_len == 0 && tick > 0) {
+          cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
+                     "  Size: " << int((double(output.size()) / 1024.0) + 1)
+                                << "K\n    " << std::flush);
+        }
       }
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (ofs) {
-      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata);
-  if (!strdata.empty()) {
-    output.append(strdata);
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (ofs) {
-      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-    }
-  }
+      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
+      if (ofs) {
+        ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+      }
+    },
+    [this, &processOutput, &output, &ofs]() {
+      std::string strdata;
+      processOutput.DecodeText(std::string(), strdata);
+      if (!strdata.empty()) {
+        output.append(strdata);
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
+        if (ofs) {
+          ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+        }
+      }
+    });
+
+  bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
                                  << std::endl);
 
-  cmsysProcess_WaitForExit(cp, nullptr);
-
-  int result = cmsysProcess_GetState(cp);
-
-  if (result == cmsysProcess_State_Exited) {
-    *retVal = cmsysProcess_GetExitValue(cp);
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               "Command exited with the value: " << *retVal << std::endl);
-  } else if (result == cmsysProcess_State_Exception) {
-    *retVal = cmsysProcess_GetExitException(cp);
-    cmCTestLog(this, WARNING,
-               "There was an exception: " << *retVal << std::endl);
-  } else if (result == cmsysProcess_State_Expired) {
+  if (finished) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        *retVal = static_cast<int>(status.ExitStatus);
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   "Command exited with the value: " << *retVal << std::endl);
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        output += "\n*** ERROR executing: ";
+        output += exception.second;
+        output += "\n***The build process failed.";
+        cmCTestLog(this, ERROR_MESSAGE,
+                   "There was an error: " << exception.second << std::endl);
+        break;
+      default:
+        *retVal = static_cast<int>(exception.first);
+        cmCTestLog(this, WARNING,
+                   "There was an exception: " << *retVal << std::endl);
+        break;
+    }
+  } else {
     cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
-  } else if (result == cmsysProcess_State_Error) {
-    output += "\n*** ERROR executing: ";
-    output += cmsysProcess_GetErrorString(cp);
-    output += "\n***The build process failed.";
-    cmCTestLog(this, ERROR_MESSAGE,
-               "There was an error: " << cmsysProcess_GetErrorString(cp)
-                                      << std::endl);
   }
 
-  cmsysProcess_Delete(cp);
-
-  return result;
+  return true;
 }
 
 // ######################################################################
@@ -1192,9 +1196,10 @@
 // ######################################################################
 // ######################################################################
 
-int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
-                     int* retVal, std::ostream* log, cmDuration testTimeOut,
-                     std::vector<std::string>* environment, Encoding encoding)
+bool cmCTest::RunTest(const std::vector<std::string>& argv,
+                      std::string* output, int* retVal, std::ostream* log,
+                      cmDuration testTimeOut,
+                      std::vector<std::string>* environment, Encoding encoding)
 {
   bool modifyEnv = (environment && !environment->empty());
 
@@ -1233,19 +1238,16 @@
     inst.SetStreams(&oss, &oss);
 
     std::vector<std::string> args;
-    for (char const* i : argv) {
-      if (i) {
-        // make sure we pass the timeout in for any build and test
-        // invocations. Since --build-generator is required this is a
-        // good place to check for it, and to add the arguments in
-        if (strcmp(i, "--build-generator") == 0 &&
-            timeout != cmCTest::MaxDuration() &&
-            timeout > cmDuration::zero()) {
-          args.emplace_back("--test-timeout");
-          args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
-        }
-        args.emplace_back(i);
+    for (auto const& i : argv) {
+      // make sure we pass the timeout in for any build and test
+      // invocations. Since --build-generator is required this is a
+      // good place to check for it, and to add the arguments in
+      if (i == "--build-generator" && timeout != cmCTest::MaxDuration() &&
+          timeout > cmDuration::zero()) {
+        args.emplace_back("--test-timeout");
+        args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
       }
+      args.emplace_back(i);
     }
     if (log) {
       *log << "* Run internal CTest" << std::endl;
@@ -1271,7 +1273,7 @@
                                                              << std::endl);
     }
 
-    return cmsysProcess_State_Exited;
+    return true;
   }
   std::vector<char> tempOutput;
   if (output) {
@@ -1284,41 +1286,43 @@
     cmSystemTools::AppendEnv(*environment);
   }
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(argv).SetMergedBuiltinStreams();
   cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  }
+  auto chain = builder.Start();
 
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
-
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
-  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-    processOutput.DecodeText(data, length, strdata);
-    if (output) {
-      cm::append(tempOutput, data, data + length);
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (log) {
-      log->write(strdata.c_str(), strdata.size());
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata);
-  if (!strdata.empty()) {
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (log) {
-      log->write(strdata.c_str(), strdata.size());
-    }
-  }
+  cm::uv_pipe_ptr outputStream;
+  outputStream.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputStream, chain.OutputStream());
+  auto outputHandle = cmUVStreamRead(
+    outputStream,
+    [this, &processOutput, &output, &tempOutput,
+     &log](std::vector<char> data) {
+      std::string strdata;
+      processOutput.DecodeText(data.data(), data.size(), strdata);
+      if (output) {
+        cm::append(tempOutput, data.data(), data.data() + data.size());
+      }
+      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
+      if (log) {
+        log->write(strdata.c_str(), strdata.size());
+      }
+    },
+    [this, &processOutput, &log]() {
+      std::string strdata;
+      processOutput.DecodeText(std::string(), strdata);
+      if (!strdata.empty()) {
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
+        if (log) {
+          log->write(strdata.c_str(), strdata.size());
+        }
+      }
+    });
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
   processOutput.DecodeText(tempOutput, tempOutput);
   if (output && tempOutput.begin() != tempOutput.end()) {
     output->append(tempOutput.data(), tempOutput.size());
@@ -1326,33 +1330,41 @@
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
              "-- Process completed" << std::endl);
 
-  int result = cmsysProcess_GetState(cp);
+  bool result = false;
 
-  if (result == cmsysProcess_State_Exited) {
-    *retVal = cmsysProcess_GetExitValue(cp);
-    if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
-      this->OutputTestErrors(tempOutput);
+  if (complete) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        *retVal = static_cast<int>(status.ExitStatus);
+        if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
+          this->OutputTestErrors(tempOutput);
+        }
+        result = true;
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn: {
+        std::string outerr =
+          cmStrCat("\n*** ERROR executing: ", exception.second);
+        if (output) {
+          *output += outerr;
+        }
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
+      } break;
+      default: {
+        if (this->Impl->OutputTestOutputOnTestFailure) {
+          this->OutputTestErrors(tempOutput);
+        }
+        *retVal = status.TermSignal;
+        std::string outerr =
+          cmStrCat("\n*** Exception executing: ", exception.second);
+        if (output) {
+          *output += outerr;
+        }
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
+      } break;
     }
-  } else if (result == cmsysProcess_State_Exception) {
-    if (this->Impl->OutputTestOutputOnTestFailure) {
-      this->OutputTestErrors(tempOutput);
-    }
-    *retVal = cmsysProcess_GetExitException(cp);
-    std::string outerr = cmStrCat("\n*** Exception executing: ",
-                                  cmsysProcess_GetExceptionString(cp));
-    if (output) {
-      *output += outerr;
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
-  } else if (result == cmsysProcess_State_Error) {
-    std::string outerr =
-      cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
-    if (output) {
-      *output += outerr;
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
   }
-  cmsysProcess_Delete(cp);
 
   return result;
 }
@@ -2212,6 +2224,22 @@
                                                     args[i]);
   }
 
+  else if (this->CheckArgument(arg, "--tests-from-file"_s) &&
+           i < args.size() - 1) {
+    i++;
+    this->GetTestHandler()->SetPersistentOption("TestListFile", args[i]);
+    this->GetMemCheckHandler()->SetPersistentOption("TestListFile", args[i]);
+  }
+
+  else if (this->CheckArgument(arg, "--exclude-from-file"_s) &&
+           i < args.size() - 1) {
+    i++;
+    this->GetTestHandler()->SetPersistentOption("ExcludeTestListFile",
+                                                args[i]);
+    this->GetMemCheckHandler()->SetPersistentOption("ExcludeTestListFile",
+                                                    args[i]);
+  }
+
   else if (this->CheckArgument(arg, "--rerun-failed"_s)) {
     this->GetTestHandler()->SetPersistentOption("RerunFailed", "true");
     this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true");
@@ -3470,49 +3498,70 @@
   stdOut->clear();
   stdErr->clear();
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
   }
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+
+  cm::uv_timer_ptr timer;
+  bool timedOut = false;
+  if (timeout.count()) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+  }
 
   std::vector<char> tempOutput;
+  bool outFinished = false;
+  cm::uv_pipe_ptr outStream;
   std::vector<char> tempError;
-  char* data;
-  int length;
+  bool errFinished = false;
+  cm::uv_pipe_ptr errStream;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
-  int res;
-  bool done = false;
-  while (!done) {
-    res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
-    switch (res) {
-      case cmsysProcess_Pipe_STDOUT:
-        cm::append(tempOutput, data, data + length);
-        break;
-      case cmsysProcess_Pipe_STDERR:
-        cm::append(tempError, data, data + length);
-        break;
-      default:
-        done = true;
-    }
-    if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
-        this->Impl->ExtraVerbose) {
-      processOutput.DecodeText(data, length, strdata);
-      cmSystemTools::Stdout(strdata);
-    }
+  auto startRead = [this, &chain, &processOutput](
+                     cm::uv_pipe_ptr& pipe, int stream,
+                     std::vector<char>& temp,
+                     bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
+    pipe.init(chain.GetLoop(), 0);
+    uv_pipe_open(pipe, stream);
+    return cmUVStreamRead(
+      pipe,
+      [this, &temp, &processOutput](std::vector<char> data) {
+        cm::append(temp, data);
+        if (this->Impl->ExtraVerbose) {
+          std::string strdata;
+          processOutput.DecodeText(data.data(), data.size(), strdata);
+          cmSystemTools::Stdout(strdata);
+        }
+      },
+      [&finished]() { finished = true; });
+  };
+  auto outputHandle =
+    startRead(outStream, chain.OutputStream(), tempOutput, outFinished);
+  auto errorHandle =
+    startRead(errStream, chain.ErrorStream(), tempError, errFinished);
+  while (!timedOut && !(outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
   if (this->Impl->ExtraVerbose) {
+    std::string strdata;
     processOutput.DecodeText(std::string(), strdata);
     if (!strdata.empty()) {
       cmSystemTools::Stdout(strdata);
     }
   }
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  while (!timedOut && !chain.Finished()) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
   if (!tempOutput.empty()) {
     processOutput.DecodeText(tempOutput, tempOutput);
     stdOut->append(tempOutput.data(), tempOutput.size());
@@ -3523,32 +3572,32 @@
   }
 
   bool result = true;
-  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-    } else {
-      if (cmsysProcess_GetExitValue(cp) != 0) {
-        result = false;
-      }
-    }
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
-    const char* exception_str = cmsysProcess_GetExceptionString(cp);
-    cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
-    stdErr->append(exception_str, strlen(exception_str));
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
-    const char* error_str = cmsysProcess_GetErrorString(cp);
-    cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
-    stdErr->append(error_str, strlen(error_str));
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
+  if (timedOut) {
     const char* error_str = "Process terminated due to timeout\n";
     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
     stdErr->append(error_str, strlen(error_str));
     result = false;
+  } else {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+        } else {
+          if (status.ExitStatus != 0) {
+            result = false;
+          }
+        }
+        break;
+      default: {
+        cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl);
+        stdErr->append(exception.second);
+        result = false;
+      } break;
+    }
   }
 
-  cmsysProcess_Delete(cp);
   return result;
 }
 
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index 9a8d5a6..1644d84 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -254,10 +254,10 @@
    * Run command specialized for make and configure. Returns process status
    * and retVal is return value or exception.
    */
-  int RunMakeCommand(const std::string& command, std::string& output,
-                     int* retVal, const char* dir, cmDuration timeout,
-                     std::ostream& ofs,
-                     Encoding encoding = cmProcessOutput::Auto);
+  bool RunMakeCommand(const std::string& command, std::string& output,
+                      int* retVal, const char* dir, cmDuration timeout,
+                      std::ostream& ofs,
+                      Encoding encoding = cmProcessOutput::Auto);
 
   /** Return the current tag */
   std::string GetCurrentTag();
@@ -303,10 +303,10 @@
    * environment variables prior to running the test. After running the test,
    * environment variables are restored to their previous values.
    */
-  int RunTest(std::vector<const char*> args, std::string* output, int* retVal,
-              std::ostream* logfile, cmDuration testTimeOut,
-              std::vector<std::string>* environment,
-              Encoding encoding = cmProcessOutput::Auto);
+  bool RunTest(const std::vector<std::string>& args, std::string* output,
+               int* retVal, std::ostream* logfile, cmDuration testTimeOut,
+               std::vector<std::string>* environment,
+               Encoding encoding = cmProcessOutput::Auto);
 
   /**
    * Get the handler object
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 320c57c..f4b26f3 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -16,6 +16,8 @@
 #include <cm/string_view>
 #include <cmext/string_view>
 
+#include "cmsys/RegularExpression.hxx"
+
 #include "cmComputeComponentGraph.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionDAGChecker.h"
@@ -26,6 +28,7 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -184,15 +187,6 @@
 
 namespace {
 // LINK_LIBRARY helpers
-const auto LL_BEGIN = "<LINK_LIBRARY:"_s;
-const auto LL_END = "</LINK_LIBRARY:"_s;
-
-inline std::string ExtractFeature(std::string const& item)
-{
-  return item.substr(LL_BEGIN.length(),
-                     item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
-}
-
 bool IsFeatureSupported(cmMakefile* makefile, std::string const& linkLanguage,
                         std::string const& feature)
 {
@@ -231,9 +225,208 @@
     cmStrCat("CMAKE_LINK_GROUP_USING_", feature, "_SUPPORTED");
   return makefile->GetDefinition(featureSupported).IsOn();
 }
+
+class EntriesProcessing
+{
+public:
+  using LinkEntry = cmComputeLinkDepends::LinkEntry;
+  using EntryVector = cmComputeLinkDepends::EntryVector;
+
+  EntriesProcessing(const cmGeneratorTarget* target,
+                    const std::string& linkLanguage, EntryVector& entries,
+                    EntryVector& finalEntries)
+    : Entries(entries)
+    , FinalEntries(finalEntries)
+  {
+    const auto* makefile = target->Makefile;
+
+    switch (target->GetPolicyStatusCMP0156()) {
+      case cmPolicies::WARN:
+        if (!makefile->GetCMakeInstance()->GetIsInTryCompile() &&
+            makefile->PolicyOptionalWarningEnabled(
+              "CMAKE_POLICY_WARNING_CMP0156")) {
+          makefile->GetCMakeInstance()->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0156),
+                     "\nSince the policy is not set, legacy libraries "
+                     "de-duplication strategy will be applied."),
+            target->GetBacktrace());
+        }
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        // rely on default initialization of the class
+        break;
+      case cmPolicies::REQUIRED_IF_USED:
+      case cmPolicies::REQUIRED_ALWAYS:
+        makefile->GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0156),
+          target->GetBacktrace());
+        CM_FALLTHROUGH;
+      case cmPolicies::NEW: {
+        if (auto libProcessing = makefile->GetDefinition(cmStrCat(
+              "CMAKE_", linkLanguage, "_LINK_LIBRARIES_PROCESSING"))) {
+          cmsys::RegularExpression processingOption{
+            "^(ORDER|UNICITY)=(FORWARD|REVERSE|ALL|NONE|SHARED)$"
+          };
+          std::string errorMessage;
+          for (auto const& option : cmList{ libProcessing }) {
+            if (processingOption.find(option)) {
+              if (processingOption.match(1) == "ORDER") {
+                if (processingOption.match(2) == "FORWARD") {
+                  this->Order = Forward;
+                } else if (processingOption.match(2) == "REVERSE") {
+                  this->Order = Reverse;
+                } else {
+                  errorMessage += cmStrCat("  ", option, '\n');
+                }
+              } else if (processingOption.match(1) == "UNICITY") {
+                if (processingOption.match(2) == "ALL") {
+                  this->Unicity = All;
+                } else if (processingOption.match(2) == "NONE") {
+                  this->Unicity = None;
+                } else if (processingOption.match(2) == "SHARED") {
+                  this->Unicity = Shared;
+                } else {
+                  errorMessage += cmStrCat("  ", option, '\n');
+                }
+              }
+            } else {
+              errorMessage += cmStrCat("  ", option, '\n');
+            }
+          }
+          if (!errorMessage.empty()) {
+            makefile->GetCMakeInstance()->IssueMessage(
+              MessageType::FATAL_ERROR,
+              cmStrCat("Erroneous option(s) for 'CMAKE_", linkLanguage,
+                       "_LINK_LIBRARIES_PROCESSING':\n", errorMessage),
+              target->GetBacktrace());
+          }
+        }
+      }
+    }
+  }
+
+  void AddGroups(const std::map<size_t, std::vector<size_t>>& groups)
+  {
+    if (!groups.empty()) {
+      this->Groups = &groups;
+      // record all libraries as part of groups to ensure correct
+      // deduplication: libraries as part of groups are always kept.
+      for (const auto& group : groups) {
+        for (auto index : group.second) {
+          this->Emitted.insert(index);
+        }
+      }
+    }
+  }
+
+  void AddLibraries(const std::vector<size_t>& libEntries)
+  {
+    if (this->Order == Reverse) {
+      // Iterate in reverse order so we can keep only the last occurrence
+      // of a library.
+      this->AddLibraries(cmReverseRange(libEntries));
+    } else {
+      this->AddLibraries(cmMakeRange(libEntries));
+    }
+  }
+
+  void AddObjects(const std::vector<size_t>& objectEntries)
+  {
+    // Place explicitly linked object files in the front.  The linker will
+    // always use them anyway, and they may depend on symbols from libraries.
+    if (this->Order == Reverse) {
+      // Append in reverse order at the end since we reverse the final order.
+      for (auto index : cmReverseRange(objectEntries)) {
+        this->FinalEntries.emplace_back(this->Entries[index]);
+      }
+    } else {
+      // Append in reverse order to ensure correct final order
+      for (auto index : cmReverseRange(objectEntries)) {
+        this->FinalEntries.emplace(this->FinalEntries.begin(),
+                                   this->Entries[index]);
+      }
+    }
+  }
+
+  void Finalize()
+  {
+    if (this->Order == Reverse) {
+      // Reverse the resulting order since we iterated in reverse.
+      std::reverse(this->FinalEntries.begin(), this->FinalEntries.end());
+    }
+
+    // expand groups
+    if (this->Groups != nullptr) {
+      for (const auto& group : *this->Groups) {
+        const LinkEntry& groupEntry = this->Entries[group.first];
+        auto it = this->FinalEntries.begin();
+        while (true) {
+          it = std::find_if(it, this->FinalEntries.end(),
+                            [&groupEntry](const LinkEntry& entry) -> bool {
+                              return groupEntry.Item == entry.Item;
+                            });
+          if (it == this->FinalEntries.end()) {
+            break;
+          }
+          it->Item.Value = "</LINK_GROUP>";
+          for (auto index = group.second.rbegin();
+               index != group.second.rend(); ++index) {
+            it = this->FinalEntries.insert(it, this->Entries[*index]);
+          }
+          it = this->FinalEntries.insert(it, groupEntry);
+          it->Item.Value = "<LINK_GROUP>";
+        }
+      }
+    }
+  }
+
+private:
+  enum OrderKind
+  {
+    Forward,
+    Reverse
+  };
+
+  enum UnicityKind
+  {
+    None,
+    Shared,
+    All
+  };
+
+  bool IncludeEntry(LinkEntry const& entry) const
+  {
+    return this->Unicity == None ||
+      (this->Unicity == Shared &&
+       (entry.Target == nullptr ||
+        entry.Target->GetType() != cmStateEnums::SHARED_LIBRARY)) ||
+      (this->Unicity == All && entry.Kind != LinkEntry::Library);
+  }
+
+  template <typename Range>
+  void AddLibraries(const Range& libEntries)
+  {
+    for (auto index : libEntries) {
+      LinkEntry const& entry = this->Entries[index];
+      if (this->IncludeEntry(entry) || this->Emitted.insert(index).second) {
+        this->FinalEntries.emplace_back(entry);
+      }
+    }
+  }
+
+  OrderKind Order = Reverse;
+  UnicityKind Unicity = Shared;
+  EntryVector& Entries;
+  EntryVector& FinalEntries;
+  std::set<size_t> Emitted;
+  const std::map<size_t, std::vector<size_t>>* Groups = nullptr;
+};
 }
 
-const std::string cmComputeLinkDepends::LinkEntry::DEFAULT = "DEFAULT";
+std::string const& cmComputeLinkDepends::LinkEntry::DEFAULT =
+  cmLinkItem::DEFAULT;
 
 cmComputeLinkDepends::cmComputeLinkDepends(const cmGeneratorTarget* target,
                                            const std::string& config,
@@ -376,49 +569,14 @@
   this->OrderLinkEntries();
 
   // Compute the final set of link entries.
-  // Iterate in reverse order so we can keep only the last occurrence
-  // of a shared library.
-  std::set<size_t> emitted;
-  for (size_t i : cmReverseRange(this->FinalLinkOrder)) {
-    LinkEntry const& e = this->EntryList[i];
-    cmGeneratorTarget const* t = e.Target;
-    // Entries that we know the linker will reuse do not need to be repeated.
-    bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY;
-    if (!uniquify || emitted.insert(i).second) {
-      this->FinalLinkEntries.push_back(e);
-    }
-  }
-  // Place explicitly linked object files in the front.  The linker will
-  // always use them anyway, and they may depend on symbols from libraries.
-  // Append in reverse order since we reverse the final order below.
-  for (size_t i : cmReverseRange(this->ObjectEntries)) {
-    this->FinalLinkEntries.emplace_back(this->EntryList[i]);
-  }
-  // Reverse the resulting order since we iterated in reverse.
-  std::reverse(this->FinalLinkEntries.begin(), this->FinalLinkEntries.end());
-
-  // Expand group items
-  if (!this->GroupItems.empty()) {
-    for (const auto& group : this->GroupItems) {
-      const LinkEntry& groupEntry = this->EntryList[group.first];
-      auto it = this->FinalLinkEntries.begin();
-      while (true) {
-        it = std::find_if(it, this->FinalLinkEntries.end(),
-                          [&groupEntry](const LinkEntry& entry) -> bool {
-                            return groupEntry.Item == entry.Item;
-                          });
-        if (it == this->FinalLinkEntries.end()) {
-          break;
-        }
-        it->Item.Value = "</LINK_GROUP>";
-        for (auto i = group.second.rbegin(); i != group.second.rend(); ++i) {
-          it = this->FinalLinkEntries.insert(it, this->EntryList[*i]);
-        }
-        it = this->FinalLinkEntries.insert(it, groupEntry);
-        it->Item.Value = "<LINK_GROUP>";
-      }
-    }
-  }
+  EntriesProcessing entriesProcessing{ this->Target, this->LinkLanguage,
+                                       this->EntryList,
+                                       this->FinalLinkEntries };
+  // Add groups first, to ensure that libraries of the groups are always kept.
+  entriesProcessing.AddGroups(this->GroupItems);
+  entriesProcessing.AddLibraries(this->FinalLinkOrder);
+  entriesProcessing.AddObjects(this->ObjectEntries);
+  entriesProcessing.Finalize();
 
   // Display the final set.
   if (this->DebugMode) {
@@ -466,10 +624,12 @@
   LinkEntry& entry = this->EntryList[index];
   entry.Item = BT<std::string>(item.AsStr(), item.Backtrace);
   entry.Target = item.Target;
+  entry.Feature = item.Feature;
   if (!entry.Target && entry.Item.Value[0] == '-' &&
       entry.Item.Value[1] != 'l' &&
       entry.Item.Value.substr(0, 10) != "-framework") {
     entry.Kind = LinkEntry::Flag;
+    entry.Feature = LinkEntry::DEFAULT;
   } else if (cmHasPrefix(entry.Item.Value, LG_BEGIN) &&
              cmHasSuffix(entry.Item.Value, '>')) {
     entry.Kind = LinkEntry::Group;
@@ -710,7 +870,6 @@
 {
   // Track inferred dependency sets implied by this list.
   std::map<size_t, DependSet> dependSets;
-  std::string feature = LinkEntry::DEFAULT;
 
   bool inGroup = false;
   std::pair<size_t, bool> groupIndex{
@@ -727,34 +886,27 @@
       continue;
     }
 
-    if (cmHasPrefix(item.AsStr(), LL_BEGIN) &&
-        cmHasSuffix(item.AsStr(), '>')) {
-      feature = ExtractFeature(item.AsStr());
-      // emit a warning if an undefined feature is used as part of
-      // an imported target
-      if (depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
-        const auto& depender = this->EntryList[depender_index];
-        if (depender.Target != nullptr && depender.Target->IsImported() &&
-            !IsFeatureSupported(this->Makefile, this->LinkLanguage, feature)) {
-          this->CMakeInstance->IssueMessage(
-            MessageType::AUTHOR_ERROR,
-            cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
-                     "' uses the generator-expression '$<LINK_LIBRARY>' with "
-                     "the feature '",
-                     feature,
-                     "', which is undefined or unsupported.\nDid you miss to "
-                     "define it by setting variables \"CMAKE_",
-                     this->LinkLanguage, "_LINK_LIBRARY_USING_", feature,
-                     "\" and \"CMAKE_", this->LinkLanguage,
-                     "_LINK_LIBRARY_USING_", feature, "_SUPPORTED\"?"),
-            this->Target->GetBacktrace());
-        }
+    // emit a warning if an undefined feature is used as part of
+    // an imported target
+    if (item.Feature != LinkEntry::DEFAULT &&
+        depender_index != cmComputeComponentGraph::INVALID_COMPONENT) {
+      const auto& depender = this->EntryList[depender_index];
+      if (depender.Target != nullptr && depender.Target->IsImported() &&
+          !IsFeatureSupported(this->Makefile, this->LinkLanguage,
+                              item.Feature)) {
+        this->CMakeInstance->IssueMessage(
+          MessageType::AUTHOR_ERROR,
+          cmStrCat("The 'IMPORTED' target '", depender.Target->GetName(),
+                   "' uses the generator-expression '$<LINK_LIBRARY>' with "
+                   "the feature '",
+                   item.Feature,
+                   "', which is undefined or unsupported.\nDid you miss to "
+                   "define it by setting variables \"CMAKE_",
+                   this->LinkLanguage, "_LINK_LIBRARY_USING_", item.Feature,
+                   "\" and \"CMAKE_", this->LinkLanguage,
+                   "_LINK_LIBRARY_USING_", item.Feature, "_SUPPORTED\"?"),
+          this->Target->GetBacktrace());
       }
-      continue;
-    }
-    if (cmHasPrefix(item.AsStr(), LL_END) && cmHasSuffix(item.AsStr(), '>')) {
-      feature = LinkEntry::DEFAULT;
-      continue;
     }
 
     if (cmHasPrefix(item.AsStr(), LG_BEGIN) &&
@@ -815,7 +967,7 @@
     dependee_index = ale.first;
     LinkEntry& entry = this->EntryList[dependee_index];
     auto const& itemFeature =
-      this->GetCurrentFeature(entry.Item.Value, feature);
+      this->GetCurrentFeature(entry.Item.Value, item.Feature);
     if (inGroup && ale.second && entry.Target != nullptr &&
         (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
          entry.Target->GetType() ==
@@ -834,30 +986,27 @@
           " library '", entry.Item.Value, "'."),
         this->Target->GetBacktrace());
     }
-    if (itemFeature != LinkEntry::DEFAULT) {
-      if (ale.second) {
-        // current item not yet defined
-        if (entry.Target != nullptr &&
-            (entry.Target->GetType() ==
-               cmStateEnums::TargetType::OBJECT_LIBRARY ||
-             entry.Target->GetType() ==
-               cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
-          this->CMakeInstance->IssueMessage(
-            MessageType::AUTHOR_WARNING,
-            cmStrCat("The feature '", feature,
-                     "', specified as part of a generator-expression "
-                     "'$",
-                     LL_BEGIN, feature, ">', will not be applied to the ",
-                     (entry.Target->GetType() ==
-                          cmStateEnums::TargetType::OBJECT_LIBRARY
-                        ? "OBJECT"
-                        : "INTERFACE"),
-                     " library '", entry.Item.Value, "'."),
-            this->Target->GetBacktrace());
-        } else {
-          entry.Feature = itemFeature;
-        }
+    if (ale.second) {
+      // current item not yet defined
+      if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr &&
+          (entry.Target->GetType() ==
+             cmStateEnums::TargetType::OBJECT_LIBRARY ||
+           entry.Target->GetType() ==
+             cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+        this->CMakeInstance->IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat("The feature '", itemFeature,
+                   "', specified as part of a generator-expression "
+                   "'$<LINK_LIBRARY:",
+                   itemFeature, ">', will not be applied to the ",
+                   (entry.Target->GetType() ==
+                        cmStateEnums::TargetType::OBJECT_LIBRARY
+                      ? "OBJECT"
+                      : "INTERFACE"),
+                   " library '", entry.Item.Value, "'."),
+          this->Target->GetBacktrace());
       }
+      entry.Feature = itemFeature;
     }
 
     bool supportedItem = entry.Target == nullptr ||
diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h
index 3233217..66daa07 100644
--- a/Source/cmComputeLinkDepends.h
+++ b/Source/cmComputeLinkDepends.h
@@ -49,7 +49,7 @@
     {
     }
 
-    static const std::string DEFAULT;
+    static std::string const& DEFAULT;
 
     enum EntryKind
     {
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 47717e3..c985767 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -1332,7 +1332,17 @@
   }
 
   // If in linking mode, just link to the shared library.
-  if (this->SharedDependencyMode == SharedDepModeLink) {
+  if (this->SharedDependencyMode == SharedDepModeLink ||
+      // For an imported shared library without a known runtime artifact,
+      // such as a CUDA stub, a library file named with the real soname
+      // may not be available at all, so '-rpath-link' cannot help linkers
+      // find it to satisfy '--no-allow-shlib-undefined' recursively.
+      // Pass this dependency to the linker explicitly just in case.
+      // If the linker also uses '--as-needed' behavior, this will not
+      // add an unnecessary direct dependency.
+      (tgt && tgt->IsImported() &&
+       !tgt->HasKnownRuntimeArtifactLocation(this->Config) &&
+       this->Target->LinkerEnforcesNoAllowShLibUndefined(this->Config))) {
     this->AddItem(entry);
     return;
   }
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 6f9f541..eba4c57 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -33,6 +33,9 @@
 auto const keyDEFINED = "DEFINED"_s;
 auto const keyEQUAL = "EQUAL"_s;
 auto const keyEXISTS = "EXISTS"_s;
+auto const keyIS_READABLE = "IS_READABLE"_s;
+auto const keyIS_WRITABLE = "IS_WRITABLE"_s;
+auto const keyIS_EXECUTABLE = "IS_EXECUTABLE"_s;
 auto const keyGREATER = "GREATER"_s;
 auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
 auto const keyIN_LIST = "IN_LIST"_s;
@@ -568,6 +571,24 @@
       newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
                            args);
     }
+    // check if a file is readable
+    else if (this->IsKeyword(keyIS_READABLE, *args.current)) {
+      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+                             args.next->GetValue(), cmsys::TEST_FILE_READ),
+                           args);
+    }
+    // check if a file is writable
+    else if (this->IsKeyword(keyIS_WRITABLE, *args.current)) {
+      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+                             args.next->GetValue(), cmsys::TEST_FILE_WRITE),
+                           args);
+    }
+    // check if a file is executable
+    else if (this->IsKeyword(keyIS_EXECUTABLE, *args.current)) {
+      newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
+                             args.next->GetValue(), cmsys::TEST_FILE_EXECUTE),
+                           args);
+    }
     // does a directory with this name exist
     else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
       newArgs.ReduceOneArg(
diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in
index de74716..23e4846 100644
--- a/Source/cmConfigure.cmake.h.in
+++ b/Source/cmConfigure.cmake.h.in
@@ -14,8 +14,13 @@
 #pragma warning(disable : 1572) /* floating-point equality test */
 #endif
 
-#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123)
+#if defined(__LCC__) && defined(__EDG__)
+#if __LCC__ == 123
 #pragma diag_suppress 2910 /* excess -Wunused-function in 1.23.x */
+#elif __LCC__ == 121
+#pragma diag_suppress 2727 /* excess -Wunused-function in 1.21.x */
+#include <limits.h>        /* ..._MIN, ..._MAX constants */
+#endif
 #endif
 
 #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 8c84ace..a4f36cc 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -14,6 +14,7 @@
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/RegularExpression.hxx"
 
 #include "cmArgumentParser.h"
 #include "cmConfigureLog.h"
@@ -83,6 +84,7 @@
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
 std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
+std::string const kCMAKE_LINKER_TYPE = "CMAKE_LINKER_TYPE";
 std::string const kCMAKE_LINK_SEARCH_END_STATIC =
   "CMAKE_LINK_SEARCH_END_STATIC";
 std::string const kCMAKE_LINK_SEARCH_START_STATIC =
@@ -176,6 +178,7 @@
           ArgumentParser::ExpectAtLeast{ 0 })
     .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries)
     .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions)
+    .Bind("LINKER_LANGUAGE"_s, &Arguments::LinkerLanguage)
     .Bind("COPY_FILE"_s, &Arguments::CopyFileTo)
     .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError)
     .BIND_LANG_PROPS(C)
@@ -852,8 +855,30 @@
         fclose(fout);
         return cm::nullopt;
       }
-      fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
+      fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n",
               fname.c_str());
+      // Create all relevant alias targets
+      if (arguments.LinkLibraries) {
+        const auto& aliasTargets = this->Makefile->GetAliasTargets();
+        for (std::string const& i : *arguments.LinkLibraries) {
+          auto alias = aliasTargets.find(i);
+          if (alias != aliasTargets.end()) {
+            const auto& aliasTarget =
+              this->Makefile->FindTargetToUse(alias->second);
+            // Create equivalent library/executable alias
+            if (aliasTarget->GetType() == cmStateEnums::EXECUTABLE) {
+              fprintf(fout, "add_executable(\"%s\" ALIAS \"%s\")\n", i.c_str(),
+                      alias->second.c_str());
+            } else {
+              // Other cases like UTILITY and GLOBAL_TARGET are excluded when
+              // arguments.LinkLibraries is initially parsed in this function.
+              fprintf(fout, "add_library(\"%s\" ALIAS \"%s\")\n", i.c_str(),
+                      alias->second.c_str());
+            }
+          }
+        }
+      }
+      fprintf(fout, "\n");
     }
 
     /* Set the appropriate policy information for ENABLE_EXPORTS */
@@ -877,6 +902,14 @@
               ? "NEW"
               : "OLD");
 
+    /* Set the appropriate policy information for Swift compilation mode */
+    fprintf(
+      fout, "cmake_policy(SET CMP0157 %s)\n",
+      this->Makefile->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT")
+          .IsEmpty()
+        ? "OLD"
+        : "NEW");
+
     // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
     // adding that flag in the platform and compiler language files
     fprintf(fout,
@@ -1041,6 +1074,19 @@
       }
     }
 
+    if (arguments.LinkerLanguage) {
+      std::string LinkerLanguage = *arguments.LinkerLanguage;
+      if (testLangs.find(LinkerLanguage) == testLangs.end()) {
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          "Linker language '" + LinkerLanguage +
+            "' must be enabled in project(LANGUAGES).");
+      }
+
+      fprintf(fout, "set_property(TARGET %s PROPERTY LINKER_LANGUAGE %s)\n",
+              targetName.c_str(), LinkerLanguage.c_str());
+    }
+
     if (arguments.LinkLibraries) {
       std::string libsToLink = " ";
       for (std::string const& i : *arguments.LinkLibraries) {
@@ -1112,6 +1158,20 @@
       vars.insert(varList.begin(), varList.end());
     }
 
+    if (this->Makefile->GetDefinition(kCMAKE_LINKER_TYPE)) {
+      // propagate various variables to support linker selection
+      vars.insert(kCMAKE_LINKER_TYPE);
+      auto defs = this->Makefile->GetDefinitions();
+      cmsys::RegularExpression linkerTypeDef{
+        "^CMAKE_[A-Za-z]+_USING_LINKER_"
+      };
+      for (auto const& def : defs) {
+        if (linkerTypeDef.find(def)) {
+          vars.insert(def);
+        }
+      }
+    }
+
     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0083) ==
         cmPolicies::NEW) {
       // To ensure full support of PIE, propagate cache variables
diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h
index 3217a1b..6a26e88 100644
--- a/Source/cmCoreTryCompile.h
+++ b/Source/cmCoreTryCompile.h
@@ -91,6 +91,7 @@
     cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
       LinkLibraries;
     ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions;
+    cm::optional<std::string> LinkerLanguage;
     std::map<std::string, std::string> LangProps;
     std::string CMakeInternal;
     cm::optional<std::string> OutputVariable;
diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx
index 75c25e3..9edbafe 100644
--- a/Source/cmCreateTestSourceList.cxx
+++ b/Source/cmCreateTestSourceList.cxx
@@ -81,8 +81,8 @@
     }
     std::string func_name;
     if (!cmSystemTools::GetFilenamePath(*i).empty()) {
-      func_name = cmSystemTools::GetFilenamePath(*i) + "/" +
-        cmSystemTools::GetFilenameWithoutLastExtension(*i);
+      func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/',
+                           cmSystemTools::GetFilenameWithoutLastExtension(*i));
     } else {
       func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
     }
@@ -93,9 +93,7 @@
       tests_func_name.end();
     tests_func_name.push_back(func_name);
     if (!already_declared) {
-      forwardDeclareCode += "int ";
-      forwardDeclareCode += func_name;
-      forwardDeclareCode += "(int, char*[]);\n";
+      forwardDeclareCode += cmStrCat("int ", func_name, "(int, char*[]);\n");
     }
   }
 
@@ -105,8 +103,8 @@
        ++i, ++j) {
     std::string func_name;
     if (!cmSystemTools::GetFilenamePath(*i).empty()) {
-      func_name = cmSystemTools::GetFilenamePath(*i) + "/" +
-        cmSystemTools::GetFilenameWithoutLastExtension(*i);
+      func_name = cmStrCat(cmSystemTools::GetFilenamePath(*i), '/',
+                           cmSystemTools::GetFilenameWithoutLastExtension(*i));
     } else {
       func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
     }
@@ -137,12 +135,12 @@
   {
     cmSourceFile* sf = mf.GetOrCreateSource(driver);
     sf->SetProperty("ABSTRACT", "0");
-    sourceListValue = args[1];
+    sourceListValue = driver;
   }
   for (i = testsBegin; i != tests.end(); ++i) {
     cmSourceFile* sf = mf.GetOrCreateSource(*i);
     sf->SetProperty("ABSTRACT", "0");
-    sourceListValue += ";";
+    sourceListValue += ';';
     sourceListValue += *i;
   }
 
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index 634b63b..2dea338 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -284,8 +284,12 @@
   if (!this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING")) {
     return;
   }
+  cmGeneratorExpression ge(*this->LG->GetCMakeInstance(),
+                           this->CC->GetBacktrace());
 
   for (unsigned int c = 0; c < this->GetNumberOfCommands(); ++c) {
+    // If the command is the plain name of an executable target,
+    // launch it with its emulator.
     std::string const& argv0 = this->CommandLines[c][0];
     cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(argv0);
     if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
@@ -297,7 +301,12 @@
         continue;
       }
 
-      cmExpandList(*emulator_property, this->EmulatorsWithArguments[c]);
+      // Plain target names are replaced by GetArgv0Location with the
+      // path to the executable artifact in the command config, so
+      // evaluate the launcher's location in the command config too.
+      std::string const emulator =
+        ge.Parse(*emulator_property)->Evaluate(this->LG, this->CommandConfig);
+      cmExpandList(emulator, this->EmulatorsWithArguments[c]);
     }
   }
 }
@@ -313,6 +322,8 @@
 
 const char* cmCustomCommandGenerator::GetArgv0Location(unsigned int c) const
 {
+  // If the command is the plain name of an executable target, we replace it
+  // with the path to the executable artifact in the command config.
   std::string const& argv0 = this->CommandLines[c][0];
   cmGeneratorTarget* target = this->LG->FindGeneratorTargetToUse(argv0);
   if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
diff --git a/Source/cmDebuggerExceptionManager.cxx b/Source/cmDebuggerExceptionManager.cxx
index a27426c..470e44c 100644
--- a/Source/cmDebuggerExceptionManager.cxx
+++ b/Source/cmDebuggerExceptionManager.cxx
@@ -78,7 +78,7 @@
     response.exceptionId = TheException->Id;
     response.breakMode = "always";
     response.description = TheException->Description;
-    TheException = {};
+    TheException = cm::nullopt;
   }
   return response;
 }
diff --git a/Source/cmDebuggerStackFrame.h b/Source/cmDebuggerStackFrame.h
index dc3b2ab..f4e6612 100644
--- a/Source/cmDebuggerStackFrame.h
+++ b/Source/cmDebuggerStackFrame.h
@@ -28,6 +28,10 @@
   std::string const& GetFileName() const noexcept { return this->FileName; }
   int64_t GetLine() const noexcept;
   cmMakefile* GetMakefile() const noexcept { return this->Makefile; }
+  cmListFileFunction const& GetFunction() const noexcept
+  {
+    return this->Function;
+  }
 };
 
 } // namespace cmDebugger
diff --git a/Source/cmDebuggerThread.cxx b/Source/cmDebuggerThread.cxx
index fd52f5a..f7a1778 100644
--- a/Source/cmDebuggerThread.cxx
+++ b/Source/cmDebuggerThread.cxx
@@ -13,6 +13,7 @@
 #include "cmDebuggerVariables.h"
 #include "cmDebuggerVariablesHelper.h"
 #include "cmDebuggerVariablesManager.h"
+#include "cmListFileCache.h"
 
 namespace cmDebugger {
 
@@ -135,8 +136,7 @@
 #endif
     stackFrame.line = thread->Frames[i]->GetLine();
     stackFrame.column = 1;
-    stackFrame.name = thread->Frames[i]->GetFileName() + " Line " +
-      std::to_string(stackFrame.line);
+    stackFrame.name = thread->Frames[i]->GetFunction().OriginalName();
     stackFrame.id = thread->Frames[i]->GetId();
     stackFrame.source = source;
 
diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx
index 2ce0f54..ed4762b 100644
--- a/Source/cmDyndepCollation.cxx
+++ b/Source/cmDyndepCollation.cxx
@@ -243,14 +243,16 @@
 
   auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators();
   for (auto const& exp : all_build_exports) {
-    std::vector<std::string> targets;
+    std::vector<cmExportBuildFileGenerator::TargetExport> targets;
     exp->GetTargets(targets);
 
     // Ignore exports sets which are not for this target.
     auto const& name = gt->GetName();
     bool has_current_target =
       std::any_of(targets.begin(), targets.end(),
-                  [name](std::string const& tname) { return tname == name; });
+                  [name](cmExportBuildFileGenerator::TargetExport const& te) {
+                    return te.Name == name;
+                  });
     if (!has_current_target) {
       continue;
     }
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 3efd3bd..2b923df 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -2,8 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecuteProcessCommand.h"
 
-#include <algorithm>
-#include <cctype> /* isspace */
+#include <cstdint>
 #include <cstdio>
 #include <iostream>
 #include <map>
@@ -16,7 +15,7 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
 
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
@@ -26,17 +25,20 @@
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 namespace {
 bool cmExecuteProcessCommandIsWhitespace(char c)
 {
-  return (isspace(static_cast<int>(c)) || c == '\n' || c == '\r');
+  return (cmIsSpace(c) || c == '\n' || c == '\r');
 }
 
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
                                     bool strip_trailing_whitespace);
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   int length);
+                                   std::size_t length);
 }
 
 // cmExecuteProcessCommand
@@ -161,57 +163,68 @@
     }
   }
   // Create a process instance.
-  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
-    cmsysProcess_New(), cmsysProcess_Delete);
-  cmsysProcess* cp = cp_ptr.get();
+  cmUVProcessChainBuilder builder;
 
   // Set the command sequence.
   for (std::vector<std::string> const& cmd : arguments.Commands) {
-    std::vector<const char*> argv(cmd.size() + 1);
-    std::transform(cmd.begin(), cmd.end(), argv.begin(),
-                   [](std::string const& s) { return s.c_str(); });
-    argv.back() = nullptr;
-    cmsysProcess_AddCommand(cp, argv.data());
+    builder.AddCommand(cmd);
   }
 
   // Set the process working directory.
   if (!arguments.WorkingDirectory.empty()) {
-    cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
+    builder.SetWorkingDirectory(arguments.WorkingDirectory);
   }
 
-  // Always hide the process window.
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-
   // Check the output variables.
-  bool merge_output = false;
-  if (!arguments.InputFile.empty()) {
-    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
-                             arguments.InputFile.c_str());
+  std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose);
+  if (!inputFilename.empty()) {
+    inputFile.reset(cmsys::SystemTools::Fopen(inputFilename, "rb"));
+    if (inputFile) {
+      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
+                                inputFile.get());
+    }
+  } else {
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin);
   }
-  if (!arguments.OutputFile.empty()) {
-    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
-                             arguments.OutputFile.c_str());
-  }
-  if (!arguments.ErrorFile.empty()) {
-    if (arguments.ErrorFile == arguments.OutputFile) {
-      merge_output = true;
+
+  std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose);
+  if (!outputFilename.empty()) {
+    outputFile.reset(cmsys::SystemTools::Fopen(outputFilename, "wb"));
+    if (outputFile) {
+      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                                outputFile.get());
+    }
+  } else {
+    if (arguments.OutputVariable == arguments.ErrorVariable &&
+        !arguments.ErrorVariable.empty()) {
+      builder.SetMergedBuiltinStreams();
     } else {
-      cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
-                               arguments.ErrorFile.c_str());
+      builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
     }
   }
-  if (!arguments.OutputVariable.empty() &&
-      arguments.OutputVariable == arguments.ErrorVariable) {
-    merge_output = true;
-  }
-  if (merge_output) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
+
+  std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose);
+  if (!errorFilename.empty()) {
+    if (errorFilename == outputFilename) {
+      if (outputFile) {
+        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                                  outputFile.get());
+      }
+    } else {
+      errorFile.reset(cmsys::SystemTools::Fopen(errorFilename, "wb"));
+      if (errorFile) {
+        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                                  errorFile.get());
+      }
+    }
+  } else if (arguments.ErrorVariable.empty() ||
+             (!arguments.ErrorVariable.empty() &&
+              arguments.OutputVariable != arguments.ErrorVariable)) {
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
 
   // Set the timeout if any.
-  if (timeout >= 0) {
-    cmsysProcess_SetTimeout(cp, timeout);
-  }
+  int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0);
 
   bool echo_stdout = false;
   bool echo_stderr = false;
@@ -259,36 +272,86 @@
     }
   }
   // Start the process.
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+
+  bool timedOut = false;
+  cm::uv_timer_ptr timer;
+
+  if (timeoutMillis >= 0) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timeoutPtr = static_cast<bool*>(handle->data);
+        *timeoutPtr = true;
+      },
+      timeoutMillis, 0);
+  }
 
   // Read the process output.
-  std::vector<char> tempOutput;
-  std::vector<char> tempError;
-  int length;
-  char* data;
-  int p;
+  struct ReadData
+  {
+    bool Finished = false;
+    std::vector<char> Output;
+    cm::uv_pipe_ptr Stream;
+  };
+  ReadData outputData;
+  ReadData errorData;
   cmProcessOutput processOutput(
     cmProcessOutput::FindEncoding(arguments.Encoding));
   std::string strdata;
-  while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    // Put the output in the right place.
-    if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
-      if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) {
-        processOutput.DecodeText(data, length, strdata, 1);
-        cmSystemTools::Stdout(strdata);
-      }
-      if (!arguments.OutputVariable.empty()) {
-        cmExecuteProcessCommandAppend(tempOutput, data, length);
-      }
-    } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
-      if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
-        processOutput.DecodeText(data, length, strdata, 2);
-        cmSystemTools::Stderr(strdata);
-      }
-      if (!arguments.ErrorVariable.empty()) {
-        cmExecuteProcessCommandAppend(tempError, data, length);
-      }
-    }
+
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  if (chain.OutputStream() >= 0) {
+    outputData.Stream.init(chain.GetLoop(), 0);
+    uv_pipe_open(outputData.Stream, chain.OutputStream());
+    outputHandle = cmUVStreamRead(
+      outputData.Stream,
+      [&arguments, &processOutput, &outputData,
+       &strdata](std::vector<char> data) {
+        if (!arguments.OutputQuiet) {
+          if (arguments.OutputVariable.empty() ||
+              arguments.EchoOutputVariable) {
+            processOutput.DecodeText(data.data(), data.size(), strdata, 1);
+            cmSystemTools::Stdout(strdata);
+          }
+          if (!arguments.OutputVariable.empty()) {
+            cmExecuteProcessCommandAppend(outputData.Output, data.data(),
+                                          data.size());
+          }
+        }
+      },
+      [&outputData]() { outputData.Finished = true; });
+  } else {
+    outputData.Finished = true;
+  }
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
+  if (chain.ErrorStream() >= 0 &&
+      chain.ErrorStream() != chain.OutputStream()) {
+    errorData.Stream.init(chain.GetLoop(), 0);
+    uv_pipe_open(errorData.Stream, chain.ErrorStream());
+    errorHandle = cmUVStreamRead(
+      errorData.Stream,
+      [&arguments, &processOutput, &errorData,
+       &strdata](std::vector<char> data) {
+        if (!arguments.ErrorQuiet) {
+          if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
+            processOutput.DecodeText(data.data(), data.size(), strdata, 2);
+            cmSystemTools::Stderr(strdata);
+          }
+          if (!arguments.ErrorVariable.empty()) {
+            cmExecuteProcessCommandAppend(errorData.Output, data.data(),
+                                          data.size());
+          }
+        }
+      },
+      [&errorData]() { errorData.Finished = true; });
+  } else {
+    errorData.Finished = true;
+  }
+
+  while (chain.Valid() && !timedOut &&
+         !(chain.Finished() && outputData.Finished && errorData.Finished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
   if (!arguments.OutputQuiet &&
       (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
@@ -305,151 +368,102 @@
     }
   }
 
-  // All output has been read.  Wait for the process to exit.
-  cmsysProcess_WaitForExit(cp, nullptr);
-  processOutput.DecodeText(tempOutput, tempOutput);
-  processOutput.DecodeText(tempError, tempError);
+  // All output has been read.
+  processOutput.DecodeText(outputData.Output, outputData.Output);
+  processOutput.DecodeText(errorData.Output, errorData.Output);
 
   // Fix the text in the output strings.
-  cmExecuteProcessCommandFixText(tempOutput,
+  cmExecuteProcessCommandFixText(outputData.Output,
                                  arguments.OutputStripTrailingWhitespace);
-  cmExecuteProcessCommandFixText(tempError,
+  cmExecuteProcessCommandFixText(errorData.Output,
                                  arguments.ErrorStripTrailingWhitespace);
 
   // Store the output obtained.
-  if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
+  if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
     status.GetMakefile().AddDefinition(arguments.OutputVariable,
-                                       tempOutput.data());
+                                       outputData.Output.data());
   }
-  if (!merge_output && !arguments.ErrorVariable.empty() &&
-      !tempError.empty()) {
+  if (arguments.ErrorVariable != arguments.OutputVariable &&
+      !arguments.ErrorVariable.empty() && !errorData.Output.empty()) {
     status.GetMakefile().AddDefinition(arguments.ErrorVariable,
-                                       tempError.data());
+                                       errorData.Output.data());
   }
 
   // Store the result of running the process.
   if (!arguments.ResultVariable.empty()) {
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        int v = cmsysProcess_GetExitValue(cp);
-        char buf[16];
-        snprintf(buf, sizeof(buf), "%d", v);
-        status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
-      } break;
-      case cmsysProcess_State_Exception:
+    if (timedOut) {
+      status.GetMakefile().AddDefinition(arguments.ResultVariable,
+                                         "Process terminated due to timeout");
+    } else {
+      auto const* lastStatus = chain.GetStatus().back();
+      auto exception = lastStatus->GetException();
+      if (exception.first == cmUVProcessChain::ExceptionCode::None) {
         status.GetMakefile().AddDefinition(
-          arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
-        break;
-      case cmsysProcess_State_Error:
+          arguments.ResultVariable,
+          std::to_string(static_cast<int>(lastStatus->ExitStatus)));
+      } else {
         status.GetMakefile().AddDefinition(arguments.ResultVariable,
-                                           cmsysProcess_GetErrorString(cp));
-        break;
-      case cmsysProcess_State_Expired:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultVariable, "Process terminated due to timeout");
-        break;
+                                           exception.second);
+      }
     }
   }
   // Store the result of running the processes.
   if (!arguments.ResultsVariable.empty()) {
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        std::vector<std::string> res;
-        for (size_t i = 0; i < arguments.Commands.size(); ++i) {
-          switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
-            case kwsysProcess_StateByIndex_Exited: {
-              int exitCode =
-                cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
-              char buf[16];
-              snprintf(buf, sizeof(buf), "%d", exitCode);
-              res.emplace_back(buf);
-            } break;
-            case kwsysProcess_StateByIndex_Exception:
-              res.emplace_back(cmsysProcess_GetExceptionStringByIndex(
-                cp, static_cast<int>(i)));
-              break;
-            case kwsysProcess_StateByIndex_Error:
-            default:
-              res.emplace_back("Error getting the child return code");
-              break;
-          }
+    if (timedOut) {
+      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                         "Process terminated due to timeout");
+    } else {
+      std::vector<std::string> res;
+      for (auto const* processStatus : chain.GetStatus()) {
+        auto exception = processStatus->GetException();
+        if (exception.first == cmUVProcessChain::ExceptionCode::None) {
+          res.emplace_back(
+            std::to_string(static_cast<int>(processStatus->ExitStatus)));
+        } else {
+          res.emplace_back(exception.second);
         }
-        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                           cmList::to_string(res));
-      } break;
-      case cmsysProcess_State_Exception:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
-        break;
-      case cmsysProcess_State_Error:
-        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                           cmsysProcess_GetErrorString(cp));
-        break;
-      case cmsysProcess_State_Expired:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultsVariable, "Process terminated due to timeout");
-        break;
+      }
+      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                         cmList::to_string(res));
     }
   }
 
-  auto queryProcessStatusByIndex = [&cp](int index) -> std::string {
-    std::string processStatus;
-    switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) {
-      case kwsysProcess_StateByIndex_Exited: {
-        int exitCode = cmsysProcess_GetExitValueByIndex(cp, index);
-        if (exitCode) {
-          processStatus = "Child return code: " + std::to_string(exitCode);
-        }
-      } break;
-      case kwsysProcess_StateByIndex_Exception: {
-        processStatus = cmStrCat(
-          "Abnormal exit with child return code: ",
-          cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index)));
-        break;
+  auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string {
+    auto const& processStatus = chain.GetStatus(index);
+    auto exception = processStatus.GetException();
+    if (exception.first == cmUVProcessChain::ExceptionCode::None) {
+      if (processStatus.ExitStatus) {
+        return cmStrCat("Child return code: ", processStatus.ExitStatus);
       }
-      case kwsysProcess_StateByIndex_Error:
-      default:
-        processStatus = "Error getting the child return code";
-        break;
+      return "";
     }
-    return processStatus;
+    return cmStrCat("Abnormal exit with child return code: ",
+                    exception.second);
   };
 
   if (arguments.CommandErrorIsFatal == "ANY"_s) {
     bool ret = true;
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        std::map<int, std::string> failureIndices;
-        for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) {
-          std::string processStatus = queryProcessStatusByIndex(i);
-          if (!processStatus.empty()) {
-            failureIndices[i] = processStatus;
-          }
-          if (!failureIndices.empty()) {
-            std::ostringstream oss;
-            oss << "failed command indexes:\n";
-            for (auto const& e : failureIndices) {
-              oss << "  " << e.first + 1 << ": \"" << e.second << "\"\n";
-            }
-            status.SetError(oss.str());
-            ret = false;
-          }
+    if (timedOut) {
+      status.SetError("Process terminated due to timeout");
+      ret = false;
+    } else {
+      std::map<std::size_t, std::string> failureIndices;
+      auto statuses = chain.GetStatus();
+      for (std::size_t i = 0; i < statuses.size(); ++i) {
+        std::string processStatus = queryProcessStatusByIndex(i);
+        if (!processStatus.empty()) {
+          failureIndices[i] = processStatus;
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        status.SetError(
-          cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
+      }
+      if (!failureIndices.empty()) {
+        std::ostringstream oss;
+        oss << "failed command indexes:\n";
+        for (auto const& e : failureIndices) {
+          oss << "  " << e.first + 1 << ": \"" << e.second << "\"\n";
+        }
+        status.SetError(oss.str());
         ret = false;
-        break;
-      case cmsysProcess_State_Error:
-        status.SetError(cmStrCat("error getting child return code: ",
-                                 cmsysProcess_GetErrorString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Expired:
-        status.SetError("Process terminated due to timeout");
-        ret = false;
-        break;
+      }
     }
 
     if (!ret) {
@@ -460,29 +474,23 @@
 
   if (arguments.CommandErrorIsFatal == "LAST"_s) {
     bool ret = true;
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
+    if (timedOut) {
+      status.SetError("Process terminated due to timeout");
+      ret = false;
+    } else {
+      auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
+      auto exception = lastStatus.GetException();
+      if (exception.first != cmUVProcessChain::ExceptionCode::None) {
+        status.SetError(cmStrCat("Abnormal exit: ", exception.second));
+        ret = false;
+      } else {
         int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
         const std::string processStatus = queryProcessStatusByIndex(lastIndex);
         if (!processStatus.empty()) {
           status.SetError("last command failed");
           ret = false;
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        status.SetError(
-          cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Error:
-        status.SetError(cmStrCat("Error getting child return code: ",
-                                 cmsysProcess_GetErrorString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Expired:
-        status.SetError("Process terminated due to timeout");
-        ret = false;
-        break;
+      }
     }
     if (!ret) {
       cmSystemTools::SetFatalErrorOccurred();
@@ -525,7 +533,7 @@
 }
 
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   int length)
+                                   std::size_t length)
 {
 #if defined(__APPLE__)
   // HACK on Apple to work around bug with inserting at the
diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h
index ced3548..e023971 100644
--- a/Source/cmExecutionStatus.h
+++ b/Source/cmExecutionStatus.h
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/optional>
+
 class cmMakefile;
 
 /** \class cmExecutionStatus
@@ -53,6 +55,11 @@
   void SetNestedError() { this->NestedError = true; }
   bool GetNestedError() const { return this->NestedError; }
 
+  void SetExitCode(int code) noexcept { this->ExitCode = code; }
+  bool HasExitCode() const noexcept { return this->ExitCode.has_value(); }
+  void CleanExitCode() noexcept { this->ExitCode.reset(); }
+  int GetExitCode() const noexcept { return this->ExitCode.value_or(-1); }
+
 private:
   cmMakefile& Makefile;
   std::string Error;
@@ -60,5 +67,6 @@
   bool BreakInvoked = false;
   bool ContinueInvoked = false;
   bool NestedError = false;
+  cm::optional<int> ExitCode;
   std::vector<std::string> Variables;
 };
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx
index c54e6ac..34bda1b 100644
--- a/Source/cmExportBuildAndroidMKGenerator.cxx
+++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -57,8 +57,8 @@
 }
 
 void cmExportBuildAndroidMKGenerator::GenerateImportPropertyCode(
-  std::ostream&, const std::string&, cmGeneratorTarget const*,
-  ImportPropertyMap const&)
+  std::ostream&, const std::string&, const std::string&,
+  cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
 {
 }
 
diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h
index 7067488..9562cee 100644
--- a/Source/cmExportBuildAndroidMKGenerator.h
+++ b/Source/cmExportBuildAndroidMKGenerator.h
@@ -51,10 +51,11 @@
   void GenerateExpectedTargetsCode(
     std::ostream& os, const std::string& expectedTargets) override;
   void GenerateImportPropertyCode(
-    std::ostream& os, const std::string& config,
-    cmGeneratorTarget const* target,
-    ImportPropertyMap const& properties) override;
+    std::ostream& os, const std::string& config, const std::string& suffix,
+    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+    const std::string& importedXcFrameworkLocation) override;
   void GenerateMissingTargetsCheckCode(std::ostream& os) override;
+  void GenerateFindDependencyCalls(std::ostream&) override {}
   void GenerateInterfaceProperties(
     cmGeneratorTarget const* target, std::ostream& os,
     const ImportPropertyMap& properties) override;
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 1a22445..81e3d54 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include <cm/string_view>
-#include <cmext/algorithm>
 #include <cmext/string_view>
 
 #include "cmCryptoHash.h"
@@ -56,15 +55,15 @@
   {
     std::string expectedTargets;
     std::string sep;
-    std::vector<std::string> targets;
+    std::vector<TargetExport> targets;
     bool generatedInterfaceRequired = false;
     this->GetTargets(targets);
-    for (std::string const& tei : targets) {
-      cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
+    for (auto const& tei : targets) {
+      cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
       expectedTargets += sep + this->Namespace + te->GetExportName();
       sep = " ";
       if (this->ExportedTargets.insert(te).second) {
-        this->Exports.push_back(te);
+        this->Exports.emplace_back(te, tei.XcFrameworkLocation);
       } else {
         std::ostringstream e;
         e << "given target \"" << te->GetName() << "\" more than once.";
@@ -78,13 +77,14 @@
     }
 
     if (generatedInterfaceRequired) {
-      this->GenerateRequiredCMakeVersion(os, "3.0.0");
+      this->SetRequiredCMakeVersion(3, 0, 0);
     }
     this->GenerateExpectedTargetsCode(os, expectedTargets);
   }
 
   // Create all the imported targets.
-  for (cmGeneratorTarget* gte : this->Exports) {
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* gte = exp.Target;
     this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
 
     gte->Target->AppendBuildInterfaceIncludes();
@@ -165,7 +165,7 @@
     cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
     constexpr std::size_t HASH_TRUNCATION = 12;
     for (auto const& target : this->Targets) {
-      hasher.Append(target);
+      hasher.Append(target.Name);
     }
     cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
   }
@@ -190,7 +190,9 @@
 void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
   std::ostream& os, const std::string& config, std::string const& suffix)
 {
-  for (cmGeneratorTarget* target : this->Exports) {
+  for (auto const& exp : this->Exports) {
+    cmGeneratorTarget* target = exp.Target;
+
     // Collect import properties for this target.
     ImportPropertyMap properties;
 
@@ -214,7 +216,23 @@
       //                              properties);
 
       // Generate code in the export file.
-      this->GenerateImportPropertyCode(os, config, target, properties);
+      std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
+      if (!importedXcFrameworkLocation.empty()) {
+        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+          importedXcFrameworkLocation,
+          cmGeneratorExpression::PreprocessContext::BuildInterface);
+        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+          importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
+          exp.Target, nullptr, exp.Target);
+        if (!importedXcFrameworkLocation.empty() &&
+            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
+          importedXcFrameworkLocation =
+            cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
+                     importedXcFrameworkLocation);
+        }
+      }
+      this->GenerateImportPropertyCode(os, config, suffix, target, properties,
+                                       importedXcFrameworkLocation);
     }
   }
 }
@@ -322,7 +340,7 @@
 }
 
 void cmExportBuildFileGenerator::GetTargets(
-  std::vector<std::string>& targets) const
+  std::vector<TargetExport>& targets) const
 {
   if (this->ExportSet) {
     for (std::unique_ptr<cmTargetExport> const& te :
@@ -330,7 +348,7 @@
       if (te->NamelinkOnly) {
         continue;
       }
-      targets.push_back(te->TargetName);
+      targets.emplace_back(te->TargetName, te->XcFrameworkLocation);
     }
     return;
   }
@@ -348,9 +366,11 @@
 
   for (auto const& exp : exportSets) {
     const auto& exportSet = exp.second;
-    std::vector<std::string> targets;
+    std::vector<TargetExport> targets;
     exportSet->GetTargets(targets);
-    if (cm::contains(targets, name)) {
+    if (std::any_of(
+          targets.begin(), targets.end(),
+          [&name](const TargetExport& te) { return te.Name == name; })) {
       exportFiles.push_back(exp.first);
       ns = exportSet->GetNamespace();
     }
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h
index ea51437..ee4779f 100644
--- a/Source/cmExportBuildFileGenerator.h
+++ b/Source/cmExportBuildFileGenerator.h
@@ -33,15 +33,27 @@
 class cmExportBuildFileGenerator : public cmExportFileGenerator
 {
 public:
+  struct TargetExport
+  {
+    TargetExport(std::string name, std::string xcFrameworkLocation)
+      : Name(std::move(name))
+      , XcFrameworkLocation(std::move(xcFrameworkLocation))
+    {
+    }
+
+    std::string Name;
+    std::string XcFrameworkLocation;
+  };
+
   cmExportBuildFileGenerator();
 
   /** Set the list of targets to export.  */
-  void SetTargets(std::vector<std::string> const& targets)
+  void SetTargets(std::vector<TargetExport> const& targets)
   {
     this->Targets = targets;
   }
-  void GetTargets(std::vector<std::string>& targets) const;
-  void AppendTargets(std::vector<std::string> const& targets)
+  void GetTargets(std::vector<TargetExport>& targets) const;
+  void AppendTargets(std::vector<TargetExport> const& targets)
   {
     cm::append(this->Targets, targets);
   }
@@ -90,6 +102,7 @@
                                     cmTargetExport* te) override;
   std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
                               cmTargetExport* te) override;
+  cmExportSet* GetExportSet() const override { return this->ExportSet; }
 
   std::string GetCxxModulesDirectory() const override;
   void GenerateCxxModuleConfigInformation(std::string const&,
@@ -100,9 +113,22 @@
   std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
     cmGlobalGenerator* gg, const std::string& name);
 
-  std::vector<std::string> Targets;
+  struct TargetExportPrivate
+  {
+    TargetExportPrivate(cmGeneratorTarget* target,
+                        std::string xcFrameworkLocation)
+      : Target(target)
+      , XcFrameworkLocation(std::move(xcFrameworkLocation))
+    {
+    }
+
+    cmGeneratorTarget* Target;
+    std::string XcFrameworkLocation;
+  };
+
+  std::vector<TargetExport> Targets;
   cmExportSet* ExportSet;
-  std::vector<cmGeneratorTarget*> Exports;
+  std::vector<TargetExportPrivate> Exports;
   cmLocalGenerator* LG;
   // The directory for C++ module information.
   std::string CxxModulesDirectory;
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 7e44210..7d23c91 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -8,6 +8,7 @@
 
 #include <cm/memory>
 #include <cm/optional>
+#include <cmext/algorithm>
 #include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
@@ -24,10 +25,12 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 #if defined(__HAIKU__)
 #  include <FindDirectory.h>
@@ -66,6 +69,11 @@
     std::string CxxModulesDirectory;
     bool Append = false;
     bool ExportOld = false;
+
+    std::vector<std::vector<std::string>> PackageDependencyArgs;
+    bool ExportPackageDependencies = false;
+
+    std::vector<std::vector<std::string>> TargetArgs;
   };
 
   auto parser =
@@ -75,7 +83,13 @@
       .Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory);
 
   if (args[0] == "EXPORT") {
-    parser.Bind("EXPORT"_s, &Arguments::ExportSetName);
+    parser.Bind("EXPORT"_s, &Arguments::ExportSetName)
+      .Bind("EXPORT_PACKAGE_DEPENDENCIES"_s,
+            &Arguments::ExportPackageDependencies);
+  } else if (args[0] == "SETUP") {
+    parser.Bind("SETUP"_s, &Arguments::ExportSetName);
+    parser.Bind("PACKAGE_DEPENDENCY"_s, &Arguments::PackageDependencyArgs);
+    parser.Bind("TARGET"_s, &Arguments::TargetArgs);
   } else {
     parser.Bind("TARGETS"_s, &Arguments::Targets);
     parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile);
@@ -91,6 +105,91 @@
     return false;
   }
 
+  if (args[0] == "SETUP") {
+    cmMakefile& mf = status.GetMakefile();
+    cmGlobalGenerator* gg = mf.GetGlobalGenerator();
+
+    cmExportSetMap& setMap = gg->GetExportSets();
+    auto& exportSet = setMap[arguments.ExportSetName];
+
+    struct PackageDependencyArguments
+    {
+      std::string Enabled;
+      ArgumentParser::MaybeEmpty<std::vector<std::string>> ExtraArgs;
+    };
+
+    auto packageDependencyParser =
+      cmArgumentParser<PackageDependencyArguments>{}
+        .Bind("ENABLED"_s, &PackageDependencyArguments::Enabled)
+        .Bind("EXTRA_ARGS"_s, &PackageDependencyArguments::ExtraArgs);
+
+    for (auto const& packageDependencyArgs : arguments.PackageDependencyArgs) {
+      if (packageDependencyArgs.empty()) {
+        continue;
+      }
+
+      PackageDependencyArguments const packageDependencyArguments =
+        packageDependencyParser.Parse(
+          cmMakeRange(packageDependencyArgs).advance(1), &unknownArgs);
+
+      if (!unknownArgs.empty()) {
+        status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
+        return false;
+      }
+
+      auto& packageDependency =
+        exportSet.GetPackageDependencyForSetup(packageDependencyArgs.front());
+
+      if (!packageDependencyArguments.Enabled.empty()) {
+        if (packageDependencyArguments.Enabled == "AUTO") {
+          packageDependency.Enabled =
+            cmExportSet::PackageDependencyExportEnabled::Auto;
+        } else if (cmIsOff(packageDependencyArguments.Enabled)) {
+          packageDependency.Enabled =
+            cmExportSet::PackageDependencyExportEnabled::Off;
+        } else if (cmIsOn(packageDependencyArguments.Enabled)) {
+          packageDependency.Enabled =
+            cmExportSet::PackageDependencyExportEnabled::On;
+        } else {
+          status.SetError(
+            cmStrCat("Invalid enable setting for package dependency: \"",
+                     packageDependencyArguments.Enabled, "\""));
+          return false;
+        }
+      }
+
+      cm::append(packageDependency.ExtraArguments,
+                 packageDependencyArguments.ExtraArgs);
+    }
+
+    struct TargetArguments
+    {
+      std::string XcFrameworkLocation;
+    };
+
+    auto targetParser = cmArgumentParser<TargetArguments>{}.Bind(
+      "XCFRAMEWORK_LOCATION"_s, &TargetArguments::XcFrameworkLocation);
+
+    for (auto const& targetArgs : arguments.TargetArgs) {
+      if (targetArgs.empty()) {
+        continue;
+      }
+
+      TargetArguments const targetArguments =
+        targetParser.Parse(cmMakeRange(targetArgs).advance(1), &unknownArgs);
+
+      if (!unknownArgs.empty()) {
+        status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
+        return false;
+      }
+
+      exportSet.SetXcFrameworkLocation(targetArgs.front(),
+                                       targetArguments.XcFrameworkLocation);
+    }
+
+    return true;
+  }
+
   std::string fname;
   bool android = false;
   if (!arguments.AndroidMKFile.empty()) {
@@ -133,7 +232,7 @@
     fname = dir + "/" + fname;
   }
 
-  std::vector<std::string> targets;
+  std::vector<cmExportBuildFileGenerator::TargetExport> targets;
 
   cmGlobalGenerator* gg = mf.GetGlobalGenerator();
 
@@ -171,7 +270,7 @@
         status.SetError(e.str());
         return false;
       }
-      targets.push_back(currentTarget);
+      targets.emplace_back(currentTarget, std::string{});
     }
     if (arguments.Append) {
       if (cmExportBuildFileGenerator* ebfg =
@@ -224,6 +323,7 @@
     ebfg->SetTargets(targets);
   }
   ebfg->SetExportOld(arguments.ExportOld);
+  ebfg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
 
   // Compute the set of configurations exported.
   std::vector<std::string> configurationTypes =
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index f2742df..2e83951 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportFileGenerator.h"
 
+#include <algorithm>
 #include <array>
 #include <cassert>
 #include <cstring>
@@ -9,12 +10,15 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cmext/string_view>
 
 #include "cmsys/FStream.hxx"
 
 #include "cmComputeLinkInformation.h"
+#include "cmExportSet.h"
 #include "cmFileSet.h"
+#include "cmFindPackageStack.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmLinkItem.h"
@@ -30,6 +34,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmValue.h"
+#include "cmVersion.h"
 
 static std::string cmExportFileGeneratorEscape(std::string const& str)
 {
@@ -93,17 +98,35 @@
     return false;
   }
   std::ostream& os = *foutPtr;
+  std::stringstream mainFileWithHeadersAndFootersBuffer;
 
   // Start with the import file header.
-  this->GeneratePolicyHeaderCode(os);
-  this->GenerateImportHeaderCode(os);
+  this->GenerateImportHeaderCode(mainFileWithHeadersAndFootersBuffer);
 
   // Create all the imported targets.
-  bool result = this->GenerateMainFile(os);
+  std::stringstream mainFileBuffer;
+  bool result = this->GenerateMainFile(mainFileBuffer);
+
+  // Export find_dependency() calls. Must be done after GenerateMainFile(),
+  // because that's when target dependencies are gathered, which we need for
+  // the find_dependency() calls.
+  if (!this->AppendMode && this->GetExportSet() &&
+      this->ExportPackageDependencies) {
+    this->SetRequiredCMakeVersion(3, 9, 0);
+    this->GenerateFindDependencyCalls(mainFileWithHeadersAndFootersBuffer);
+  }
+
+  // Write cached import code.
+  mainFileWithHeadersAndFootersBuffer << mainFileBuffer.rdbuf();
 
   // End with the import file footer.
-  this->GenerateImportFooterCode(os);
-  this->GeneratePolicyFooterCode(os);
+  this->GenerateImportFooterCode(mainFileWithHeadersAndFootersBuffer);
+  this->GeneratePolicyFooterCode(mainFileWithHeadersAndFootersBuffer);
+
+  // This has to be done last, after the minimum CMake version has been
+  // determined.
+  this->GeneratePolicyHeaderCode(os);
+  os << mainFileWithHeadersAndFootersBuffer.rdbuf();
 
   return result;
 }
@@ -156,17 +179,6 @@
   }
 }
 
-void cmExportFileGenerator::GenerateRequiredCMakeVersion(
-  std::ostream& os, const char* versionString)
-{
-  /* clang-format off */
-  os << "if(CMAKE_VERSION VERSION_LESS " << versionString << ")\n"
-        "  message(FATAL_ERROR \"This file relies on consumers using "
-        "CMake " << versionString << " or greater.\")\n"
-        "endif()\n\n";
-  /* clang-format on */
-}
-
 bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
   cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
@@ -620,6 +632,12 @@
     return false;
   }
 
+  cmFindPackageStack const& pkgStack = tgt->Target->GetFindPackageStack();
+  if (!pkgStack.Empty() ||
+      tgt->Target->GetProperty("EXPORT_FIND_PACKAGE_NAME")) {
+    this->ExternalTargets.emplace(tgt);
+  }
+
   if (tgt->IsImported()) {
     input = tgt->GetName();
     return true;
@@ -867,12 +885,14 @@
     // Export IMPORTED_LINK_DEPENDENT_LIBRARIES to help consuming linkers
     // find private dependencies of shared libraries.
     std::size_t oldMissingTargetsSize = this->MissingTargets.size();
+    auto oldExternalTargets = this->ExternalTargets;
     this->SetImportLinkProperty(
       suffix, target, "IMPORTED_LINK_DEPENDENT_LIBRARIES", iface->SharedDeps,
       properties, ImportLinkPropertyTargetNames::Yes);
     // Avoid enforcing shared library private dependencies as public package
     // dependencies by ignoring missing targets added for them.
     this->MissingTargets.resize(oldMissingTargetsSize);
+    this->ExternalTargets = std::move(oldExternalTargets);
 
     if (iface->Multiplicity > 0) {
       std::string prop =
@@ -953,20 +973,30 @@
   os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.8)\n"
      << "   message(FATAL_ERROR \"CMake >= 2.8.0 required\")\n"
      << "endif()\n"
-     << "if(CMAKE_VERSION VERSION_LESS \"2.8.3\")\n"
-     << "   message(FATAL_ERROR \"CMake >= 2.8.3 required\")\n"
+     << "if(CMAKE_VERSION VERSION_LESS \""
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << "\")\n"
+     << "   message(FATAL_ERROR \"CMake >= "
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << " required\")\n"
      << "endif()\n";
   /* clang-format on */
 
   // Isolate the file policy level.
-  // Support CMake versions as far back as 2.6 but also support using NEW
-  // policy settings for up to CMake 3.26 (this upper limit may be reviewed
+  // Support CMake versions as far back as the
+  // RequiredCMakeVersion{Major,Minor,Patch}, but also support using NEW
+  // policy settings for up to CMake 3.28 (this upper limit may be reviewed
   // and increased from time to time). This reduces the opportunity for CMake
   // warnings when an older export file is later used with newer CMake
   // versions.
   /* clang-format off */
   os << "cmake_policy(PUSH)\n"
-     << "cmake_policy(VERSION 2.8.3...3.26)\n";
+     << "cmake_policy(VERSION "
+     << this->RequiredCMakeVersionMajor << '.'
+     << this->RequiredCMakeVersionMinor << '.'
+     << this->RequiredCMakeVersionPatch << "...3.28)\n";
   /* clang-format on */
 }
 
@@ -1125,8 +1155,9 @@
 }
 
 void cmExportFileGenerator::GenerateImportPropertyCode(
-  std::ostream& os, const std::string& config, cmGeneratorTarget const* target,
-  ImportPropertyMap const& properties)
+  std::ostream& os, const std::string& config, const std::string& suffix,
+  cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+  const std::string& importedXcFrameworkLocation)
 {
   // Construct the imported target name.
   std::string targetName = this->Namespace;
@@ -1145,12 +1176,98 @@
   }
   os << ")\n";
   os << "set_target_properties(" << targetName << " PROPERTIES\n";
+  std::string importedLocationProp = cmStrCat("IMPORTED_LOCATION", suffix);
   for (auto const& property : properties) {
-    os << "  " << property.first << " "
-       << cmExportFileGeneratorEscape(property.second) << "\n";
+    if (importedXcFrameworkLocation.empty() ||
+        property.first != importedLocationProp) {
+      os << "  " << property.first << " "
+         << cmExportFileGeneratorEscape(property.second) << "\n";
+    }
   }
-  os << "  )\n"
-     << "\n";
+  os << "  )\n";
+  if (!importedXcFrameworkLocation.empty()) {
+    auto importedLocationIt = properties.find(importedLocationProp);
+    if (importedLocationIt != properties.end()) {
+      os << "if(NOT CMAKE_VERSION VERSION_LESS \"3.28\" AND IS_DIRECTORY "
+         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+         << ")\n"
+            "  set_property(TARGET "
+         << targetName << " PROPERTY " << importedLocationProp << " "
+         << cmExportFileGeneratorEscape(importedXcFrameworkLocation)
+         << ")\nelse()\n  set_property(TARGET " << targetName << " PROPERTY "
+         << importedLocationProp << " "
+         << cmExportFileGeneratorEscape(importedLocationIt->second)
+         << ")\nendif()\n";
+    }
+  }
+  os << "\n";
+}
+
+void cmExportFileGenerator::GenerateFindDependencyCalls(std::ostream& os)
+{
+  os << "include(CMakeFindDependencyMacro)\n";
+  std::map<std::string, cmExportSet::PackageDependency> packageDependencies;
+  auto* exportSet = this->GetExportSet();
+  if (exportSet) {
+    packageDependencies = exportSet->GetPackageDependencies();
+  }
+
+  for (cmGeneratorTarget const* gt : this->ExternalTargets) {
+    std::string findPackageName;
+    auto exportFindPackageName = gt->GetProperty("EXPORT_FIND_PACKAGE_NAME");
+    cmFindPackageStack pkgStack = gt->Target->GetFindPackageStack();
+    if (!exportFindPackageName.IsEmpty()) {
+      findPackageName = *exportFindPackageName;
+    } else {
+      if (!pkgStack.Empty()) {
+        cmFindPackageCall const& fpc = pkgStack.Top();
+        findPackageName = fpc.Name;
+      }
+    }
+    if (!findPackageName.empty()) {
+      auto& dep = packageDependencies[findPackageName];
+      if (!pkgStack.Empty()) {
+        dep.FindPackageIndex = pkgStack.Top().Index;
+      }
+      if (dep.Enabled == cmExportSet::PackageDependencyExportEnabled::Auto) {
+        dep.Enabled = cmExportSet::PackageDependencyExportEnabled::On;
+      }
+    }
+  }
+
+  std::vector<std::pair<std::string, cmExportSet::PackageDependency>>
+    packageDependenciesSorted(packageDependencies.begin(),
+                              packageDependencies.end());
+  std::sort(
+    packageDependenciesSorted.begin(), packageDependenciesSorted.end(),
+    [](const std::pair<std::string, cmExportSet::PackageDependency>& lhs,
+       const std::pair<std::string, cmExportSet::PackageDependency>& rhs)
+      -> bool {
+      if (lhs.second.SpecifiedIndex) {
+        if (rhs.second.SpecifiedIndex) {
+          return lhs.second.SpecifiedIndex < rhs.second.SpecifiedIndex;
+        }
+        assert(rhs.second.FindPackageIndex);
+        return true;
+      }
+      assert(lhs.second.FindPackageIndex);
+      if (rhs.second.SpecifiedIndex) {
+        return false;
+      }
+      assert(rhs.second.FindPackageIndex);
+      return lhs.second.FindPackageIndex < rhs.second.FindPackageIndex;
+    });
+
+  for (auto const& it : packageDependenciesSorted) {
+    if (it.second.Enabled == cmExportSet::PackageDependencyExportEnabled::On) {
+      os << "find_dependency(" << it.first << " REQUIRED";
+      for (auto const& arg : it.second.ExtraArguments) {
+        os << " " << cmOutputConverter::EscapeForCMake(arg);
+      }
+      os << ")\n";
+    }
+  }
+  os << "\n\n";
 }
 
 void cmExportFileGenerator::GenerateMissingTargetsCheckCode(std::ostream& os)
@@ -1214,10 +1331,16 @@
   /* clang-format off */
   os << "# Loop over all imported files and verify that they actually exist\n"
         "foreach(_cmake_target IN LISTS _cmake_import_check_targets)\n"
-        "  foreach(_cmake_file IN LISTS \"_cmake_import_check_files_for_${_cmake_target}\")\n"
-        "    if(NOT EXISTS \"${_cmake_file}\")\n"
-        "      message(FATAL_ERROR \"The imported target \\\"${_cmake_target}\\\""
-        " references the file\n"
+        "  if(CMAKE_VERSION VERSION_LESS \"3.28\"\n"
+        "      OR NOT DEFINED "
+        "_cmake_import_check_xcframework_for_${_cmake_target}\n"
+        "      OR NOT IS_DIRECTORY "
+        "\"${_cmake_import_check_xcframework_for_${_cmake_target}}\")\n"
+        "    foreach(_cmake_file IN LISTS "
+        "\"_cmake_import_check_files_for_${_cmake_target}\")\n"
+        "      if(NOT EXISTS \"${_cmake_file}\")\n"
+        "        message(FATAL_ERROR \"The imported target "
+        "\\\"${_cmake_target}\\\" references the file\n"
         "   \\\"${_cmake_file}\\\"\n"
         "but this file does not exist.  Possible reasons include:\n"
         "* The file was deleted, renamed, or moved to another location.\n"
@@ -1226,8 +1349,9 @@
         "   \\\"${CMAKE_CURRENT_LIST_FILE}\\\"\n"
         "but not all the files it references.\n"
         "\")\n"
-        "    endif()\n"
-        "  endforeach()\n"
+        "      endif()\n"
+        "    endforeach()\n"
+        "  endif()\n"
         "  unset(_cmake_file)\n"
         "  unset(\"_cmake_import_check_files_for_${_cmake_target}\")\n"
         "endforeach()\n"
@@ -1240,15 +1364,18 @@
 void cmExportFileGenerator::GenerateImportedFileChecksCode(
   std::ostream& os, cmGeneratorTarget* target,
   ImportPropertyMap const& properties,
-  const std::set<std::string>& importedLocations)
+  const std::set<std::string>& importedLocations,
+  const std::string& importedXcFrameworkLocation)
 {
   // Construct the imported target name.
   std::string targetName = cmStrCat(this->Namespace, target->GetExportName());
 
-  os << "list(APPEND _cmake_import_check_targets " << targetName
-     << " )\n"
-        "list(APPEND _cmake_import_check_files_for_"
-     << targetName << " ";
+  os << "list(APPEND _cmake_import_check_targets " << targetName << " )\n";
+  if (!importedXcFrameworkLocation.empty()) {
+    os << "set(_cmake_import_check_xcframework_for_" << targetName << ' '
+       << cmExportFileGeneratorEscape(importedXcFrameworkLocation) << ")\n";
+  }
+  os << "list(APPEND _cmake_import_check_files_for_" << targetName << " ";
 
   for (std::string const& li : importedLocations) {
     auto pi = properties.find(li);
@@ -1486,3 +1613,17 @@
 
   this->GenerateCxxModuleConfigInformation(name, ap);
 }
+
+void cmExportFileGenerator::SetRequiredCMakeVersion(unsigned int major,
+                                                    unsigned int minor,
+                                                    unsigned int patch)
+{
+  if (CMake_VERSION_ENCODE(major, minor, patch) >
+      CMake_VERSION_ENCODE(this->RequiredCMakeVersionMajor,
+                           this->RequiredCMakeVersionMinor,
+                           this->RequiredCMakeVersionPatch)) {
+    this->RequiredCMakeVersionMajor = major;
+    this->RequiredCMakeVersionMinor = minor;
+    this->RequiredCMakeVersionPatch = patch;
+  }
+}
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 30333d3..f619576 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -15,6 +15,7 @@
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
 
+class cmExportSet;
 class cmFileSet;
 class cmGeneratorTarget;
 class cmLocalGenerator;
@@ -61,6 +62,11 @@
       error.  */
   bool GenerateImportFile();
 
+  void SetExportPackageDependencies(bool exportPackageDependencies)
+  {
+    this->ExportPackageDependencies = exportPackageDependencies;
+  }
+
 protected:
   using ImportPropertyMap = std::map<std::string, std::string>;
 
@@ -78,16 +84,18 @@
   virtual void GenerateImportTargetCode(std::ostream& os,
                                         cmGeneratorTarget const* target,
                                         cmStateEnums::TargetType targetType);
-  virtual void GenerateImportPropertyCode(std::ostream& os,
-                                          const std::string& config,
-                                          cmGeneratorTarget const* target,
-                                          ImportPropertyMap const& properties);
+  virtual void GenerateImportPropertyCode(
+    std::ostream& os, const std::string& config, const std::string& suffix,
+    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+    const std::string& importedXcFrameworkLocation);
   virtual void GenerateImportedFileChecksCode(
     std::ostream& os, cmGeneratorTarget* target,
     ImportPropertyMap const& properties,
-    const std::set<std::string>& importedLocations);
+    const std::set<std::string>& importedLocations,
+    const std::string& importedXcFrameworkLocation);
   virtual void GenerateImportedFileCheckLoop(std::ostream& os);
   virtual void GenerateMissingTargetsCheckCode(std::ostream& os);
+  virtual void GenerateFindDependencyCalls(std::ostream& os);
 
   virtual void GenerateExpectedTargetsCode(std::ostream& os,
                                            const std::string& expectedTargets);
@@ -173,9 +181,6 @@
     std::string& input, cmGeneratorTarget const* target,
     FreeTargetsReplace replace = NoReplaceFreeTargets);
 
-  virtual void GenerateRequiredCMakeVersion(std::ostream& os,
-                                            const char* versionString);
-
   bool PopulateCxxModuleExportProperties(
     cmGeneratorTarget const* gte, ImportPropertyMap& properties,
     cmGeneratorExpression::PreprocessContext ctx,
@@ -196,6 +201,11 @@
                                       cmFileSet* fileSet,
                                       cmTargetExport* te) = 0;
 
+  virtual cmExportSet* GetExportSet() const { return nullptr; }
+
+  void SetRequiredCMakeVersion(unsigned int major, unsigned int minor,
+                               unsigned int patch);
+
   // The namespace in which the exports are placed in the generated file.
   std::string Namespace;
 
@@ -216,6 +226,14 @@
 
   std::vector<std::string> MissingTargets;
 
+  std::set<cmGeneratorTarget const*> ExternalTargets;
+
+  unsigned int RequiredCMakeVersionMajor = 2;
+  unsigned int RequiredCMakeVersionMinor = 8;
+  unsigned int RequiredCMakeVersionPatch = 3;
+
+  bool ExportPackageDependencies = false;
+
 private:
   void PopulateInterfaceProperty(const std::string&, const std::string&,
                                  cmGeneratorTarget const* target,
diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx
index d53254d..eaa85f3 100644
--- a/Source/cmExportInstallAndroidMKGenerator.cxx
+++ b/Source/cmExportInstallAndroidMKGenerator.cxx
@@ -80,8 +80,8 @@
 }
 
 void cmExportInstallAndroidMKGenerator::GenerateImportPropertyCode(
-  std::ostream&, const std::string&, cmGeneratorTarget const*,
-  ImportPropertyMap const&)
+  std::ostream&, const std::string&, const std::string&,
+  cmGeneratorTarget const*, ImportPropertyMap const&, const std::string&)
 {
 }
 
@@ -110,11 +110,6 @@
 {
 }
 
-void cmExportInstallAndroidMKGenerator::GenerateRequiredCMakeVersion(
-  std::ostream&, const char*)
-{
-}
-
 void cmExportInstallAndroidMKGenerator::CleanupTemporaryVariables(
   std::ostream&)
 {
@@ -127,7 +122,7 @@
 
 void cmExportInstallAndroidMKGenerator::GenerateImportedFileChecksCode(
   std::ostream&, cmGeneratorTarget*, ImportPropertyMap const&,
-  const std::set<std::string>&)
+  const std::set<std::string>&, const std::string&)
 {
 }
 
diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h
index 061358d..b1778ef 100644
--- a/Source/cmExportInstallAndroidMKGenerator.h
+++ b/Source/cmExportInstallAndroidMKGenerator.h
@@ -45,22 +45,22 @@
   void GenerateExpectedTargetsCode(
     std::ostream& os, const std::string& expectedTargets) override;
   void GenerateImportPropertyCode(
-    std::ostream& os, const std::string& config,
-    cmGeneratorTarget const* target,
-    ImportPropertyMap const& properties) override;
+    std::ostream& os, const std::string& config, const std::string& suffix,
+    cmGeneratorTarget const* target, ImportPropertyMap const& properties,
+    const std::string& importedXcFrameworkLocation) override;
   void GenerateMissingTargetsCheckCode(std::ostream& os) override;
+  void GenerateFindDependencyCalls(std::ostream&) override {}
   void GenerateInterfaceProperties(
     cmGeneratorTarget const* target, std::ostream& os,
     const ImportPropertyMap& properties) override;
   void GenerateImportPrefix(std::ostream& os) override;
   void LoadConfigFiles(std::ostream&) override;
-  void GenerateRequiredCMakeVersion(std::ostream& os,
-                                    const char* versionString) override;
   void CleanupTemporaryVariables(std::ostream&) override;
   void GenerateImportedFileCheckLoop(std::ostream& os) override;
   void GenerateImportedFileChecksCode(
     std::ostream& os, cmGeneratorTarget* target,
     ImportPropertyMap const& properties,
-    const std::set<std::string>& importedLocations) override;
+    const std::set<std::string>& importedLocations,
+    const std::string& importedXcFrameworkLocation) override;
   bool GenerateImportFileConfig(const std::string& config) override;
 };
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 348b12b..c5b9dc9 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -76,9 +76,6 @@
   // Compute the relative import prefix for the file
   this->GenerateImportPrefix(os);
 
-  bool require2_8_12 = false;
-  bool require3_0_0 = false;
-  bool require3_1_0 = false;
   bool requiresConfigFiles = false;
   // Create all the imported targets.
   for (cmTargetExport* te : allTargets) {
@@ -147,16 +144,16 @@
       if (this->PopulateInterfaceLinkLibrariesProperty(
             gt, cmGeneratorExpression::InstallInterface, properties) &&
           !this->ExportOld) {
-        require2_8_12 = true;
+        this->SetRequiredCMakeVersion(2, 8, 12);
       }
     }
     if (targetType == cmStateEnums::INTERFACE_LIBRARY) {
-      require3_0_0 = true;
+      this->SetRequiredCMakeVersion(3, 0, 0);
     }
     if (gt->GetProperty("INTERFACE_SOURCES")) {
       // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
       // can consume them.
-      require3_1_0 = true;
+      this->SetRequiredCMakeVersion(3, 1, 0);
     }
 
     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gt,
@@ -169,14 +166,6 @@
     this->GenerateTargetFileSets(gt, os, te);
   }
 
-  if (require3_1_0) {
-    this->GenerateRequiredCMakeVersion(os, "3.1.0");
-  } else if (require3_0_0) {
-    this->GenerateRequiredCMakeVersion(os, "3.0.0");
-  } else if (require2_8_12) {
-    this->GenerateRequiredCMakeVersion(os, "2.8.12");
-  }
-
   this->LoadConfigFiles(os);
 
   bool result = true;
@@ -388,9 +377,26 @@
       //                              properties);
 
       // Generate code in the export file.
-      this->GenerateImportPropertyCode(os, config, gtgt, properties);
-      this->GenerateImportedFileChecksCode(os, gtgt, properties,
-                                           importedLocations);
+      std::string importedXcFrameworkLocation = te->XcFrameworkLocation;
+      if (!importedXcFrameworkLocation.empty()) {
+        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
+          importedXcFrameworkLocation,
+          cmGeneratorExpression::PreprocessContext::InstallInterface, true);
+        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
+          importedXcFrameworkLocation, te->Target->GetLocalGenerator(), config,
+          te->Target, nullptr, te->Target);
+        if (!importedXcFrameworkLocation.empty() &&
+            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation) &&
+            !cmHasLiteralPrefix(importedXcFrameworkLocation,
+                                "${_IMPORT_PREFIX}/")) {
+          importedXcFrameworkLocation =
+            cmStrCat("${_IMPORT_PREFIX}/", importedXcFrameworkLocation);
+        }
+      }
+      this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
+                                       importedXcFrameworkLocation);
+      this->GenerateImportedFileChecksCode(
+        os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
     }
   }
 }
diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h
index 253a65e..7a72584 100644
--- a/Source/cmExportInstallFileGenerator.h
+++ b/Source/cmExportInstallFileGenerator.h
@@ -12,12 +12,13 @@
 #include <vector>
 
 #include "cmExportFileGenerator.h"
+#include "cmInstallExportGenerator.h"
 #include "cmStateTypes.h"
 
+class cmExportSet;
 class cmFileSet;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
-class cmInstallExportGenerator;
 class cmInstallTargetGenerator;
 class cmTargetExport;
 
@@ -123,6 +124,11 @@
   bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&,
                                                     std::string const&);
 
+  cmExportSet* GetExportSet() const override
+  {
+    return this->IEGen->GetExportSet();
+  }
+
   cmInstallExportGenerator* IEGen;
 
   // The import file generated for each configuration.
diff --git a/Source/cmExportSet.cxx b/Source/cmExportSet.cxx
index 3d4ef0a..b32bb8d 100644
--- a/Source/cmExportSet.cxx
+++ b/Source/cmExportSet.cxx
@@ -1,6 +1,6 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmExportSet.h"
+#include "cmExportSet.h" // IWYU pragma: associated
 
 #include <algorithm>
 #include <tuple>
@@ -11,7 +11,7 @@
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
-#include "cmTargetExport.h"
+#include "cmTargetExport.h" // IWYU pragma: associated
 
 cmExportSet::cmExportSet(std::string name)
   : Name(std::move(name))
@@ -20,6 +20,17 @@
 
 cmExportSet::~cmExportSet() = default;
 
+cmExportSet::PackageDependency& cmExportSet::GetPackageDependencyForSetup(
+  const std::string& name)
+{
+  auto& dep = this->PackageDependencies[name];
+  if (!dep.SpecifiedIndex) {
+    dep.SpecifiedIndex = this->NextPackageDependencyIndex;
+    this->NextPackageDependencyIndex++;
+  }
+  return dep;
+}
+
 bool cmExportSet::Compute(cmLocalGenerator* lg)
 {
   for (std::unique_ptr<cmTargetExport>& tgtExport : this->TargetExports) {
@@ -61,6 +72,16 @@
   this->Installations.push_back(installation);
 }
 
+void cmExportSet::SetXcFrameworkLocation(const std::string& name,
+                                         const std::string& location)
+{
+  for (auto& te : this->TargetExports) {
+    if (name == te->TargetName) {
+      te->XcFrameworkLocation = location;
+    }
+  }
+}
+
 cmExportSet& cmExportSetMap::operator[](const std::string& name)
 {
   auto it = this->find(name);
diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h
index b75a26d..f2fc4a7 100644
--- a/Source/cmExportSet.h
+++ b/Source/cmExportSet.h
@@ -9,6 +9,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/optional>
+
 class cmInstallExportGenerator;
 class cmLocalGenerator;
 class cmTargetExport;
@@ -31,6 +33,9 @@
 
   void AddInstallation(cmInstallExportGenerator const* installation);
 
+  void SetXcFrameworkLocation(const std::string& name,
+                              const std::string& location);
+
   std::string const& GetName() const { return this->Name; }
 
   std::vector<std::unique_ptr<cmTargetExport>> const& GetTargetExports() const
@@ -43,10 +48,36 @@
     return &this->Installations;
   }
 
+  enum class PackageDependencyExportEnabled
+  {
+    Auto,
+    Off,
+    On,
+  };
+
+  struct PackageDependency
+  {
+    PackageDependencyExportEnabled Enabled =
+      PackageDependencyExportEnabled::Auto;
+    std::vector<std::string> ExtraArguments;
+    cm::optional<unsigned int> SpecifiedIndex;
+    cm::optional<unsigned int> FindPackageIndex;
+  };
+
+  PackageDependency& GetPackageDependencyForSetup(const std::string& name);
+
+  const std::map<std::string, PackageDependency>& GetPackageDependencies()
+    const
+  {
+    return this->PackageDependencies;
+  }
+
 private:
   std::vector<std::unique_ptr<cmTargetExport>> TargetExports;
   std::string Name;
   std::vector<cmInstallExportGenerator const*> Installations;
+  std::map<std::string, PackageDependency> PackageDependencies;
+  unsigned int NextPackageDependencyIndex = 0;
 };
 
 /// A name -> cmExportSet map with overloaded operator[].
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 8b0f309..4524ba6 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -727,7 +727,7 @@
 // The "codemodel" object kind.
 
 // Update Help/manual/cmake-file-api.7.rst when updating this constant.
-static unsigned int const CodeModelV2Minor = 6;
+static unsigned int const CodeModelV2Minor = 7;
 
 void cmFileAPI::BuildClientRequestCodeModel(
   ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index d069186..e9302da 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -41,10 +41,12 @@
 #include "cmInstallSubdirectoryGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h" // IWYU pragma: keep
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmSourceGroup.h"
 #include "cmState.h"
@@ -503,6 +505,8 @@
   Json::Value DumpDependencies();
   Json::Value DumpDependency(cmTargetDepend const& td);
   Json::Value DumpFolder();
+  Json::Value DumpLauncher(const char* name, const char* type);
+  Json::Value DumpLaunchers();
 
 public:
   Target(cmGeneratorTarget* gt, std::string const& config);
@@ -1223,6 +1227,13 @@
     target["archive"] = this->DumpArchive();
   }
 
+  if (type == cmStateEnums::EXECUTABLE) {
+    Json::Value launchers = this->DumpLaunchers();
+    if (!launchers.empty()) {
+      target["launchers"] = std::move(launchers);
+    }
+  }
+
   Json::Value dependencies = this->DumpDependencies();
   if (!dependencies.empty()) {
     target["dependencies"] = dependencies;
@@ -2075,6 +2086,50 @@
   }
   return folder;
 }
+
+Json::Value Target::DumpLauncher(const char* name, const char* type)
+{
+  cmValue property = this->GT->GetProperty(name);
+  Json::Value launcher;
+  if (property) {
+    cmLocalGenerator* lg = this->GT->GetLocalGenerator();
+    cmGeneratorExpression ge(*lg->GetCMakeInstance());
+    cmList commandWithArgs{ ge.Parse(*property)->Evaluate(lg, this->Config) };
+    if (!commandWithArgs.empty() && !commandWithArgs[0].empty()) {
+      std::string command(commandWithArgs[0]);
+      cmSystemTools::ConvertToUnixSlashes(command);
+      launcher = Json::objectValue;
+      launcher["command"] = RelativeIfUnder(this->TopSource, command);
+      launcher["type"] = type;
+      Json::Value args;
+      for (std::string const& arg : cmMakeRange(commandWithArgs).advance(1)) {
+        args.append(arg);
+      }
+      if (!args.empty()) {
+        launcher["arguments"] = std::move(args);
+      }
+    }
+  }
+  return launcher;
+}
+
+Json::Value Target::DumpLaunchers()
+{
+  Json::Value launchers;
+  {
+    Json::Value launcher = DumpLauncher("TEST_LAUNCHER", "test");
+    if (!launcher.empty()) {
+      launchers.append(std::move(launcher));
+    }
+  }
+  if (this->GT->Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
+    Json::Value emulator = DumpLauncher("CROSSCOMPILING_EMULATOR", "emulator");
+    if (!emulator.empty()) {
+      launchers.append(std::move(emulator));
+    }
+  }
+  return launchers;
+}
 }
 
 Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version)
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index c65136c..e3f5b96 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -315,6 +315,7 @@
   unsigned int limit_count = 0;
   cmsys::RegularExpression regex;
   bool have_regex = false;
+  bool store_regex = true;
   bool newline_consume = false;
   bool hex_conversion_enabled = true;
   enum
@@ -399,6 +400,26 @@
         return false;
       }
       have_regex = true;
+      switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0159)) {
+        case cmPolicies::REQUIRED_IF_USED:
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::NEW:
+          // store_regex = true
+          break;
+        case cmPolicies::WARN:
+          if (status.GetMakefile().PolicyOptionalWarningEnabled(
+                "CMAKE_POLICY_WARNING_CMP0159")) {
+            status.GetMakefile().IssueMessage(
+              MessageType::AUTHOR_WARNING,
+              cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0159), '\n',
+                       "For compatibility, CMake is leaving CMAKE_MATCH_<n> "
+                       "unchanged."));
+          }
+          CM_FALLTHROUGH;
+        case cmPolicies::OLD:
+          store_regex = false;
+          break;
+      }
       arg_mode = arg_none;
     } else if (arg_mode == arg_encoding) {
       if (args[i] == "UTF-8") {
@@ -539,6 +560,10 @@
       // string matches the requirements.  The length may now be as
       // low as zero since blank lines are allowed.
       if (s.length() >= minlen && (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -555,6 +580,10 @@
       // be at least one no matter what the user specified.
       if (s.length() >= minlen && !s.empty() &&
           (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -572,6 +601,10 @@
     if (maxlen > 0 && s.size() == maxlen) {
       // Terminate a string if the maximum length is reached.
       if (s.length() >= minlen && (!have_regex || regex.find(s))) {
+        if (store_regex) {
+          status.GetMakefile().ClearMatches();
+          status.GetMakefile().StoreMatches(regex);
+        }
         output_size += static_cast<int>(s.size()) + 1;
         if (limit_output >= 0 && output_size >= limit_output) {
           s.clear();
@@ -588,6 +621,10 @@
   // matches the requirements.
   if ((!limit_count || strings.size() < limit_count) && !s.empty() &&
       s.length() >= minlen && (!have_regex || regex.find(s))) {
+    if (store_regex) {
+      status.GetMakefile().ClearMatches();
+      status.GetMakefile().StoreMatches(regex);
+    }
     output_size += static_cast<int>(s.size()) + 1;
     if (limit_output < 0 || output_size < limit_output) {
       strings.push_back(s);
@@ -1322,13 +1359,15 @@
       if (oldPolicyPath != realPath) {
         status.GetMakefile().IssueMessage(
           MessageType::AUTHOR_WARNING,
-          cmStrCat(
-            cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), '\n',
-            "From input path:\n  ", input,
-            "\nthe policy OLD behavior produces path:\n  ", oldPolicyPath,
-            "\nbut the policy NEW behavior produces path:\n  ", realPath,
-            "\nSince the policy is not set, CMake is using the OLD "
-            "behavior for compatibility."));
+          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152),
+                   "\n"
+                   "From input path:\n  ",
+                   input, "\nthe policy OLD behavior produces path:\n  ",
+                   oldPolicyPath,
+                   "\nbut the policy NEW behavior produces path:\n  ",
+                   realPath,
+                   "\nSince the policy is not set, CMake is using the OLD "
+                   "behavior for compatibility."));
       }
     }
     realPath = oldPolicyPath;
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 30458cd..9b51b1a 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -1044,6 +1044,8 @@
   PushPopRootPathStack pushPopRootPathStack(*this);
   SetRestoreFindDefinitions setRestoreFindDefinitions(*this, components,
                                                       componentVarDefs);
+  cmMakefile::FindPackageStackRAII findPackageStackRAII(this->Makefile,
+                                                        this->Name);
 
   // See if we have been told to delegate to FetchContent or some other
   // redirected config package first. We have to check all names that
diff --git a/Source/cmFindPackageStack.cxx b/Source/cmFindPackageStack.cxx
new file mode 100644
index 0000000..1aeb2a7
--- /dev/null
+++ b/Source/cmFindPackageStack.cxx
@@ -0,0 +1,7 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#define cmFindPackageStack_cxx
+#include "cmFindPackageStack.h"
+
+#include "cmConstStack.tcc" // IWYU pragma: keep
+template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
diff --git a/Source/cmFindPackageStack.h b/Source/cmFindPackageStack.h
new file mode 100644
index 0000000..2062fbc
--- /dev/null
+++ b/Source/cmFindPackageStack.h
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <memory>
+#include <string>
+
+#include "cmConstStack.h"
+
+/**
+ * Represents one call to find_package.
+ */
+class cmFindPackageCall
+{
+public:
+  std::string Name;
+  unsigned int Index;
+};
+
+/**
+ * Represents a stack of find_package calls with efficient value semantics.
+ */
+class cmFindPackageStack
+  : public cmConstStack<cmFindPackageCall, cmFindPackageStack>
+{
+  using cmConstStack::cmConstStack;
+  friend class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+};
+#ifndef cmFindPackageStack_cxx
+extern template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+#endif
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index 21a140d..33dae79 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -272,6 +272,11 @@
     if (status.GetContinueInvoked()) {
       break;
     }
+    if (status.HasExitCode()) {
+      inStatus.SetExitCode(status.GetExitCode());
+      result.Break = true;
+      break;
+    }
     if (cmSystemTools::GetFatalErrorOccurred()) {
       result.Restore = false;
       result.Break = true;
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 8d2d972..33721fc 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -124,6 +124,10 @@
       makefile.RaiseScope(status.GetReturnVariables());
       break;
     }
+    if (status.HasExitCode()) {
+      inStatus.SetExitCode(status.GetExitCode());
+      break;
+    }
   }
 
   // pop scope on the makefile
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index d51dbd0..4e46df7 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -175,14 +175,15 @@
   cm::string_view property(this->Top()->Property);
 
   return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
-    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s;
+    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s ||
+    property == "LINKER_TYPE"_s;
 }
 
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
 {
   cm::string_view property(this->Top()->Property);
 
-  return property == "LINK_OPTIONS"_s;
+  return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s;
 }
 
 bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index c8147b2..4274448 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1859,6 +1859,7 @@
 static const CharacterNode<'>'> angle_rNode;
 static const CharacterNode<','> commaNode;
 static const CharacterNode<';'> semicolonNode;
+static const CharacterNode<'"'> quoteNode;
 
 struct CompilerIdNode : public cmGeneratorExpressionNode
 {
@@ -4529,6 +4530,7 @@
     { "ANGLE-R", &angle_rNode },
     { "COMMA", &commaNode },
     { "SEMICOLON", &semicolonNode },
+    { "QUOTE", &quoteNode },
     { "TARGET_PROPERTY", &targetPropertyNode },
     { "TARGET_NAME", &targetNameNode },
     { "TARGET_OBJECTS", &targetObjectsNode },
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 3cfa5a9..28ee442 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -868,6 +868,31 @@
   return this->LocalGenerator->GetFeature(feature, config);
 }
 
+std::string cmGeneratorTarget::GetLinkerTypeProperty(
+  std::string const& lang, std::string const& config) const
+{
+  std::string propName{ "LINKER_TYPE" };
+  auto linkerType = this->GetProperty(propName);
+  if (!linkerType.IsEmpty()) {
+    cmGeneratorExpressionDAGChecker dagChecker(this, propName, nullptr,
+                                               nullptr);
+    auto ltype =
+      cmGeneratorExpression::Evaluate(*linkerType, this->GetLocalGenerator(),
+                                      config, this, &dagChecker, this, lang);
+    if (this->IsDeviceLink()) {
+      cmList list{ ltype };
+      const auto DL_BEGIN = "<DEVICE_LINK>"_s;
+      const auto DL_END = "</DEVICE_LINK>"_s;
+      cm::erase_if(list, [&](const std::string& item) {
+        return item == DL_BEGIN || item == DL_END;
+      });
+      return list.to_string();
+    }
+    return ltype;
+  }
+  return std::string{};
+}
+
 const char* cmGeneratorTarget::GetLinkPIEProperty(
   const std::string& config) const
 {
@@ -1821,32 +1846,31 @@
   AddInterfaceEntries(this, config, "INTERFACE_SOURCES", std::string(),
                       &dagChecker, linkInterfaceSourcesEntries,
                       IncludeRuntimeInterface::No, LinkInterfaceFor::Usage);
-  std::vector<std::string>::size_type numFilesBefore = files.size();
   bool contextDependentInterfaceSources = processSources(
     this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
 
   // Collect TARGET_OBJECTS of direct object link-dependencies.
   bool contextDependentObjects = false;
-  std::vector<std::string>::size_type numFilesBefore2 = files.size();
   if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
     EvaluatedTargetPropertyEntries linkObjectsEntries;
     AddObjectEntries(this, config, &dagChecker, linkObjectsEntries);
     contextDependentObjects = processSources(this, linkObjectsEntries, files,
                                              uniqueSrcs, debugSources);
+    // Note that for imported targets or multi-config generators supporting
+    // cross-config builds the paths to the object files must be per-config,
+    // so contextDependentObjects will be true here even if object libraries
+    // are specified without per-config generator expressions.
   }
 
   // Collect this target's file sets.
-  std::vector<std::string>::size_type numFilesBefore3 = files.size();
   EvaluatedTargetPropertyEntries fileSetEntries;
   AddFileSetEntries(this, config, &dagChecker, fileSetEntries);
   bool contextDependentFileSets =
     processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
 
   // Determine if sources are context-dependent or not.
-  if (!contextDependentDirectSources &&
-      !(contextDependentInterfaceSources && numFilesBefore < files.size()) &&
-      !(contextDependentObjects && numFilesBefore2 < files.size()) &&
-      !(contextDependentFileSets && numFilesBefore3 < files.size())) {
+  if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
+      !contextDependentObjects && !contextDependentFileSets) {
     this->SourcesAreContextDependent = Tribool::False;
   } else {
     this->SourcesAreContextDependent = Tribool::True;
@@ -3554,7 +3578,7 @@
 
       flags += "]\"";
     }
-  } else if (compiler == "Clang") {
+  } else if (compiler == "Clang" && compileOrLink == cmBuildStep::Compile) {
     for (CudaArchitecture& architecture : architectures) {
       flags += " --cuda-gpu-arch=sm_" + architecture.name;
 
@@ -5528,6 +5552,64 @@
   return this->GetLinkClosure(config)->LinkerLanguage;
 }
 
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& config) const
+{
+  return this->GetLinkerTool(this->GetLinkerLanguage(config), config);
+}
+
+std::string cmGeneratorTarget::GetLinkerTool(const std::string& lang,
+                                             const std::string& config) const
+{
+  auto usingLinker =
+    cmStrCat("CMAKE_", lang, "_USING_", this->IsDeviceLink() ? "DEVICE_" : "",
+             "LINKER_");
+  auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+  if (!format || format != "TOOL"_s) {
+    return this->Makefile->GetDefinition("CMAKE_LINKER");
+  }
+
+  auto linkerType = this->GetLinkerTypeProperty(lang, config);
+  if (linkerType.empty()) {
+    linkerType = "DEFAULT";
+  }
+  usingLinker = cmStrCat(usingLinker, linkerType);
+  auto linkerTool = this->Makefile->GetDefinition(usingLinker);
+
+  if (!linkerTool) {
+    if (this->GetGlobalGenerator()->IsVisualStudio() &&
+        linkerType == "DEFAULT"_s) {
+      return std::string{};
+    }
+
+    // fall-back to generic definition
+    linkerTool = this->Makefile->GetDefinition("CMAKE_LINKER");
+
+    if (linkerType != "DEFAULT"_s) {
+      this->LocalGenerator->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("LINKER_TYPE '", linkerType,
+                 "' is unknown. Did you forgot to define '", usingLinker,
+                 "' variable?"));
+    }
+  }
+
+  return linkerTool;
+}
+
+bool cmGeneratorTarget::LinkerEnforcesNoAllowShLibUndefined(
+  std::string const& config) const
+{
+  // FIXME(#25486): Account for the LINKER_TYPE target property.
+  // Also factor out the hard-coded list below into a platform
+  // information table based on the linker id.
+  std::string ll = this->GetLinkerLanguage(config);
+  std::string linkerIdVar = cmStrCat("CMAKE_", ll, "_COMPILER_LINKER_ID");
+  cmValue linkerId = this->Makefile->GetDefinition(linkerIdVar);
+  // The GNU bfd-based linker may enforce '--no-allow-shlib-undefined'
+  // recursively by default.  The Solaris linker has similar behavior.
+  return linkerId && (*linkerId == "GNU" || *linkerId == "Solaris");
+}
+
 std::string cmGeneratorTarget::GetPDBOutputName(
   const std::string& config) const
 {
@@ -6846,7 +6928,8 @@
 
 cm::optional<cmLinkItem> cmGeneratorTarget::LookupLinkItem(
   std::string const& n, cmListFileBacktrace const& bt,
-  LookupLinkItemScope* scope, LookupSelf lookupSelf) const
+  std::string const& linkFeature, LookupLinkItemScope* scope,
+  LookupSelf lookupSelf) const
 {
   cm::optional<cmLinkItem> maybeItem;
   if (this->IsLinkLookupScope(n, scope->LG)) {
@@ -6858,7 +6941,8 @@
       (lookupSelf == LookupSelf::No && name == this->GetName())) {
     return maybeItem;
   }
-  maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG);
+  maybeItem =
+    this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG, linkFeature);
   return maybeItem;
 }
 
@@ -6889,9 +6973,16 @@
     cmList libs{ cge->Evaluate(this->LocalGenerator, config, headTarget,
                                &dagChecker, this,
                                headTarget->LinkerLanguage) };
+
+    auto linkFeature = cmLinkItem::DEFAULT;
     for (auto const& lib : libs) {
+      if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
+        linkFeature = std::move(*maybeLinkFeature);
+        continue;
+      }
+
       if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
-            lib, cge->GetBacktrace(), &scope,
+            lib, cge->GetBacktrace(), linkFeature, &scope,
             field == LinkInterfaceField::Libraries ? LookupSelf::No
                                                    : LookupSelf::Yes)) {
         cmLinkItem item = std::move(*maybeItem);
@@ -7637,9 +7728,16 @@
                           LinkInterfaceField::Libraries, iface);
     cmList deps{ info->SharedDeps };
     LookupLinkItemScope scope{ this->LocalGenerator };
+
+    auto linkFeature = cmLinkItem::DEFAULT;
     for (auto const& dep : deps) {
+      if (auto maybeLinkFeature = ParseLinkFeature(dep)) {
+        linkFeature = std::move(*maybeLinkFeature);
+        continue;
+      }
+
       if (cm::optional<cmLinkItem> maybeItem = this->LookupLinkItem(
-            dep, cmListFileBacktrace(), &scope, LookupSelf::No)) {
+            dep, cmListFileBacktrace(), linkFeature, &scope, LookupSelf::No)) {
         iface.SharedDeps.emplace_back(std::move(*maybeItem));
       }
     }
@@ -8440,7 +8538,13 @@
       impl.HadLinkLanguageSensitiveCondition = true;
     }
 
+    auto linkFeature = cmLinkItem::DEFAULT;
     for (auto const& lib : llibs) {
+      if (auto maybeLinkFeature = ParseLinkFeature(lib)) {
+        linkFeature = std::move(*maybeLinkFeature);
+        continue;
+      }
+
       if (this->IsLinkLookupScope(lib, lg)) {
         continue;
       }
@@ -8487,8 +8591,8 @@
       }
 
       // The entry is meant for this configuration.
-      cmLinkItem item =
-        this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg);
+      cmLinkItem item = this->ResolveLinkItem(
+        BT<std::string>(name, entry.Backtrace), lg, linkFeature);
       if (item.Target) {
         auto depsForTarget = synthTargetsForConfig.find(item.Target);
         if (depsForTarget != synthTargetsForConfig.end()) {
@@ -8536,7 +8640,14 @@
     CMP0003_ComputeLinkType(config, debugConfigs);
   cmTarget::LinkLibraryVectorType const& oldllibs =
     this->Target->GetOriginalLinkLibraries();
+
+  auto linkFeature = cmLinkItem::DEFAULT;
   for (cmTarget::LibraryID const& oldllib : oldllibs) {
+    if (auto maybeLinkFeature = ParseLinkFeature(oldllib.first)) {
+      linkFeature = std::move(*maybeLinkFeature);
+      continue;
+    }
+
     if (oldllib.second != GENERAL_LibraryType && oldllib.second != linkType) {
       std::string name = this->CheckCMP0004(oldllib.first);
       if (name == this->GetName() || name.empty()) {
@@ -8544,7 +8655,7 @@
       }
       // Support OLD behavior for CMP0003.
       impl.WrongConfigLibraries.push_back(
-        this->ResolveLinkItem(BT<std::string>(name)));
+        this->ResolveLinkItem(BT<std::string>(name), linkFeature));
     }
   }
 }
@@ -8570,19 +8681,20 @@
 }
 
 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
-  BT<std::string> const& name) const
+  BT<std::string> const& name, std::string const& linkFeature) const
 {
-  return this->ResolveLinkItem(name, this->LocalGenerator);
+  return this->ResolveLinkItem(name, this->LocalGenerator, linkFeature);
 }
 
-cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name,
-                                              cmLocalGenerator const* lg) const
+cmLinkItem cmGeneratorTarget::ResolveLinkItem(
+  BT<std::string> const& name, cmLocalGenerator const* lg,
+  std::string const& linkFeature) const
 {
   auto bt = name.Backtrace;
   TargetOrString resolved = this->ResolveTargetReference(name.Value, lg);
 
   if (!resolved.Target) {
-    return cmLinkItem(resolved.String, false, bt);
+    return cmLinkItem(resolved.String, false, bt, linkFeature);
   }
 
   // Check deprecation, issue message with `bt` backtrace.
@@ -8603,10 +8715,10 @@
   // within the project.
   if (resolved.Target->GetType() == cmStateEnums::EXECUTABLE &&
       !resolved.Target->IsExecutableWithExports()) {
-    return cmLinkItem(resolved.Target->GetName(), false, bt);
+    return cmLinkItem(resolved.Target->GetName(), false, bt, linkFeature);
   }
 
-  return cmLinkItem(resolved.Target, false, bt);
+  return cmLinkItem(resolved.Target, false, bt, linkFeature);
 }
 
 bool cmGeneratorTarget::HasPackageReferences() const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index c13b2f6..fb56a0d 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -206,6 +206,9 @@
   cmValue GetFeature(const std::string& feature,
                      const std::string& config) const;
 
+  std::string GetLinkerTypeProperty(std::string const& lang,
+                                    std::string const& config) const;
+
   const char* GetLinkPIEProperty(const std::string& config) const;
 
   bool IsIPOEnabled(std::string const& lang, std::string const& config) const;
@@ -447,9 +450,12 @@
   TargetOrString ResolveTargetReference(std::string const& name,
                                         cmLocalGenerator const* lg) const;
 
-  cmLinkItem ResolveLinkItem(BT<std::string> const& name) const;
-  cmLinkItem ResolveLinkItem(BT<std::string> const& name,
-                             cmLocalGenerator const* lg) const;
+  cmLinkItem ResolveLinkItem(
+    BT<std::string> const& name,
+    std::string const& linkFeature = cmLinkItem::DEFAULT) const;
+  cmLinkItem ResolveLinkItem(
+    BT<std::string> const& name, cmLocalGenerator const* lg,
+    std::string const& linkFeature = cmLinkItem::DEFAULT) const;
 
   bool HasPackageReferences() const;
   std::vector<std::string> GetPackageReferences() const;
@@ -794,6 +800,13 @@
 
   //! Return the preferred linker language for this target
   std::string GetLinkerLanguage(const std::string& config) const;
+  //! Return the preferred linker tool for this target
+  std::string GetLinkerTool(const std::string& config) const;
+  std::string GetLinkerTool(const std::string& lang,
+                            const std::string& config) const;
+
+  /** Is the linker known to enforce '--no-allow-shlib-undefined'? */
+  bool LinkerEnforcesNoAllowShLibUndefined(std::string const& config) const;
 
   /** Does this target have a GNU implib to convert to MS format?  */
   bool HasImplibGNUtoMS(std::string const& config) const;
@@ -1175,6 +1188,7 @@
   };
   cm::optional<cmLinkItem> LookupLinkItem(std::string const& n,
                                           cmListFileBacktrace const& bt,
+                                          std::string const& linkFeature,
                                           LookupLinkItemScope* scope,
                                           LookupSelf lookupSelf) const;
 
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 95e2187..940f49d 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -116,6 +116,19 @@
 
 void cmGhsMultiTargetGenerator::GenerateTarget()
 {
+  if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE &&
+      !this->GeneratorTarget
+         ->GetLinkerTypeProperty(
+           this->GeneratorTarget->GetLinkerLanguage(this->ConfigName),
+           this->ConfigName)
+         .empty()) {
+    // Green Hill MULTI does not support this feature.
+    cmSystemTools::Message(
+      cmStrCat("'LINKER_TYPE' property, specified on target '",
+               this->GeneratorTarget->GetName(),
+               "', is not supported by this generator."));
+  }
+
   // Open the target file in copy-if-different mode.
   std::string fproj =
     cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index d44863d..71ab2a6 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -694,7 +694,22 @@
     std::string includes =
       mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
     cmList includesList{ includes };
-    for (std::string const& setupFile : includesList) {
+    for (std::string setupFile : includesList) {
+      // Any relative path without a .cmake extension is checked for valid
+      // cmake modules. This logic should be consistent with CMake's include()
+      // command. Otherwise default to checking relative path w.r.t. source
+      // directory
+      if (!cmSystemTools::FileIsFullPath(setupFile) &&
+          !cmHasLiteralSuffix(setupFile, ".cmake")) {
+        std::string mfile = mf->GetModulesFile(cmStrCat(setupFile, ".cmake"));
+        if (mfile.empty()) {
+          cmSystemTools::Error(cmStrCat(
+            "CMAKE_PROJECT_TOP_LEVEL_INCLUDES module:\n  ", setupFile));
+          mf->GetState()->SetInTopLevelIncludes(false);
+          return;
+        }
+        setupFile = mfile;
+      }
       std::string absSetupFile = cmSystemTools::CollapseFullPath(
         setupFile, mf->GetCurrentSourceDirectory());
       if (!cmSystemTools::FileExists(absSetupFile)) {
@@ -859,7 +874,11 @@
         noCompiler <<
           "The " << compilerName << ":\n"
           "  " << *compilerFile << "\n"
-          "is not a full path and was not found in the PATH.\n"
+          "is not a full path and was not found in the PATH."
+#ifdef _WIN32
+          "  Perhaps the extension is missing?"
+#endif
+          "\n"
           ;
         /* clang-format on */
       } else if (!cmSystemTools::FileExists(*compilerFile)) {
@@ -1139,20 +1158,26 @@
 {
   const std::string& lang = source.GetLanguage();
   if (!lang.empty()) {
-    auto const it = this->LanguageToOutputExtension.find(lang);
-    if (it != this->LanguageToOutputExtension.end()) {
-      return it->second;
+    return this->GetLanguageOutputExtension(lang);
+  }
+  // if no language is found then check to see if it is already an
+  // output extension for some language.  In that case it should be ignored
+  // and in this map, so it will not be compiled but will just be used.
+  std::string const& ext = source.GetExtension();
+  if (!ext.empty()) {
+    if (this->OutputExtensions.count(ext)) {
+      return ext;
     }
-  } else {
-    // if no language is found then check to see if it is already an
-    // output extension for some language.  In that case it should be ignored
-    // and in this map, so it will not be compiled but will just be used.
-    std::string const& ext = source.GetExtension();
-    if (!ext.empty()) {
-      if (this->OutputExtensions.count(ext)) {
-        return ext;
-      }
-    }
+  }
+  return "";
+}
+
+std::string cmGlobalGenerator::GetLanguageOutputExtension(
+  std::string const& lang) const
+{
+  auto const it = this->LanguageToOutputExtension.find(lang);
+  if (it != this->LanguageToOutputExtension.end()) {
+    return it->second;
   }
   return "";
 }
@@ -2858,6 +2883,14 @@
   gti.Name = this->GetTestTargetName();
   gti.Message = "Running tests...";
   gti.UsesTerminal = true;
+  // Unlike the 'install' target, the 'test' target does not depend on 'all'
+  // by default.  Enable it only if CMAKE_SKIP_TEST_ALL_DEPENDENCY is
+  // explicitly set to OFF.
+  if (cmValue noall = mf->GetDefinition("CMAKE_SKIP_TEST_ALL_DEPENDENCY")) {
+    if (cmIsOff(noall)) {
+      gti.Depends.emplace_back(this->GetAllTargetName());
+    }
+  }
   cmCustomCommandLine singleLine;
   singleLine.push_back(cmSystemTools::GetCTestCommand());
   singleLine.push_back("--force-new-ctest-process");
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index bc80547..ba39768 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -349,6 +349,8 @@
   int GetLinkerPreference(const std::string& lang) const;
   //! What is the object file extension for a given source file?
   std::string GetLanguageOutputExtension(cmSourceFile const&) const;
+  //! What is the object file extension for a given language?
+  std::string GetLanguageOutputExtension(std::string const& lang) const;
 
   //! What is the configurations directory variable called?
   virtual const char* GetCMakeCFGIntDir() const { return "."; }
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 6acdc34..f018a8c 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -378,6 +378,15 @@
   }
 
   {
+    std::string ninjaDepfilePath;
+    bool depfileIsOutput = false;
+    if (!depfile.empty()) {
+      ninjaDepfilePath = this->ConvertToNinjaPath(depfile);
+      depfileIsOutput =
+        std::find(outputs.ExplicitOuts.begin(), outputs.ExplicitOuts.end(),
+                  ninjaDepfilePath) != outputs.ExplicitOuts.end();
+    }
+
     cmNinjaBuild build("CUSTOM_COMMAND");
     build.Comment = comment;
     build.Outputs = std::move(outputs.ExplicitOuts);
@@ -405,7 +414,13 @@
       vars["pool"] = job_pool;
     }
     if (!depfile.empty()) {
-      vars["depfile"] = depfile;
+      vars["depfile"] = ninjaDepfilePath;
+      // Add the depfile to the `.ninja_deps` database. Since this (generally)
+      // removes the file, it cannot be declared as an output or byproduct of
+      // the command.
+      if (!depfileIsOutput) {
+        vars["deps"] = "gcc";
+      }
     }
     if (config.empty()) {
       this->WriteBuild(*this->GetCommonFileStream(), build);
@@ -813,6 +828,9 @@
     this->NinjaExpectedEncoding = codecvt_Encoding::ANSI;
   }
 #endif
+  this->NinjaSupportsCWDDepend =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForCWDDepend());
 }
 
 void cmGlobalNinjaGenerator::CheckNinjaCodePage()
@@ -1359,10 +1377,10 @@
   } else {
     cmNinjaDeps outs;
 
-    auto computeISPCOuputs = [](cmGlobalNinjaGenerator* gg,
-                                cmGeneratorTarget const* depTarget,
-                                cmNinjaDeps& outputDeps,
-                                const std::string& targetConfig) {
+    auto computeISPCOutputs = [](cmGlobalNinjaGenerator* gg,
+                                 cmGeneratorTarget const* depTarget,
+                                 cmNinjaDeps& outputDeps,
+                                 const std::string& targetConfig) {
       if (depTarget->CanCompileSources()) {
         auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig);
         if (!headers.empty()) {
@@ -1386,10 +1404,10 @@
       }
       if (targetDep.IsCross()) {
         this->AppendTargetOutputs(targetDep, outs, fileConfig, depends);
-        computeISPCOuputs(this, targetDep, outs, fileConfig);
+        computeISPCOutputs(this, targetDep, outs, fileConfig);
       } else {
         this->AppendTargetOutputs(targetDep, outs, config, depends);
-        computeISPCOuputs(this, targetDep, outs, config);
+        computeISPCOutputs(this, targetDep, outs, config);
       }
     }
     std::sort(outs.begin(), outs.end());
@@ -1981,6 +1999,11 @@
   return this->NinjaSupportsMultilineDepfile;
 }
 
+bool cmGlobalNinjaGenerator::SupportsCWDDepend() const
+{
+  return this->NinjaSupportsCWDDepend;
+}
+
 bool cmGlobalNinjaGenerator::WriteTargetCleanAdditional(std::ostream& os)
 {
   const auto& lgr = this->LocalGenerators.at(0);
@@ -3250,10 +3273,3 @@
   return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_',
                   cmSystemTools::UpperCase(config));
 }
-
-std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTargetPrivate(
-  cmGeneratorTarget const* target, const std::string& config) const
-{
-  return cmStrCat(this->OrderDependsTargetForTarget(target, config),
-                  "_private");
-}
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 220d393..64eed4d 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -349,7 +349,7 @@
   virtual std::string OrderDependsTargetForTarget(
     cmGeneratorTarget const* target, const std::string& config) const;
 
-  virtual std::string OrderDependsTargetForTargetPrivate(
+  std::string OrderDependsTargetForTargetPrivate(
     cmGeneratorTarget const* target, const std::string& config) const;
 
   void AppendTargetOutputs(cmGeneratorTarget const* target,
@@ -415,10 +415,12 @@
     return "1.10.2";
   }
   static std::string RequiredNinjaVersionForCodePage() { return "1.11"; }
+  static std::string RequiredNinjaVersionForCWDDepend() { return "1.7"; }
   bool SupportsDirectConsole() const override;
   bool SupportsImplicitOuts() const;
   bool SupportsManifestRestat() const;
   bool SupportsMultilineDepfile() const;
+  bool SupportsCWDDepend() const;
 
   std::string NinjaOutputPath(std::string const& path) const;
   bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
@@ -597,6 +599,7 @@
   bool NinjaSupportsMultipleOutputs = false;
   bool NinjaSupportsMetadataOnRegeneration = false;
   bool NinjaSupportsCodePage = false;
+  bool NinjaSupportsCWDDepend = false;
 
   codecvt_Encoding NinjaExpectedEncoding = codecvt_Encoding::None;
 
@@ -742,9 +745,6 @@
   std::string OrderDependsTargetForTarget(
     cmGeneratorTarget const* target, const std::string& config) const override;
 
-  std::string OrderDependsTargetForTargetPrivate(
-    cmGeneratorTarget const* target, const std::string& config) const override;
-
 protected:
   bool OpenBuildFileStreams() override;
   void CloseBuildFileStreams() override;
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 541db63..c93b140 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -191,6 +191,23 @@
     }
   }
 
+  if (this->GeneratorToolsetFortran) {
+    if (*this->GeneratorToolsetFortran != "ifx" &&
+        *this->GeneratorToolsetFortran != "ifort") {
+      mf->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("Generator\n"
+                                "  ",
+                                this->GetName(),
+                                "\n"
+                                "given toolset\n"
+                                "  fortran=",
+                                *this->GeneratorToolsetFortran,
+                                "\n"
+                                "but the value is not \"ifx\" or \"ifort\"."));
+      this->GeneratorToolsetFortran = cm::nullopt;
+    }
+  }
+
   if (!this->GeneratorToolsetVersion.empty() &&
       this->GeneratorToolsetVersion != "Test Toolset Version"_s) {
     // If a specific minor version of the MSVC toolset is requested, verify
@@ -300,6 +317,9 @@
   if (const char* cudaDir = this->GetPlatformToolsetCudaCustomDir()) {
     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR", cudaDir);
   }
+  if (cm::optional<std::string> fortran = this->GetPlatformToolsetFortran()) {
+    mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_FORTRAN", *fortran);
+  }
   if (const char* vcTargetsDir = this->GetCustomVCTargetsPath()) {
     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR",
                       vcTargetsDir);
@@ -410,6 +430,10 @@
     cmSystemTools::ConvertToUnixSlashes(this->CustomFlagTableDir);
     return true;
   }
+  if (key == "fortran"_s) {
+    this->GeneratorToolsetFortran = value;
+    return true;
+  }
   if (key == "version"_s) {
     this->GeneratorToolsetVersion = value;
     return true;
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index 40bdd71..a2b351c 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -93,6 +93,12 @@
    * directory */
   std::string const& GetPlatformToolsetCudaVSIntegrationSubdirString() const;
 
+  /** The fortran toolset name.  */
+  cm::optional<std::string> GetPlatformToolsetFortran() const override
+  {
+    return this->GeneratorToolsetFortran;
+  }
+
   /** Return whether we need to use No/Debug instead of false/true
       for GenerateDebugInformation.  */
   bool GetPlatformToolsetNeedsDebugEnum() const
@@ -221,6 +227,7 @@
   std::string GeneratorToolsetCudaCustomDir;
   std::string GeneratorToolsetCudaNvccSubdir;
   std::string GeneratorToolsetCudaVSIntegrationSubdir;
+  cm::optional<std::string> GeneratorToolsetFortran;
   std::string DefaultPlatformToolset;
   std::string DefaultPlatformToolsetHostArchitecture;
   std::string DefaultAndroidToolset;
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 1abdd0b..9739a09 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -94,8 +94,8 @@
     cmSystemTools::ReadRegistryValue(vskey, intelVersion,
                                      cmSystemTools::KeyWOW64_32);
     unsigned int intelVersionNumber = ~0u;
-    sscanf(intelVersion.c_str(), "%u", &intelVersionNumber);
-    if (intelVersionNumber >= 11) {
+    if (sscanf(intelVersion.c_str(), "%u", &intelVersionNumber) != 1 ||
+        intelVersionNumber >= 11) {
       // Default to latest known project file version.
       intelVersion = "11.0";
     } else if (intelVersionNumber == 10) {
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index 6f6109e..2056b2f 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
+
 #include <cm3p/json/value.h>
 
 #include "cmGlobalVisualStudioGenerator.h"
@@ -102,6 +104,10 @@
   }
 
   const std::string& GetIntelProjectVersion();
+  virtual cm::optional<std::string> GetPlatformToolsetFortran() const
+  {
+    return cm::nullopt;
+  }
 
   bool FindMakeProgram(cmMakefile* mf) override;
 
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 72dba42..98d77c6 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -2480,6 +2480,25 @@
       buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS",
                                   swiftDefs.CreateList());
     }
+
+    if (cm::optional<cmSwiftCompileMode> swiftCompileMode =
+          this->CurrentLocalGenerator->GetSwiftCompileMode(gtgt, configName)) {
+      switch (*swiftCompileMode) {
+        case cmSwiftCompileMode::Wholemodule:
+          buildSettings->AddAttribute("SWIFT_COMPILATION_MODE",
+                                      this->CreateString("wholemodule"));
+          break;
+        case cmSwiftCompileMode::Incremental:
+        case cmSwiftCompileMode::Singlefile:
+          break;
+        case cmSwiftCompileMode::Unknown:
+          this->CurrentLocalGenerator->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat("Unknown Swift_COMPILATION_MODE on target '",
+                     gtgt->GetName(), "'"));
+          break;
+      }
+    }
   }
 
   std::string extraLinkOptionsVar;
@@ -2501,6 +2520,9 @@
     this->CurrentLocalGenerator->GetStaticLibraryFlags(
       extraLinkOptions, configName, llang, gtgt);
   } else {
+    this->CurrentLocalGenerator->AppendLinkerTypeFlags(extraLinkOptions, gtgt,
+                                                       configName, llang);
+
     cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
     if (targetLinkFlags) {
       this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
@@ -3045,7 +3067,8 @@
   }
 
   // Precompile Headers
-  std::string pchHeader = gtgt->GetPchHeader(configName, llang);
+  std::string pchHeader =
+    gtgt->GetPchHeader(configName, langForPreprocessorDefinitions);
   if (!pchHeader.empty()) {
     buildSettings->AddAttribute("GCC_PREFIX_HEADER",
                                 this->CreateString(pchHeader));
@@ -4278,6 +4301,15 @@
                            dstSubfolderSpec, NoActionOnCopyByDefault);
 }
 
+void cmGlobalXCodeGenerator::AddEmbeddedXPCServices(cmXCodeObject* target)
+{
+  static const auto dstSubfolderSpec = "16";
+
+  this->AddEmbeddedObjects(
+    target, "Embed XPC Services", "XCODE_EMBED_XPC_SERVICES", dstSubfolderSpec,
+    NoActionOnCopyByDefault, "$(CONTENTS_FOLDER_PATH)/XPCServices");
+}
+
 bool cmGlobalXCodeGenerator::CreateGroups(
   std::vector<cmLocalGenerator*>& generators)
 {
@@ -4607,6 +4639,10 @@
     buildSettings->AddAttribute("CODE_SIGNING_ALLOWED",
                                 this->CreateString("NO"));
   }
+
+  // This code supports the OLD behavior of CMP0157. We should be able to
+  // remove computing the debug configuration set once the old behavior is
+  // removed.
   auto debugConfigs = this->GetCMakeInstance()->GetDebugConfigs();
   std::set<std::string> debugConfigSet(debugConfigs.begin(),
                                        debugConfigs.end());
@@ -4616,9 +4652,16 @@
 
     cmXCodeObject* buildSettingsForCfg = this->CreateFlatClone(buildSettings);
 
-    if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) {
-      buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE",
-                                        this->CreateString("wholemodule"));
+    // Supports the OLD behavior of CMP0157. CMP0157 OLD behavior globally set
+    // wholemodule compilation for all non-debug configurations, for all
+    // targets.
+    if (this->CurrentMakefile
+          ->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT")
+          .IsEmpty()) {
+      if (debugConfigSet.count(cmSystemTools::UpperCase(config.first)) == 0) {
+        buildSettingsForCfg->AddAttribute("SWIFT_COMPILATION_MODE",
+                                          this->CreateString("wholemodule"));
+      }
     }
 
     // Put this last so it can override existing settings
@@ -4680,6 +4723,7 @@
     this->AddEmbeddedAppExtensions(t);
     this->AddEmbeddedExtensionKitExtensions(t);
     this->AddEmbeddedResources(t);
+    this->AddEmbeddedXPCServices(t);
     // Inherit project-wide values for any target-specific search paths.
     this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS");
     this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS");
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index da0a4ea..12a5cad 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -225,6 +225,7 @@
   void AddEmbeddedAppExtensions(cmXCodeObject* target);
   void AddEmbeddedExtensionKitExtensions(cmXCodeObject* target);
   void AddEmbeddedResources(cmXCodeObject* target);
+  void AddEmbeddedXPCServices(cmXCodeObject* target);
   void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
                                            cmXCodeObject* buildSettings,
                                            const std::string& configName);
diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx
index c2a09c1..0b80aaa 100644
--- a/Source/cmIfCommand.cxx
+++ b/Source/cmIfCommand.cxx
@@ -161,6 +161,10 @@
         inStatus.SetContinueInvoked();
         return true;
       }
+      if (status.HasExitCode()) {
+        inStatus.SetExitCode(status.GetExitCode());
+        return true;
+      }
     }
   }
   return true;
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 0fc4011..e2755da 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -2030,7 +2030,7 @@
     cm::make_unique<cmInstallExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true,
+      ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, false,
       helper.Makefile->GetBacktrace()));
 
   return true;
@@ -2054,12 +2054,14 @@
   bool exportOld = false;
   std::string filename;
   std::string cxx_modules_directory;
+  bool exportPackageDependencies = false;
 
   ica.Bind("EXPORT"_s, exp);
   ica.Bind("NAMESPACE"_s, name_space);
   ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
   ica.Bind("FILE"_s, filename);
   ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
+  ica.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s, exportPackageDependencies);
 
   std::vector<std::string> unknownArgs;
   ica.Parse(args, &unknownArgs);
@@ -2147,7 +2149,8 @@
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
       ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory,
-      exportOld, false, helper.Makefile->GetBacktrace()));
+      exportOld, false, exportPackageDependencies,
+      helper.Makefile->GetBacktrace()));
 
   return true;
 }
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index ead9c0e..2f3da3e 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -27,7 +27,7 @@
   std::string const& component, MessageLevel message, bool exclude_from_all,
   std::string filename, std::string name_space,
   std::string cxx_modules_directory, bool exportOld, bool android,
-  cmListFileBacktrace backtrace)
+  bool exportPackageDependencies, cmListFileBacktrace backtrace)
   : cmInstallGenerator(destination, configurations, component, message,
                        exclude_from_all, false, std::move(backtrace))
   , ExportSet(exportSet)
@@ -36,6 +36,7 @@
   , Namespace(std::move(name_space))
   , CxxModulesDirectory(std::move(cxx_modules_directory))
   , ExportOld(exportOld)
+  , ExportPackageDependencies(exportPackageDependencies)
 {
   if (android) {
 #ifndef CMAKE_BOOTSTRAP
@@ -119,6 +120,7 @@
       this->EFGen->AddConfiguration(c);
     }
   }
+  this->EFGen->SetExportPackageDependencies(this->ExportPackageDependencies);
   this->EFGen->GenerateImportFile();
 
   // Perform the main install script generation.
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h
index f2d4a05..5f92851 100644
--- a/Source/cmInstallExportGenerator.h
+++ b/Source/cmInstallExportGenerator.h
@@ -29,7 +29,8 @@
                            bool exclude_from_all, std::string filename,
                            std::string name_space,
                            std::string cxx_modules_directory, bool exportOld,
-                           bool android, cmListFileBacktrace backtrace);
+                           bool android, bool exportPackageDependencies,
+                           cmListFileBacktrace backtrace);
   cmInstallExportGenerator(const cmInstallExportGenerator&) = delete;
   ~cmInstallExportGenerator() override;
 
@@ -70,6 +71,7 @@
   std::string const Namespace;
   std::string const CxxModulesDirectory;
   bool const ExportOld;
+  bool const ExportPackageDependencies;
   cmLocalGenerator* LocalGenerator = nullptr;
 
   std::string TempDir;
diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx
index 1abdaa6..5c44fba 100644
--- a/Source/cmJSONState.cxx
+++ b/Source/cmJSONState.cxx
@@ -45,7 +45,7 @@
 
 void cmJSONState::AddError(std::string const& errMsg)
 {
-  this->errors.push_back(Error(errMsg));
+  this->errors.emplace_back(errMsg);
 }
 
 void cmJSONState::AddErrorAtValue(std::string const& errMsg,
@@ -65,7 +65,7 @@
     this->AddError(errMsg);
   } else {
     Location loc = LocateInDocument(offset);
-    this->errors.push_back(Error(loc, errMsg));
+    this->errors.emplace_back(loc, errMsg);
   }
 }
 
@@ -118,7 +118,7 @@
 
 void cmJSONState::push_stack(std::string const& k, const Json::Value* value)
 {
-  this->parseStack.push_back(JsonPair(k, value));
+  this->parseStack.emplace_back(k, value);
 }
 
 void cmJSONState::pop_stack()
diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx
index 2dc40ff..3654176 100644
--- a/Source/cmLinkItem.cxx
+++ b/Source/cmLinkItem.cxx
@@ -4,20 +4,30 @@
 
 #include <utility> // IWYU pragma: keep
 
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmGeneratorTarget.h"
+#include "cmStringAlgorithms.h"
+
+const std::string cmLinkItem::DEFAULT = "DEFAULT";
 
 cmLinkItem::cmLinkItem() = default;
 
-cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt)
+cmLinkItem::cmLinkItem(std::string n, bool c, cmListFileBacktrace bt,
+                       std::string feature)
   : String(std::move(n))
+  , Feature(std::move(feature))
   , Cross(c)
   , Backtrace(std::move(bt))
 {
 }
 
 cmLinkItem::cmLinkItem(cmGeneratorTarget const* t, bool c,
-                       cmListFileBacktrace bt)
+                       cmListFileBacktrace bt, std::string feature)
   : Target(t)
+  , Feature(std::move(feature))
   , Cross(c)
   , Backtrace(std::move(bt))
 {
@@ -73,3 +83,19 @@
   , CheckCMP0027(checkCMP0027)
 {
 }
+
+namespace {
+const cm::string_view LL_BEGIN = "<LINK_LIBRARY:"_s;
+const cm::string_view LL_END = "</LINK_LIBRARY:"_s;
+}
+cm::optional<std::string> ParseLinkFeature(std::string const& item)
+{
+  if (cmHasPrefix(item, LL_BEGIN) && cmHasSuffix(item, '>')) {
+    return item.substr(LL_BEGIN.length(),
+                       item.find('>', LL_BEGIN.length()) - LL_BEGIN.length());
+  }
+  if (cmHasPrefix(item, LL_END) && cmHasSuffix(item, '>')) {
+    return cmLinkItem::DEFAULT;
+  }
+  return cm::nullopt;
+}
diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h
index e4444d3..1946c9b 100644
--- a/Source/cmLinkItem.h
+++ b/Source/cmLinkItem.h
@@ -10,6 +10,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include <cm/optional>
 #include <cmext/algorithm>
 
 #include "cmListFileCache.h"
@@ -25,14 +26,20 @@
   std::string String;
 
 public:
+  // default feature: link library without decoration
+  static const std::string DEFAULT;
+
   cmLinkItem();
-  cmLinkItem(std::string s, bool c, cmListFileBacktrace bt);
-  cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt);
+  cmLinkItem(std::string s, bool c, cmListFileBacktrace bt,
+             std::string feature = DEFAULT);
+  cmLinkItem(cmGeneratorTarget const* t, bool c, cmListFileBacktrace bt,
+             std::string feature = DEFAULT);
   std::string const& AsStr() const;
   cmGeneratorTarget const* Target = nullptr;
   // The source file representing the external object (used when linking
   // `$<TARGET_OBJECTS>`)
   cmSourceFile const* ObjectSource = nullptr;
+  std::string Feature;
   bool Cross = false;
   cmListFileBacktrace Backtrace;
   friend bool operator<(cmLinkItem const& l, cmLinkItem const& r);
@@ -160,3 +167,6 @@
   // The current configuration is not a debug configuration.
   return OPTIMIZED_LibraryType;
 }
+
+// Parse LINK_LIBRARY genex markers.
+cm::optional<std::string> ParseLinkFeature(std::string const& item);
diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx
index 0ad846b..a63b794 100644
--- a/Source/cmLinkItemGraphVisitor.cxx
+++ b/Source/cmLinkItemGraphVisitor.cxx
@@ -82,7 +82,7 @@
 bool cmLinkItemGraphVisitor::LinkVisited(cmLinkItem const& depender,
                                          cmLinkItem const& dependee)
 {
-  auto const link = std::make_pair<>(depender.AsStr(), dependee.AsStr());
+  auto const link = std::make_pair(depender.AsStr(), dependee.AsStr());
 
   bool const linkVisited =
     this->VisitedLinks.find(link) != this->VisitedLinks.cend();
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 86c55f5..0e539fe 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -82,7 +82,6 @@
                                 "CMAKE_CURRENT_SOURCE_DIR",
                                 "CMAKE_CURRENT_BINARY_DIR",
                                 "CMAKE_RANLIB",
-                                "CMAKE_LINKER",
                                 "CMAKE_MT",
                                 "CMAKE_TAPI",
                                 "CMAKE_CUDA_HOST_COMPILER",
@@ -1367,7 +1366,7 @@
 {
   const std::string configUpper = cmSystemTools::UpperCase(config);
   std::vector<BT<std::string>> flags;
-  if (linkLanguage != "Swift") {
+  if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) {
     std::string staticLibFlags;
     this->AppendFlags(
       staticLibFlags,
@@ -1604,6 +1603,7 @@
   }
 
   std::string extraLinkFlags;
+  this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage);
   this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
                                              linkLanguage);
   this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
@@ -1651,6 +1651,39 @@
   if (lang == "Fortran") {
     this->AppendFlags(compileFlags,
                       this->GetTargetFortranFlags(target, config));
+  } else if (lang == "Swift") {
+    // Only set the compile mode if CMP0157 is set
+    if (cm::optional<cmSwiftCompileMode> swiftCompileMode =
+          this->GetSwiftCompileMode(target, config)) {
+      std::string swiftCompileModeFlag;
+      switch (*swiftCompileMode) {
+        case cmSwiftCompileMode::Incremental: {
+          swiftCompileModeFlag = "-incremental";
+          if (cmValue flag =
+                mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) {
+            swiftCompileModeFlag = *flag;
+          }
+          break;
+        }
+        case cmSwiftCompileMode::Wholemodule: {
+          swiftCompileModeFlag = "-wmo";
+          if (cmValue flag =
+                mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) {
+            swiftCompileModeFlag = *flag;
+          }
+          break;
+        }
+        case cmSwiftCompileMode::Singlefile:
+          break;
+        case cmSwiftCompileMode::Unknown: {
+          this->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat("Unknown Swift_COMPILATION_MODE on target '",
+                     target->GetName(), "'"));
+        }
+      }
+      this->AppendFlags(compileFlags, swiftCompileModeFlag);
+    }
   }
 
   this->AddCMP0018Flags(compileFlags, target, lang, config);
@@ -2994,6 +3027,40 @@
   return msvcDebugInformationFormat;
 }
 
+cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode(
+  cmGeneratorTarget const* target, std::string const& config)
+{
+  cmMakefile const* mf = this->GetMakefile();
+  cmValue const swiftCompileModeDefault =
+    mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT");
+  if (!cmNonempty(swiftCompileModeDefault)) {
+    return {};
+  }
+  cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE");
+  if (!swiftCompileMode) {
+    swiftCompileMode = swiftCompileModeDefault;
+  }
+
+  std::string const expandedCompileMode =
+    cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target);
+  if (expandedCompileMode == "wholemodule") {
+    return cmSwiftCompileMode::Wholemodule;
+  }
+  if (expandedCompileMode == "singlefile") {
+    return cmSwiftCompileMode::Singlefile;
+  }
+  if (expandedCompileMode == "incremental") {
+    return cmSwiftCompileMode::Incremental;
+  }
+  return cmSwiftCompileMode::Unknown;
+}
+
+bool cmLocalGenerator::IsSplitSwiftBuild() const
+{
+  return cmNonempty(this->GetMakefile()->GetDefinition(
+    "CMAKE_Swift_COMPILATION_MODE_DEFAULT"));
+}
+
 namespace {
 
 inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf,
@@ -3102,8 +3169,17 @@
 
     chunk = std::min(itemsLeft, batchSize);
 
-    std::string filename = cmStrCat(filename_base, "unity_", batch,
-                                    (lang == "C") ? "_c.c" : "_cxx.cxx");
+    std::string extension;
+    if (lang == "C") {
+      extension = "_c.c";
+    } else if (lang == "CXX") {
+      extension = "_cxx.cxx";
+    } else if (lang == "OBJC") {
+      extension = "_m.m";
+    } else if (lang == "OBJCXX") {
+      extension = "_mm.mm";
+    }
+    std::string filename = cmStrCat(filename_base, "unity_", batch, extension);
     auto const begin = filtered_sources.begin() + batch * batchSize;
     auto const end = begin + chunk;
     unity_files.emplace_back(this->WriteUnitySource(
@@ -3204,7 +3280,7 @@
   cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
   cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE");
 
-  for (std::string lang : { "C", "CXX" }) {
+  for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX" }) {
     std::vector<UnityBatchedSource> filtered_sources;
     std::copy_if(unitySources.begin(), unitySources.end(),
                  std::back_inserter(filtered_sources),
@@ -3250,6 +3326,49 @@
   }
 }
 
+void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags,
+                                             cmGeneratorTarget* target,
+                                             const std::string& config,
+                                             const std::string& linkLanguage)
+{
+  switch (target->GetType()) {
+    case cmStateEnums::EXECUTABLE:
+    case cmStateEnums::SHARED_LIBRARY:
+    case cmStateEnums::MODULE_LIBRARY:
+      break;
+    default:
+      return;
+  }
+
+  auto usingLinker =
+    cmStrCat("CMAKE_", linkLanguage, "_USING_",
+             target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_");
+
+  auto format = this->Makefile->GetDefinition(cmStrCat(usingLinker, "MODE"));
+  if (format && format != "FLAG"_s) {
+    return;
+  }
+
+  auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config);
+  if (linkerType.empty()) {
+    linkerType = "DEFAULT";
+  }
+  usingLinker = cmStrCat(usingLinker, linkerType);
+  auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker);
+  if (linkerTypeFlags) {
+    if (!linkerTypeFlags.IsEmpty()) {
+      auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags);
+      target->ResolveLinkerWrapper(linkerFlags, linkLanguage);
+      this->AppendFlags(flags, linkerFlags);
+    }
+  } else if (linkerType != "DEFAULT"_s) {
+    this->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("LINKER_TYPE '", linkerType,
+                                "' is unknown. Did you forgot to define '",
+                                usingLinker, "' variable?"));
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index a920cfe..3ec349b 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -67,6 +67,15 @@
   Link
 };
 
+/** What compilation mode the swift files are in */
+enum class cmSwiftCompileMode
+{
+  Wholemodule,
+  Incremental,
+  Singlefile,
+  Unknown,
+};
+
 /** Target and source file which have a specific output.  */
 struct cmSourcesWithOutput
 {
@@ -177,6 +186,9 @@
   void AddPchDependencies(cmGeneratorTarget* target);
   void AddUnityBuild(cmGeneratorTarget* target);
   virtual void AddXCConfigSources(cmGeneratorTarget* /* target */) {}
+  void AppendLinkerTypeFlags(std::string& flags, cmGeneratorTarget* target,
+                             const std::string& config,
+                             const std::string& linkLanguage);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);
@@ -546,6 +558,14 @@
                               const std::string& prop,
                               const std::string& config);
 
+  // Return Swift_COMPILATION_MODE value if CMP0157 is NEW.
+  cm::optional<cmSwiftCompileMode> GetSwiftCompileMode(
+    cmGeneratorTarget const* target, std::string const& config);
+
+  // Can we build Swift with a separate object build and link step
+  // (If CMP0157 is NEW, we can do a split build)
+  bool IsSplitSwiftBuild() const;
+
 protected:
   // The default implementation converts to a Windows shortpath to
   // help older toolchains handle spaces and such.  A generator may
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 7b02c56..d315f0f 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -32,6 +32,7 @@
 #include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmSourceFile.h"
@@ -794,6 +795,9 @@
       target->GetType() == cmStateEnums::OBJECT_LIBRARY
       ? ".lib"
       : cmSystemTools::GetFilenameLastExtension(targetNameFull);
+    if (cm::optional<std::string> fortran = gg->GetPlatformToolsetFortran()) {
+      fout << "\t\t\tUseCompiler=\"" << *fortran << "Compiler\"\n";
+    }
     /* clang-format off */
     fout <<
       "\t\t\tTargetName=\"" << this->EscapeForXML(targetName) << "\"\n"
@@ -1085,6 +1089,16 @@
       cmComputeLinkInformation& cli = *pcli;
       std::string linkLanguage = cli.GetLinkLanguage();
 
+      if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+        // Visual Studio 10 or upper is required for this feature
+        this->GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("'LINKER_TYPE' property, specified on target '",
+                   target->GetName(),
+                   "', is not supported by this generator."),
+          target->GetBacktrace());
+      }
+
       // Compute the variable name to lookup standard libraries for this
       // language.
       std::string standardLibsVar =
@@ -1161,6 +1175,16 @@
       cmComputeLinkInformation& cli = *pcli;
       std::string linkLanguage = cli.GetLinkLanguage();
 
+      if (!target->GetLinkerTypeProperty(linkLanguage, configName).empty()) {
+        // Visual Studio 10 or upper is required for this feature
+        this->GetCMakeInstance()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("'LINKER_TYPE' property, specified on target '",
+                   target->GetName(),
+                   "', is not supported by this generator."),
+          target->GetBacktrace());
+      }
+
       bool isWin32Executable = target->IsWin32Executable(configName);
 
       // Compute the variable name to lookup standard libraries for this
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 3d7cd8b..b57c3dd 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -135,6 +135,10 @@
       inStatus.SetBreakInvoked();
       return true;
     }
+    if (status.HasExitCode()) {
+      inStatus.SetExitCode(status.GetExitCode());
+      return true;
+    }
   }
   return true;
 }
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 93fb8b4..cfaac11 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -302,6 +302,11 @@
   return this->Backtrace;
 }
 
+cmFindPackageStack cmMakefile::GetFindPackageStack() const
+{
+  return this->FindPackageStack;
+}
+
 void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff,
                                    cmListFileBacktrace const& bt,
                                    CommandMissingFromStack missing) const
@@ -524,6 +529,12 @@
           cmSystemTools::SetFatalErrorOccurred();
         }
       }
+      if (this->GetCMakeInstance()->HasScriptModeExitCode() &&
+          this->GetCMakeInstance()->GetWorkingMode() == cmake::SCRIPT_MODE) {
+        // pass-through the exit code from inner cmake_language(EXIT) ,
+        // possibly from include() or similar command...
+        status.SetExitCode(this->GetCMakeInstance()->GetScriptModeExitCode());
+      }
     }
   } else {
     if (!cmSystemTools::GetFatalErrorOccurred()) {
@@ -893,6 +904,11 @@
     if (cmSystemTools::GetFatalErrorOccurred()) {
       break;
     }
+    if (status.HasExitCode()) {
+      // cmake_language EXIT was requested, early break.
+      this->GetCMakeInstance()->SetScriptModeExitCode(status.GetExitCode());
+      break;
+    }
     if (status.GetReturnInvoked()) {
       this->RaiseScope(status.GetReturnVariables());
       // Exit early due to return command.
@@ -1128,9 +1144,15 @@
   const std::string& target, cmObjectLibraryCommands objLibCommands,
   const cmListFileBacktrace& lfbt) const
 {
-  // Find the target to which to add the custom command.
-  auto ti = this->Targets.find(target);
+  auto realTarget = target;
 
+  auto ai = this->AliasTargets.find(target);
+  if (ai != this->AliasTargets.end()) {
+    realTarget = ai->second;
+  }
+
+  // Find the target to which to add the custom command.
+  auto ti = this->Targets.find(realTarget);
   if (ti == this->Targets.end()) {
     MessageType messageType = MessageType::AUTHOR_WARNING;
     bool issueMessage = false;
@@ -1215,8 +1237,8 @@
   // Dispatch command creation to allow generator expressions in outputs.
   this->AddGeneratorAction(
     std::move(cc),
-    [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
-        std::unique_ptr<cmCustomCommand> tcc) {
+    [this, t, type](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
+                    std::unique_ptr<cmCustomCommand> tcc) {
       BacktraceGuard guard(this->Backtrace, lfbt);
       tcc->SetBacktrace(lfbt);
       detail::AddCustomCommandToTarget(lg, cmCommandOrigin::Project, t, type,
@@ -1254,8 +1276,9 @@
   // Dispatch command creation to allow generator expressions in outputs.
   this->AddGeneratorAction(
     std::move(cc),
-    [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
-        std::unique_ptr<cmCustomCommand> tcc) {
+    [this, replace, callback](cmLocalGenerator& lg,
+                              const cmListFileBacktrace& lfbt,
+                              std::unique_ptr<cmCustomCommand> tcc) {
       BacktraceGuard guard(this->Backtrace, lfbt);
       tcc->SetBacktrace(lfbt);
       cmSourceFile* sf = detail::AddCustomCommandToOutput(
@@ -1341,7 +1364,8 @@
   if (this->ValidateCustomCommand(commandLines)) {
     // Dispatch command creation to allow generator expressions in outputs.
     this->AddGeneratorAction(
-      [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) {
+      [this, output, depends, implicit_depends,
+       commandLines](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt) {
         BacktraceGuard guard(this->Backtrace, lfbt);
         detail::AppendCustomCommandToOutput(lg, lfbt, output, depends,
                                             implicit_depends, commandLines);
@@ -1372,8 +1396,8 @@
   // Dispatch command creation to allow generator expressions in outputs.
   this->AddGeneratorAction(
     std::move(cc),
-    [=](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
-        std::unique_ptr<cmCustomCommand> tcc) {
+    [this, target](cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
+                   std::unique_ptr<cmCustomCommand> tcc) {
       BacktraceGuard guard(this->Backtrace, lfbt);
       tcc->SetBacktrace(lfbt);
       detail::AddUtilityCommand(lg, cmCommandOrigin::Project, target,
@@ -1420,8 +1444,8 @@
   for (std::string::size_type lpos = dflags.find(flag, 0);
        lpos != std::string::npos; lpos = dflags.find(flag, lpos)) {
     std::string::size_type rpos = lpos + len;
-    if ((lpos <= 0 || isspace(dflags[lpos - 1])) &&
-        (rpos >= dflags.size() || isspace(dflags[rpos]))) {
+    if ((lpos <= 0 || cmIsSpace(dflags[lpos - 1])) &&
+        (rpos >= dflags.size() || cmIsSpace(dflags[rpos]))) {
       dflags.erase(lpos, len);
     } else {
       ++lpos;
@@ -4633,12 +4657,14 @@
   }
 
   // Deprecate old policies.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0120 &&
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0128 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
           id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083 ||
-          id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104)) &&
+          id == cmPolicies::CMP0091 || id == cmPolicies::CMP0104 ||
+          id == cmPolicies::CMP0123 || id == cmPolicies::CMP0126 ||
+          id == cmPolicies::CMP0128)) &&
       (!this->IsSet("CMAKE_WARN_DEPRECATED") ||
        this->IsOn("CMAKE_WARN_DEPRECATED"))) {
     this->IssueMessage(MessageType::DEPRECATION_WARNING,
@@ -4771,6 +4797,36 @@
   this->Makefile->PopMacroScope(this->ReportError);
 }
 
+cmMakefile::FindPackageStackRAII::FindPackageStackRAII(cmMakefile* mf,
+                                                       std::string const& name)
+  : Makefile(mf)
+{
+  this->Makefile->FindPackageStack =
+    this->Makefile->FindPackageStack.Push(cmFindPackageCall{
+      name,
+      this->Makefile->FindPackageStackNextIndex,
+    });
+  this->Makefile->FindPackageStackNextIndex++;
+}
+
+cmMakefile::FindPackageStackRAII::~FindPackageStackRAII()
+{
+  this->Makefile->FindPackageStackNextIndex =
+    this->Makefile->FindPackageStack.Top().Index + 1;
+  this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop();
+
+  if (!this->Makefile->FindPackageStack.Empty()) {
+    auto top = this->Makefile->FindPackageStack.Top();
+    this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop();
+
+    top.Index = this->Makefile->FindPackageStackNextIndex;
+    this->Makefile->FindPackageStackNextIndex++;
+
+    this->Makefile->FindPackageStack =
+      this->Makefile->FindPackageStack.Push(top);
+  }
+}
+
 cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf,
                                                std::string const& pkg)
   : Makefile(mf)
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 24daa72..e5edbae 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -25,6 +25,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
+#include "cmFindPackageStack.h"
 #include "cmFunctionBlocker.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h" // IWYU pragma: keep
@@ -660,6 +661,11 @@
   cmListFileBacktrace GetBacktrace() const;
 
   /**
+   * Get the current stack of find_package calls.
+   */
+  cmFindPackageStack GetFindPackageStack() const;
+
+  /**
    * Get the vector of  files created by this makefile
    */
   const std::vector<std::string>& GetOutputFiles() const
@@ -1020,6 +1026,15 @@
   // searches
   std::deque<std::vector<std::string>> FindPackageRootPathStack;
 
+  class FindPackageStackRAII
+  {
+    cmMakefile* Makefile;
+
+  public:
+    FindPackageStackRAII(cmMakefile* mf, std::string const& pkg);
+    ~FindPackageStackRAII();
+  };
+
   class DebugFindPkgRAII
   {
     cmMakefile* Makefile;
@@ -1210,6 +1225,9 @@
   std::vector<BT<GeneratorAction>> GeneratorActions;
   bool GeneratorActionsInvoked = false;
 
+  cmFindPackageStack FindPackageStack;
+  unsigned int FindPackageStackNextIndex = 0;
+
   bool DebugFindPkg = false;
 
   bool CheckSystemVars;
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 4a2b9e8..96a0d5c 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -344,6 +344,8 @@
     return;
   }
 
+  auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
   // Build list of dependencies.
   std::vector<std::string> depends;
   this->AppendLinkDepends(depends, linkLanguage);
@@ -533,6 +535,7 @@
     vars.CMTargetType =
       cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
     vars.Language = linkLanguage.c_str();
+    vars.Linker = linker.c_str();
     vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index fc3caa1..bc48a3b 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -218,6 +218,9 @@
     extraFlags, this->GeneratorTarget, linkLineComputer.get(),
     this->GetConfigName());
 
+  this->UseLWYU = this->LocalGenerator->AppendLWYUFlags(
+    extraFlags, this->GeneratorTarget, linkLanguage);
+
   this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
 }
 
@@ -441,6 +444,8 @@
     return;
   }
 
+  auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
+
   // Build list of dependencies.
   std::vector<std::string> depends;
   this->AppendLinkDepends(depends, linkLanguage);
@@ -766,6 +771,7 @@
     vars.CMTargetType =
       cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
     vars.Language = linkLanguage.c_str();
+    vars.Linker = linker.c_str();
     vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 90afb1b..ff509ce 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -139,7 +139,7 @@
   this->LocalGenerator->AppendFlags(
     flags, this->GeneratorTarget->GetSafeProperty("LINK_FLAGS"));
 
-  std::string linkFlagsConfig =
+  std::string const linkFlagsConfig =
     cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(this->GetConfigName()));
   this->LocalGenerator->AppendFlags(
     flags, this->GeneratorTarget->GetSafeProperty(linkFlagsConfig));
@@ -153,6 +153,8 @@
   this->LocalGenerator->AppendCompileOptions(flags, opts);
   this->LocalGenerator->SetLinkScriptShell(false);
 
+  this->LocalGenerator->AppendLinkerTypeFlags(
+    flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
   this->LocalGenerator->AppendPositionIndependentLinkerFlags(
     flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
   this->LocalGenerator->AppendDependencyInfoLinkerFlags(
@@ -234,7 +236,7 @@
   }
 
   // Look for ISPC extra object files generated by this target
-  auto ispcAdditionalObjs =
+  auto const ispcAdditionalObjs =
     this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
   for (std::string const& ispcObj : ispcAdditionalObjs) {
     this->CleanFiles.insert(
@@ -242,7 +244,7 @@
   }
 
   // add custom commands to the clean rules?
-  bool clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM"));
+  bool const clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM"));
 
   // First generate the object rule files.  Save a list of all object
   // files for this target.
@@ -1797,7 +1799,6 @@
   *this->BuildFileStream << "# Object files for target "
                          << this->GeneratorTarget->GetName() << "\n"
                          << variableName << " =";
-  std::string object;
   const auto& lineContinue = this->GlobalGenerator->LineContinueDirective;
 
   cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
@@ -1825,7 +1826,6 @@
     << variableNameExternal << " =";
   /* clang-format on */
   for (std::string const& obj : this->ExternalObjects) {
-    object = this->LocalGenerator->MaybeRelativeToCurBinDir(obj);
     *this->BuildFileStream << " " << lineContinue;
     *this->BuildFileStream
       << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
@@ -1905,7 +1905,7 @@
   for (std::string const& obj : this->ExternalObjects) {
     helper.Feed(obj);
   }
-  auto ispcAdditionalObjs =
+  auto const ispcAdditionalObjs =
     this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
   for (std::string const& obj : ispcAdditionalObjs) {
     helper.Feed(obj);
@@ -1920,13 +1920,12 @@
   std::string dir =
     this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
   std::string buildTargetRuleName =
-    cmStrCat(dir, relink ? "/preinstall" : "/build");
+    cmStrCat(std::move(dir), relink ? "/preinstall" : "/build");
   buildTargetRuleName =
     this->LocalGenerator->MaybeRelativeToTopBinDir(buildTargetRuleName);
 
   // Build the list of target outputs to drive.
-  std::vector<std::string> depends;
-  depends.push_back(main_output);
+  std::vector<std::string> depends{ main_output };
 
   const char* comment = nullptr;
   if (relink) {
@@ -1969,7 +1968,7 @@
   }
 
   // Loop over all library dependencies.
-  if (cmComputeLinkInformation* cli =
+  if (cmComputeLinkInformation const* cli =
         this->GeneratorTarget->GetLinkInformation(cfg)) {
     cm::append(depends, cli->GetDepends());
   }
@@ -2194,16 +2193,17 @@
     std::string responseFlag = this->GetResponseFlag(responseMode);
 
     // Create this response file.
-    std::string responseFileName =
+    std::string const responseFileName =
       (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp";
-    std::string responseLang = (responseMode == Link) ? linkLanguage : "CUDA";
+    std::string const responseLang =
+      (responseMode == Link) ? linkLanguage : "CUDA";
     std::string link_rsp = this->CreateResponseFile(
       responseFileName, linkLibs, makefile_depends, responseLang);
 
     // Reference the response file.
-    linkLibs = cmStrCat(responseFlag,
+    linkLibs = cmStrCat(std::move(responseFlag),
                         this->LocalGenerator->ConvertToOutputFormat(
-                          link_rsp, cmOutputConverter::SHELL));
+                          std::move(link_rsp), cmOutputConverter::SHELL));
   }
 }
 
@@ -2227,7 +2227,7 @@
                               responseFileLimit);
 
     // Lookup the response file reference flag.
-    std::string responseFlag = this->GetResponseFlag(responseMode);
+    std::string const responseFlag = this->GetResponseFlag(responseMode);
 
     // Write a response file for each string.
     const char* sep = "";
@@ -2267,15 +2267,15 @@
                                                 const std::string& lang,
                                                 const std::string& /*config*/)
 {
-  std::string responseVar =
+  std::string const responseVar =
     cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES");
-  bool useResponseFile = this->Makefile->IsOn(responseVar);
+  bool const useResponseFile = this->Makefile->IsOn(responseVar);
 
   std::vector<std::string> includes;
   this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
                                               lang, this->GetConfigName());
 
-  std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
+  std::string const includeFlags = this->LocalGenerator->GetIncludeFlags(
     includes, this->GeneratorTarget, lang, this->GetConfigName(),
     useResponseFile);
   if (includeFlags.empty()) {
@@ -2290,8 +2290,8 @@
     if (responseFlag.empty()) {
       responseFlag = "@";
     }
-    std::string name = cmStrCat("includes_", lang, ".rsp");
-    std::string arg = std::move(responseFlag) +
+    std::string const name = cmStrCat("includes_", lang, ".rsp");
+    std::string const arg = std::move(responseFlag) +
       this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang],
                                lang);
     this->LocalGenerator->AppendFlags(flags, arg);
@@ -2320,7 +2320,7 @@
   cmd += this->LocalGenerator->ConvertToOutputFormat(
     this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file),
     cmOutputConverter::SHELL);
-  cmValue nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
+  cmValue const nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
   if (cmNonempty(nm_executable)) {
     cmd += " --nm=";
     cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
@@ -2360,7 +2360,7 @@
     responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG";
   }
 
-  if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
+  if (cmValue const p = this->Makefile->GetDefinition(responseFlagVar)) {
     responseFlag = *p;
   }
   return responseFlag;
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 48c30b6..d365ef6 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -294,6 +294,9 @@
         .c_str();
 
     vars.Language = "CUDA";
+    std::string linker =
+      this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+    vars.Linker = linker.c_str();
 
     // build response file name
     std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
@@ -400,6 +403,9 @@
   vars.Fatbinary = "$FATBIN";
   vars.RegisterFile = "$REGISTER";
   vars.LinkFlags = "$LINK_FLAGS";
+  std::string linker =
+    this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
+  vars.Linker = linker.c_str();
 
   std::string flags = this->GetFlags("CUDA", config);
   vars.Flags = flags.c_str();
@@ -441,11 +447,14 @@
     vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
     vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
 
+    std::string linker = this->GetGeneratorTarget()->GetLinkerTool(config);
+    vars.Linker = linker.c_str();
     std::string lang = this->TargetLinkLanguage(config);
     vars.Language = lang.c_str();
     vars.AIXExports = "$AIX_EXPORTS";
 
-    if (this->TargetLinkLanguage(config) == "Swift") {
+    if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+        this->TargetLinkLanguage(config) == "Swift") {
       vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
       vars.SwiftModule = "$SWIFT_MODULE";
       vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
@@ -500,7 +509,8 @@
         vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
       }
 
-      if (this->TargetLinkLanguage(config) == "Swift") {
+      if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+          this->TargetLinkLanguage(config) == "Swift") {
         vars.SwiftSources = responseFlag.c_str();
       } else {
         vars.Objects = responseFlag.c_str();
@@ -1201,7 +1211,10 @@
     globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
   }
 
-  if (this->TargetLinkLanguage(config) == "Swift") {
+  // If we can't split the Swift build model (CMP0157 is OLD or unset), fall
+  // back on the old one-step "build/link" logic.
+  if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+      this->TargetLinkLanguage(config) == "Swift") {
     vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
       cmGeneratorTarget::Names targetNames =
         this->GetGeneratorTarget()->GetLibraryNames(config);
@@ -1214,12 +1227,12 @@
       cmOutputConverter::SHELL);
 
     vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
-      std::vector<cmSourceFile const*> sources;
+      std::vector<cmSourceFile const*> sourceFiles;
       std::stringstream oss;
 
-      this->GetGeneratorTarget()->GetObjectSources(sources, config);
+      this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
       cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
-      for (const auto& source : sources) {
+      for (const auto& source : sourceFiles) {
         const std::string sourcePath = source->GetLanguage() == "Swift"
           ? this->GetCompiledSourceNinjaPath(source)
           : this->GetObjectFilePath(source, config);
@@ -1237,10 +1250,8 @@
     vars["FLAGS"] = this->GetFlags("Swift", config);
     vars["INCLUDES"] = this->GetIncludes("Swift", config);
     this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
-  }
 
-  // Compute specific libraries to link with.
-  if (this->TargetLinkLanguage(config) == "Swift") {
+    // Compute specific libraries to link with.
     std::vector<cmSourceFile const*> sources;
     gt->GetObjectSources(sources, config);
     for (const auto& source : sources) {
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 58248fc..a465b08 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
@@ -31,6 +32,7 @@
 #include "cmGlobalCommonGenerator.h"
 #include "cmGlobalNinjaGenerator.h"
 #include "cmList.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalNinjaGenerator.h"
 #include "cmMakefile.h"
@@ -47,6 +49,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmTargetDepend.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -145,7 +148,7 @@
 bool cmNinjaTargetGenerator::NeedExplicitPreprocessing(
   std::string const& lang) const
 {
-  return lang == "Fortran";
+  return lang == "Fortran" || lang == "Swift";
 }
 
 bool cmNinjaTargetGenerator::CompileWithDefines(std::string const& lang) const
@@ -408,28 +411,29 @@
   return this->ConvertToNinjaAbsPath(source->GetFullPath());
 }
 
-std::string cmNinjaTargetGenerator::GetObjectFilePath(
-  cmSourceFile const* source, const std::string& config) const
+std::string cmNinjaTargetGenerator::GetObjectFileDir(
+  const std::string& config) const
 {
   std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
   if (!path.empty()) {
     path += '/';
   }
-  std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
-  path += cmStrCat(
-    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
-    this->GetGlobalGenerator()->ConfigDirectory(config), '/', objectName);
+  path +=
+    cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+             this->GetGlobalGenerator()->ConfigDirectory(config));
   return path;
 }
 
+std::string cmNinjaTargetGenerator::GetObjectFilePath(
+  cmSourceFile const* source, const std::string& config) const
+{
+  std::string const& objectName = this->GeneratorTarget->GetObjectName(source);
+  return cmStrCat(this->GetObjectFileDir(config), '/', objectName);
+}
+
 std::string cmNinjaTargetGenerator::GetBmiFilePath(
   cmSourceFile const* source, const std::string& config) const
 {
-  std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
-  if (!path.empty()) {
-    path += '/';
-  }
-
   auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules;
   if (!importedConfigInfo.Initialized()) {
     std::string configUpper = cmSystemTools::UpperCase(config);
@@ -441,10 +445,7 @@
   std::string bmiName =
     importedConfigInfo.BmiNameForSource(source->GetFullPath());
 
-  path += cmStrCat(
-    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
-    this->GetGlobalGenerator()->ConfigDirectory(config), '/', bmiName);
-  return path;
+  return cmStrCat(this->GetObjectFileDir(config), '/', bmiName);
 }
 
 std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath(
@@ -852,6 +853,14 @@
     flags = cmStrCat(responseFlag, rule.RspFile);
     vars.Defines = "";
     vars.Includes = "";
+
+    // Swift consumes all source files in a module at once, which reaches
+    // command line length limits pretty quickly. Inject source files into the
+    // response file in this case as well.
+    if (lang == "Swift") {
+      rule.RspContent = cmStrCat(rule.RspContent, ' ', vars.Source);
+      vars.Source = "";
+    }
   }
 
   // Tell ninja dependency format so all deps can be loaded into a database
@@ -1027,6 +1036,15 @@
     }
   }
 
+  if (!this->GetGlobalGenerator()->SupportsCWDDepend()) {
+    // Ensure that the object directory exists. If there are no objects in the
+    // target (e.g., an empty `OBJECT` library), the directory is still listed
+    // as an order-only depends in the build files. Alternate `ninja`
+    // implementations may not allow this (such as `samu`). See #25526.
+    auto const objectDir = this->GetObjectFileDir(config);
+    this->EnsureDirectoryExists(objectDir);
+  }
+
   {
     cmNinjaBuild build("phony");
     build.Comment =
@@ -1101,11 +1119,15 @@
     // that "output ... of phony edge with no inputs doesn't exist" and
     // consider the phony output "dirty".
     if (orderOnlyDeps.empty()) {
-      // Any path that always exists will work here.  It would be nice to
-      // use just "." but that is not supported by Ninja < 1.7.
-      std::string tgtDir = cmStrCat(
-        this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
-        this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
+      std::string tgtDir;
+      if (this->GetGlobalGenerator()->SupportsCWDDepend()) {
+        tgtDir = ".";
+      } else {
+        // Any path that always exists will work here.
+        tgtDir = cmStrCat(
+          this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+          this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
+      }
       orderOnlyDeps.push_back(this->ConvertToNinjaPath(tgtDir));
     }
 
@@ -1141,9 +1163,19 @@
     std::vector<cmSourceFile const*> objectSources;
     this->GeneratorTarget->GetObjectSources(objectSources, config);
 
+    std::vector<cmSourceFile const*> swiftSources;
+
     for (cmSourceFile const* sf : objectSources) {
-      this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig);
+      if (this->GetLocalGenerator()->IsSplitSwiftBuild() &&
+          sf->GetLanguage() == "Swift") {
+        swiftSources.push_back(sf);
+      } else {
+        this->WriteObjectBuildStatement(sf, config, fileConfig,
+                                        firstForConfig);
+      }
     }
+    WriteSwiftObjectBuildStatement(swiftSources, config, fileConfig,
+                                   firstForConfig);
   }
 
   {
@@ -1243,9 +1275,11 @@
     if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
       return *name;
     }
-    return this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(),
-                                             '/', config, '/',
-                                             target->GetName(), ".swiftdeps"));
+    return this->GetLocalGenerator()->ConvertToOutputFormat(
+      this->ConvertToNinjaPath(cmStrCat(target->GetSupportDirectory(), '/',
+                                        config, '/', target->GetName(),
+                                        ".swiftdeps")),
+      cmOutputConverter::SHELL);
   }();
 
   std::string mapFilePath =
@@ -1263,8 +1297,10 @@
 
   // Add flag
   this->LocalGenerator->AppendFlags(flags, "-output-file-map");
-  this->LocalGenerator->AppendFlagEscape(flags,
-                                         ConvertToNinjaPath(mapFilePath));
+  this->LocalGenerator->AppendFlagEscape(
+    flags,
+    this->GetLocalGenerator()->ConvertToOutputFormat(
+      ConvertToNinjaPath(mapFilePath), cmOutputConverter::SHELL));
 }
 
 namespace {
@@ -1442,10 +1478,13 @@
     }
   }
 
+  this->SetMsvcTargetPdbVariable(vars, config);
+
   if (firstForConfig) {
     this->ExportObjectCompileCommand(
       language, sourceFilePath, objectDir, objectFileName, objectFileDir,
-      vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config, withScanning);
+      vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"],
+      vars["TARGET_COMPILE_PDB"], vars["TARGET_PDB"], config, withScanning);
   }
 
   objBuild.Outputs.push_back(objectFileName);
@@ -1636,8 +1675,6 @@
     }
   }
 
-  this->SetMsvcTargetPdbVariable(vars, config);
-
   objBuild.RspFile = cmStrCat(objectFileName, ".rsp");
 
   if (language == "ISPC") {
@@ -1788,10 +1825,13 @@
     vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile;
   }
 
+  this->SetMsvcTargetPdbVariable(vars, config);
+
   if (firstForConfig) {
     this->ExportObjectCompileCommand(
       language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"],
-      vars["DEFINES"], vars["INCLUDES"], config, WithScanning::Yes);
+      vars["DEFINES"], vars["INCLUDES"], vars["TARGET_COMPILE_PDB"],
+      vars["TARGET_PDB"], config, WithScanning::Yes);
   }
 
   bmiBuild.Outputs.push_back(bmiFileName);
@@ -1862,14 +1902,215 @@
   this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
                              vars);
 
-  this->SetMsvcTargetPdbVariable(vars, config);
-
   bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp");
 
   this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
                                          bmiBuild, commandLineLengthLimit);
 }
 
+void cmNinjaTargetGenerator::WriteSwiftObjectBuildStatement(
+  std::vector<cmSourceFile const*> const& sources, std::string const& config,
+  std::string const& fileConfig, bool firstForConfig)
+{
+  // Swift sources are compiled as a module, not individually like with C/C++.
+  // Flags, header search paths, and definitions are passed to the entire
+  // module build, but we still need to emit compile-commands for each source
+  // file in order to support CMAKE_EXPORT_COMPILE_COMMANDS.
+  // In whole-module mode, with a single thread, the Swift compiler will
+  // only emit a single object file, but if more than one thread is specified,
+  // or building in other modes, the compiler will emit multiple object files.
+  // When building a single-output, we do not provide an output-file-map (OFM),
+  // and instead pass `-o` to tell the compiler where to write the object.
+  // When building multiple outputs, we provide an OFM to tell the compiler
+  // where to put each object.
+  //
+  //
+  // Per-Target (module):
+  //  - Flags
+  //  - Definitions
+  //  - Include paths
+  //  - (single-output) output object filename
+  //  - Swiftmodule
+  //
+  //  Per-File:
+  //  - compile-command
+  //  - (multi-output) OFM data
+  //  - (multi-output) output object filename
+  //
+  //  Note: Due to the differences in the build models, we are only able to
+  //  build the object build-graph if we know what mode the target is built in.
+  //  For that, we need the "NEW" behavior for CMP0157. Otherwise, we have to
+  //  fall back on the old "linker" build. Otherwise, this should be
+  //  indistinguishable from the old behavior.
+
+  if (sources.empty()) {
+    return;
+  }
+
+  cmSwiftCompileMode compileMode;
+  if (cm::optional<cmSwiftCompileMode> optionalCompileMode =
+        this->LocalGenerator->GetSwiftCompileMode(this->GeneratorTarget,
+                                                  config)) {
+    compileMode = *optionalCompileMode;
+  } else {
+    // CMP0157 is not NEW, bailing early!
+    return;
+  }
+
+  auto getTargetPropertyOrDefault =
+    [](cmGeneratorTarget const& target, std::string const& property,
+       std::string defaultValue) -> std::string {
+    if (cmValue value = target.GetProperty(property)) {
+      return *value;
+    }
+    return defaultValue;
+  };
+
+  std::string const language = "Swift";
+  std::string const objectDir = this->ConvertToNinjaPath(
+    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
+             this->GetGlobalGenerator()->ConfigDirectory(config)));
+
+  cmGeneratorTarget const& target = *this->GeneratorTarget;
+  cmNinjaBuild objBuild(
+    this->LanguageCompilerRule(language, config, WithScanning::No));
+  cmNinjaVars& vars = objBuild.Variables;
+
+  // The swift toolchain leaves outputs untouched if there are no meaningful
+  // changes to input files (e.g. addition of a comment).
+  vars.emplace("restat", "1");
+
+  std::string const moduleName =
+    getTargetPropertyOrDefault(target, "Swift_MODULE_NAME", target.GetName());
+  std::string const moduleDirectory = getTargetPropertyOrDefault(
+    target, "Swift_MODULE_DIRECTORY",
+    target.LocalGenerator->GetCurrentBinaryDirectory());
+  std::string const moduleFilename = getTargetPropertyOrDefault(
+    target, "Swift_MODULE", cmStrCat(moduleName, ".swiftmodule"));
+  std::string const moduleFilepath =
+    this->ConvertToNinjaPath(cmStrCat(moduleDirectory, '/', moduleFilename));
+
+  bool const isSingleOutput = [this, compileMode]() -> bool {
+    bool isMultiThread = false;
+    if (cmValue numThreadStr =
+          this->GetMakefile()->GetDefinition("CMAKE_Swift_NUM_THREADS")) {
+      unsigned long numThreads;
+      cmStrToULong(*numThreadStr, &numThreads);
+      // numThreads == 1 is multi-threaded according to swiftc
+      isMultiThread = numThreads > 0;
+    }
+    return !isMultiThread && compileMode == cmSwiftCompileMode::Wholemodule;
+  }();
+
+  // Without `-emit-library` or `-emit-executable`, targets with a single
+  // source file parse as a Swift script instead of like normal source. For
+  // non-executable targets, append this to ensure that they are parsed like a
+  // normal source.
+  if (target.GetType() != cmStateEnums::EXECUTABLE) {
+    this->LocalGenerator->AppendFlags(vars["FLAGS"], "-parse-as-library");
+  }
+
+  if (target.GetType() == cmStateEnums::STATIC_LIBRARY) {
+    this->LocalGenerator->AppendFlags(vars["FLAGS"], "-static");
+  }
+
+  // Swift modules only make sense to emit from things that can be imported.
+  // Executables that don't export symbols can't be imported, so don't try to
+  // emit a swiftmodule for them. It will break.
+  if (target.GetType() != cmStateEnums::EXECUTABLE ||
+      target.IsExecutableWithExports()) {
+    std::string const emitModuleFlag = "-emit-module";
+    std::string const modulePathFlag = "-emit-module-path";
+    this->LocalGenerator->AppendFlags(
+      vars["FLAGS"], { emitModuleFlag, modulePathFlag, moduleFilepath });
+    objBuild.Outputs.push_back(moduleFilepath);
+
+    std::string const moduleNameFlag = "-module-name";
+    this->LocalGenerator->AppendFlags(
+      vars["FLAGS"], cmStrCat(moduleNameFlag, ' ', moduleName));
+  }
+
+  if (target.GetType() != cmStateEnums::EXECUTABLE) {
+    std::string const libraryLinkNameFlag = "-module-link-name";
+    std::string const libraryLinkName =
+      this->GetGeneratorTarget()->GetLibraryNames(config).Base;
+    this->LocalGenerator->AppendFlags(
+      vars["FLAGS"], cmStrCat(libraryLinkNameFlag, ' ', libraryLinkName));
+  }
+
+  this->LocalGenerator->AppendFlags(vars["FLAGS"],
+                                    this->GetFlags(language, config));
+  vars["DEFINES"] = this->GetDefines(language, config);
+  vars["INCLUDES"] = this->GetIncludes(language, config);
+
+  // target-level object filename
+  std::string const targetObjectFilename = this->ConvertToNinjaPath(cmStrCat(
+    objectDir, '/', moduleName,
+    this->GetGlobalGenerator()->GetLanguageOutputExtension(language)));
+  objBuild.RspFile = cmStrCat(targetObjectFilename, ".swift.rsp");
+
+  if (isSingleOutput) {
+    this->LocalGenerator->AppendFlags(vars["FLAGS"],
+                                      cmStrCat("-o ", targetObjectFilename));
+    objBuild.Outputs.push_back(targetObjectFilename);
+    this->Configs[config].Objects.push_back(targetObjectFilename);
+  }
+
+  for (cmSourceFile const* sf : sources) {
+    // Add dependency to object build on each source file
+    std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(sf);
+    objBuild.ExplicitDeps.push_back(sourceFilePath);
+
+    if (!isSingleOutput) {
+      // Object outputs
+      std::string const objectFilepath =
+        this->ConvertToNinjaPath(this->GetObjectFilePath(sf, config));
+      this->EnsureParentDirectoryExists(objectFilepath);
+      objBuild.Outputs.push_back(objectFilepath);
+      this->Configs[config].Objects.push_back(objectFilepath);
+
+      // Add OFM data
+      this->EmitSwiftDependencyInfo(sf, config);
+    }
+  }
+
+  if (!isSingleOutput) {
+    this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
+  }
+
+  if (firstForConfig) {
+    this->ExportSwiftObjectCompileCommand(
+      sources, targetObjectFilename, vars["FLAGS"], vars["DEFINES"],
+      vars["INCLUDES"], config, isSingleOutput);
+  }
+
+  for (cmTargetDepend const& dep :
+       this->GetGlobalGenerator()->GetTargetDirectDepends(&target)) {
+    if (!dep->IsLanguageUsed("Swift", config)) {
+      continue;
+    }
+    // Add dependencies on the emitted swiftmodule file from swift targets we
+    // depend on
+    std::string const depModuleName =
+      getTargetPropertyOrDefault(*dep, "Swift_MODULE_NAME", dep->GetName());
+    std::string const depModuleDir = getTargetPropertyOrDefault(
+      *dep, "Swift_MODULE_DIRECTORY",
+      dep->LocalGenerator->GetCurrentBinaryDirectory());
+    std::string const depModuleFilename = getTargetPropertyOrDefault(
+      *dep, "Swift_MODULE", cmStrCat(depModuleName, ".swiftmodule"));
+    std::string const depModuleFilepath =
+      this->ConvertToNinjaPath(cmStrCat(depModuleDir, '/', depModuleFilename));
+    objBuild.ImplicitDeps.push_back(depModuleFilepath);
+  }
+
+  objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
+
+  // Write object build
+  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
+                                         objBuild,
+                                         this->ForceResponseFile() ? -1 : 0);
+}
+
 void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
                                                    const std::string& config)
 {
@@ -2003,6 +2244,7 @@
   std::string const& objectDir, std::string const& objectFileName,
   std::string const& objectFileDir, std::string const& flags,
   std::string const& defines, std::string const& includes,
+  std::string const& targetCompilePdb, std::string const& targetPdb,
   std::string const& outputConfig, WithScanning withScanning)
 {
   if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
@@ -2051,6 +2293,8 @@
   compileObjectVars.Flags = fullFlags.c_str();
   compileObjectVars.Defines = defines.c_str();
   compileObjectVars.Includes = includes.c_str();
+  compileObjectVars.TargetCompilePDB = targetCompilePdb.c_str();
+  compileObjectVars.TargetPDB = targetPdb.c_str();
 
   // Rule for compiling object file.
   std::string cudaCompileMode;
@@ -2105,6 +2349,80 @@
                                                    objectFileName);
 }
 
+void cmNinjaTargetGenerator::ExportSwiftObjectCompileCommand(
+  std::vector<cmSourceFile const*> const& moduleSourceFiles,
+  std::string const& moduleObjectFilename, std::string const& flags,
+  std::string const& defines, std::string const& includes,
+  std::string const& outputConfig, bool singleOutput)
+{
+  if (!this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS")) {
+    return;
+  }
+
+  auto escapeSourceFileName = [this](std::string srcFilename) -> std::string {
+    if (!cmSystemTools::FileIsFullPath(srcFilename)) {
+      srcFilename =
+        cmSystemTools::CollapseFullPath(srcFilename,
+                                        this->GetGlobalGenerator()
+                                          ->GetCMakeInstance()
+                                          ->GetHomeOutputDirectory());
+    }
+
+    return this->LocalGenerator->ConvertToOutputFormat(
+      srcFilename, cmOutputConverter::SHELL);
+  };
+  auto escapedModuleObjectFilename =
+    this->ConvertToNinjaPath(moduleObjectFilename);
+
+  cmRulePlaceholderExpander::RuleVariables compileObjectVars;
+  compileObjectVars.Language = "Swift";
+  compileObjectVars.Flags = flags.c_str();
+  compileObjectVars.Defines = defines.c_str();
+  compileObjectVars.Includes = includes.c_str();
+
+  // Build up the list of source files in the module
+  std::vector<std::string> filenames;
+  filenames.reserve(moduleSourceFiles.size());
+  for (cmSourceFile const* sf : moduleSourceFiles) {
+    filenames.emplace_back(
+      escapeSourceFileName(this->GetCompiledSourceNinjaPath(sf)));
+  }
+  // Note that `escapedSourceFilenames` must remain alive until the
+  // compileObjectVars is consumed or Source will be a dangling pointer.
+  std::string const escapedSourceFilenames = cmJoin(filenames, " ");
+  compileObjectVars.Source = escapedSourceFilenames.c_str();
+
+  std::string const& compileCommand =
+    this->Makefile->GetRequiredDefinition("CMAKE_Swift_COMPILE_OBJECT");
+  cmList compileCmds(compileCommand);
+
+  auto rulePlaceholderExpander =
+    this->GetLocalGenerator()->CreateRulePlaceholderExpander();
+
+  for (cmSourceFile const* sf : moduleSourceFiles) {
+    std::string const sourceFilename = this->GetCompiledSourceNinjaPath(sf);
+    std::string objectFilename = escapedModuleObjectFilename;
+
+    if (!singleOutput) {
+      // If it's not single-output, each source file gets a separate object
+      objectFilename =
+        this->ConvertToNinjaPath(this->GetObjectFilePath(sf, outputConfig));
+    }
+    compileObjectVars.Objects = objectFilename.c_str();
+
+    for (std::string& cmd : compileCmds) {
+      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
+                                                   cmd, compileObjectVars);
+    }
+
+    std::string commandLine = this->GetLocalGenerator()->BuildCommandLine(
+      compileCmds, outputConfig, outputConfig);
+
+    this->GetGlobalGenerator()->AddCXXCompileCommand(
+      commandLine, sourceFilename, objectFilename);
+  }
+}
+
 void cmNinjaTargetGenerator::AdditionalCleanFiles(const std::string& config)
 {
   if (cmValue prop_value =
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index a9bff1d..2bfed80 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -128,6 +128,7 @@
   /// @return the source file path for the given @a source.
   std::string GetCompiledSourceNinjaPath(cmSourceFile const* source) const;
 
+  std::string GetObjectFileDir(const std::string& config) const;
   /// @return the object file path for the given @a source.
   std::string GetObjectFilePath(cmSourceFile const* source,
                                 const std::string& config) const;
@@ -171,6 +172,9 @@
                                        const std::string& config,
                                        const std::string& fileConfig,
                                        bool firstForConfig);
+  void WriteSwiftObjectBuildStatement(
+    std::vector<cmSourceFile const*> const& sources, const std::string& config,
+    const std::string& fileConfig, bool firstForConfig);
   void WriteObjectBuildStatement(cmSourceFile const* source,
                                  const std::string& config,
                                  const std::string& fileConfig,
@@ -189,8 +193,15 @@
     std::string const& objectDir, std::string const& objectFileName,
     std::string const& objectFileDir, std::string const& flags,
     std::string const& defines, std::string const& includes,
+    std::string const& targetCompilePdb, std::string const& targetPdb,
     std::string const& outputConfig, WithScanning withScanning);
 
+  void ExportSwiftObjectCompileCommand(
+    std::vector<cmSourceFile const*> const& moduleSourceFiles,
+    std::string const& moduleObjectFilename, std::string const& flags,
+    std::string const& defines, std::string const& includes,
+    std::string const& outputConfig, bool singleOutput);
+
   void AdditionalCleanFiles(const std::string& config);
 
   cmNinjaDeps GetObjects(const std::string& config) const;
diff --git a/Source/cmPlaceholderExpander.cxx b/Source/cmPlaceholderExpander.cxx
index 118017e..11c7485 100644
--- a/Source/cmPlaceholderExpander.cxx
+++ b/Source/cmPlaceholderExpander.cxx
@@ -48,6 +48,10 @@
   }
   // add the rest of the input
   expandedInput += s.substr(pos, s.size() - pos);
+  // remove trailing whitespace
+  if (!expandedInput.empty() && expandedInput.back() == ' ') {
+    expandedInput.pop_back();
+  }
   s = expandedInput;
 
   return s;
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 8838de4..705314f 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -473,7 +473,27 @@
   SELECT(POLICY, CMP0155,                                                     \
          "C++ sources in targets with at least C++20 are scanned for "        \
          "imports when supported.",                                           \
-         3, 28, 0, cmPolicies::WARN)
+         3, 28, 0, cmPolicies::WARN)                                          \
+  SELECT(                                                                     \
+    POLICY, CMP0156,                                                          \
+    "De-duplicate libraries on link lines based on linker capabilities.", 3,  \
+    29, 0, cmPolicies::WARN)                                                  \
+  SELECT(POLICY, CMP0157,                                                     \
+         "Swift compilation mode selected by an abstraction.", 3, 29, 0,      \
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0158,                                                     \
+         "add_test() honors CMAKE_CROSSCOMPILING_EMULATOR only when "         \
+         "cross-compiling.",                                                  \
+         3, 29, 0, cmPolicies::WARN)                                          \
+  SELECT(POLICY, CMP0159,                                                     \
+         "file(STRINGS) with REGEX updates CMAKE_MATCH_<n>.", 3, 29, 0,       \
+         cmPolicies::WARN)                                                    \
+  SELECT(                                                                     \
+    POLICY, CMP0160,                                                          \
+    "More read-only target properties now error when trying to set them.", 3, \
+    29, 0, cmPolicies::WARN)                                                  \
+  SELECT(POLICY, CMP0161, "CPACK_PRODUCTBUILD_DOMAINS defaults to true.", 3,  \
+         29, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -513,7 +533,10 @@
   F(CMP0131)                                                                  \
   F(CMP0142)                                                                  \
   F(CMP0154)                                                                  \
-  F(CMP0155)
+  F(CMP0155)                                                                  \
+  F(CMP0156)                                                                  \
+  F(CMP0157)                                                                  \
+  F(CMP0160)
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \
diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx
index 9e7854b..1dd1dce 100644
--- a/Source/cmProcessTools.cxx
+++ b/Source/cmProcessTools.cxx
@@ -2,48 +2,68 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcessTools.h"
 
+#include <algorithm>
+#include <iterator>
 #include <ostream>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
 
 #include "cmProcessOutput.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
 
-void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
-                                OutputParser* err, Encoding encoding)
+std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess(
+  cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err,
+  Encoding encoding)
 {
-  cmsysProcess_Execute(cp);
-  char* data = nullptr;
-  int length = 0;
-  int p;
   cmProcessOutput processOutput(encoding);
+
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+
+  auto chain = builder.Start();
+
   std::string strdata;
-  while ((out || err) &&
-         (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    if (out && p == cmsysProcess_Pipe_STDOUT) {
-      processOutput.DecodeText(data, length, strdata, 1);
-      if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-        out = nullptr;
+  cm::uv_pipe_ptr outputPipe;
+  outputPipe.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputPipe, chain.OutputStream());
+  auto outputHandle = cmUVStreamRead(
+    outputPipe,
+    [&out, &processOutput, &strdata](std::vector<char> data) {
+      if (out) {
+        processOutput.DecodeText(data.data(), data.size(), strdata, 1);
+        if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+          out = nullptr;
+        }
       }
-    } else if (err && p == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 2);
-      if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-        err = nullptr;
+    },
+    [&out]() { out = nullptr; });
+  cm::uv_pipe_ptr errorPipe;
+  errorPipe.init(chain.GetLoop(), 0);
+  uv_pipe_open(errorPipe, chain.ErrorStream());
+  auto errorHandle = cmUVStreamRead(
+    errorPipe,
+    [&err, &processOutput, &strdata](std::vector<char> data) {
+      if (err) {
+        processOutput.DecodeText(data.data(), data.size(), strdata, 2);
+        if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+          err = nullptr;
+        }
       }
-    }
+    },
+    [&err]() { err = nullptr; });
+  while (out || err || !chain.Finished()) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
-  if (out) {
-    processOutput.DecodeText(std::string(), strdata, 1);
-    if (!strdata.empty()) {
-      out->Process(strdata.c_str(), static_cast<int>(strdata.size()));
-    }
-  }
-  if (err) {
-    processOutput.DecodeText(std::string(), strdata, 2);
-    if (!strdata.empty()) {
-      err->Process(strdata.c_str(), static_cast<int>(strdata.size()));
-    }
-  }
-  cmsysProcess_WaitForExit(cp, nullptr);
+
+  std::vector<cmUVProcessChain::Status> result;
+  auto status = chain.GetStatus();
+  std::transform(
+    status.begin(), status.end(), std::back_inserter(result),
+    [](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status {
+      return *s;
+    });
+  return result;
 }
 
 cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)
diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h
index 74ec5e0..2bdabea 100644
--- a/Source/cmProcessTools.h
+++ b/Source/cmProcessTools.h
@@ -7,8 +7,10 @@
 #include <cstring>
 #include <iosfwd>
 #include <string>
+#include <vector>
 
 #include "cmProcessOutput.h"
+#include "cmUVProcessChain.h"
 
 /** \class cmProcessTools
  * \brief Helper classes for process output parsing
@@ -81,7 +83,7 @@
   };
 
   /** Run a process and send output to given parsers.  */
-  static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
-                         OutputParser* err = nullptr,
-                         Encoding encoding = cmProcessOutput::Auto);
+  static std::vector<cmUVProcessChain::Status> RunProcess(
+    cmUVProcessChainBuilder& builder, OutputParser* out,
+    OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto);
 };
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 3aef299..53166c1 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -11,6 +11,7 @@
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmExecutionStatus.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -371,29 +372,55 @@
   if (!include) {
     return true;
   }
+  cmList includeFiles{ *include };
 
-  std::string includeFile =
-    cmSystemTools::CollapseFullPath(*include, mf.GetCurrentSourceDirectory());
-  if (!cmSystemTools::FileExists(includeFile)) {
-    status.SetError(cmStrCat("could not find requested file:\n  ", *include));
-    return false;
-  }
-  if (cmSystemTools::FileIsDirectory(includeFile)) {
-    status.SetError(cmStrCat("requested file is a directory:\n  ", *include));
-    return false;
-  }
+  bool failed = false;
+  for (auto filePath : includeFiles) {
+    // Any relative path without a .cmake extension is checked for valid cmake
+    // modules. This logic should be consistent with CMake's include() command.
+    // Otherwise default to checking relative path w.r.t. source directory
+    if (!cmSystemTools::FileIsFullPath(filePath) &&
+        !cmHasLiteralSuffix(filePath, ".cmake")) {
+      std::string mfile = mf.GetModulesFile(cmStrCat(filePath, ".cmake"));
+      if (mfile.empty()) {
+        status.SetError(
+          cmStrCat("could not find requested module:\n  ", filePath));
+        failed = true;
+        continue;
+      }
+      filePath = mfile;
+    }
+    std::string includeFile = cmSystemTools::CollapseFullPath(
+      filePath, mf.GetCurrentSourceDirectory());
+    if (!cmSystemTools::FileExists(includeFile)) {
+      status.SetError(
+        cmStrCat("could not find requested file:\n  ", filePath));
+      failed = true;
+      continue;
+    }
+    if (cmSystemTools::FileIsDirectory(includeFile)) {
+      status.SetError(
+        cmStrCat("requested file is a directory:\n  ", filePath));
+      failed = true;
+      continue;
+    }
 
-  const bool readit = mf.ReadDependentFile(*include);
-  if (readit) {
-    return true;
-  }
+    const bool readit = mf.ReadDependentFile(filePath);
+    if (readit) {
+      // If the included file ran successfully, continue to the next file
+      continue;
+    }
 
-  if (cmSystemTools::GetFatalErrorOccurred()) {
-    return true;
-  }
+    if (cmSystemTools::GetFatalErrorOccurred()) {
+      failed = true;
+      continue;
+    }
 
-  status.SetError(cmStrCat("could not load requested file:\n  ", *include));
-  return false;
+    status.SetError(cmStrCat("could not load requested file:\n  ", filePath));
+    failed = true;
+  }
+  // At this point all files were processed
+  return !failed;
 }
 
 static void TopLevelCMakeVarCondSet(cmMakefile& mf, std::string const& name,
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index adbdba8..0a394b5 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -76,13 +76,6 @@
 
 unsigned int const cmQtAutoGen::ParallelMax = 64;
 
-#ifdef _WIN32
-// Actually 32767 (see
-// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but we
-// allow for a small margin
-size_t const cmQtAutoGen::CommandLineLengthMax = 32000;
-#endif
-
 cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
 {
   switch (genType) {
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index d111422..b302403 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <cm/string_view>
@@ -16,6 +17,22 @@
 class cmQtAutoGen
 {
 public:
+  /** String value with per configuration variants.  */
+  class ConfigString
+  {
+  public:
+    std::string Default;
+    std::unordered_map<std::string, std::string> Config;
+  };
+
+  /** String values with per configuration variants.  */
+  template <typename C>
+  class ConfigStrings
+  {
+  public:
+    C Default;
+    std::unordered_map<std::string, C> Config;
+  };
   /** Integer version.  */
   struct IntegerVersion
   {
@@ -64,11 +81,6 @@
   /// @brief Maximum number of parallel threads/processes in a generator
   static unsigned int const ParallelMax;
 
-#ifdef _WIN32
-  /// @brief Maximum number of characters on command line
-  static size_t const CommandLineLengthMax;
-#endif
-
   /// @brief Returns the generator name
   static cm::string_view GeneratorName(GenT genType);
   /// @brief Returns the generator name in upper case
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index 1da8847..591c53e 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -49,7 +49,7 @@
     bool globalAutoGenTarget = false;
     bool globalAutoRccTarget = false;
     {
-      cmMakefile* makefile = localGen->GetMakefile();
+      cmMakefile const* makefile = localGen->GetMakefile();
       // Detect global autogen target name
       if (makefile->IsOn("CMAKE_GLOBAL_AUTOGEN_TARGET")) {
         std::string targetName =
@@ -118,7 +118,7 @@
           target->GetSafeProperty(this->kw().AUTORCC_EXECUTABLE);
 
         // We support Qt4, Qt5 and Qt6
-        auto qtVersion =
+        auto const qtVersion =
           cmQtAutoGenInitializer::GetQtVersion(target.get(), mocExec);
         bool const validQt = (qtVersion.first.Major == 4) ||
           (qtVersion.first.Major == 5) || (qtVersion.first.Major == 6);
@@ -167,7 +167,7 @@
 {
   // Test if the target already exists
   if (localGen->FindGeneratorTargetToUse(name) == nullptr) {
-    cmMakefile* makefile = localGen->GetMakefile();
+    cmMakefile const* makefile = localGen->GetMakefile();
 
     // Create utility target
     auto cc = cm::make_unique<cmCustomCommand>();
@@ -192,9 +192,10 @@
 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoGen(
   cmLocalGenerator* localGen, std::string const& targetName)
 {
-  auto it = this->GlobalAutoGenTargets_.find(localGen);
+  auto const it = this->GlobalAutoGenTargets_.find(localGen);
   if (it != this->GlobalAutoGenTargets_.end()) {
-    cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
+    cmGeneratorTarget const* target =
+      localGen->FindGeneratorTargetToUse(it->second);
     if (target != nullptr) {
       target->Target->AddUtility(targetName, false, localGen->GetMakefile());
     }
@@ -204,33 +205,91 @@
 void cmQtAutoGenGlobalInitializer::AddToGlobalAutoRcc(
   cmLocalGenerator* localGen, std::string const& targetName)
 {
-  auto it = this->GlobalAutoRccTargets_.find(localGen);
+  auto const it = this->GlobalAutoRccTargets_.find(localGen);
   if (it != this->GlobalAutoRccTargets_.end()) {
-    cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(it->second);
+    cmGeneratorTarget const* target =
+      localGen->FindGeneratorTargetToUse(it->second);
     if (target != nullptr) {
       target->Target->AddUtility(targetName, false, localGen->GetMakefile());
     }
   }
 }
 
-cmQtAutoGen::CompilerFeaturesHandle
+cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
 cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
-  std::string const& generator, std::string const& executable,
-  std::string& error)
+  std::string const& generator, cmQtAutoGen::ConfigString const& executable,
+  std::string& error, bool const isMultiConfig, bool const UseBetterGraph)
 {
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res;
+  if (isMultiConfig && UseBetterGraph) {
+    for (auto const& config : executable.Config) {
+      auto const exe = config.second;
+      // Check if we have cached features
+      {
+        auto it = this->CompilerFeatures_.Config[config.first].find(exe);
+        if (it != this->CompilerFeatures_.Config[config.first].end()) {
+          res.Config[config.first] = it->second;
+          continue;
+        }
+      }
+
+      // Check if the executable exists
+      if (!cmSystemTools::FileExists(exe, true)) {
+        error = cmStrCat("The \"", generator, "\" executable ",
+                         cmQtAutoGen::Quoted(exe), " does not exist.");
+        res.Config[config.first] = {};
+        continue;
+      }
+
+      // Test the executable
+      std::string stdOut;
+      {
+        std::string stdErr;
+        std::vector<std::string> command;
+        command.emplace_back(exe);
+        command.emplace_back("-h");
+        int retVal = 0;
+        const bool runResult = cmSystemTools::RunSingleCommand(
+          command, &stdOut, &stdErr, &retVal, nullptr,
+          cmSystemTools::OUTPUT_NONE, cmDuration::zero(),
+          cmProcessOutput::Auto);
+        if (!runResult) {
+          error = cmStrCat("Test run of \"", generator, "\" executable ",
+                           cmQtAutoGen::Quoted(exe), " failed.\n",
+                           cmQtAutoGen::QuotedCommand(command), '\n', stdOut,
+                           '\n', stdErr);
+          res.Config[config.first] = {};
+          continue;
+        }
+      }
+
+      // Create valid handle
+      res.Config[config.first] =
+        std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      res.Config[config.first]->HelpOutput = std::move(stdOut);
+
+      // Register compiler features
+      this->CompilerFeatures_.Config[config.first].emplace(
+        exe, res.Config[config.first]);
+    }
+    return res;
+  }
+
   // Check if we have cached features
   {
-    auto it = this->CompilerFeatures_.find(executable);
-    if (it != this->CompilerFeatures_.end()) {
-      return it->second;
+    auto const it = this->CompilerFeatures_.Default.find(executable.Default);
+    if (it != this->CompilerFeatures_.Default.end()) {
+      res.Default = it->second;
+      return res;
     }
   }
 
   // Check if the executable exists
-  if (!cmSystemTools::FileExists(executable, true)) {
-    error = cmStrCat("The \"", generator, "\" executable ",
-                     cmQtAutoGen::Quoted(executable), " does not exist.");
-    return cmQtAutoGen::CompilerFeaturesHandle();
+  if (!cmSystemTools::FileExists(executable.Default, true)) {
+    error =
+      cmStrCat("The \"", generator, "\" executable ",
+               cmQtAutoGen::Quoted(executable.Default), " does not exist.");
+    return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
   }
 
   // Test the executable
@@ -238,7 +297,7 @@
   {
     std::string stdErr;
     std::vector<std::string> command;
-    command.emplace_back(executable);
+    command.emplace_back(executable.Default);
     command.emplace_back("-h");
     int retVal = 0;
     const bool runResult = cmSystemTools::RunSingleCommand(
@@ -246,20 +305,18 @@
       cmDuration::zero(), cmProcessOutput::Auto);
     if (!runResult) {
       error = cmStrCat("Test run of \"", generator, "\" executable ",
-                       cmQtAutoGen::Quoted(executable), " failed.\n",
+                       cmQtAutoGen::Quoted(executable.Default), " failed.\n",
                        cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
                        stdErr);
-      return cmQtAutoGen::CompilerFeaturesHandle();
+      return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
     }
   }
 
-  // Create valid handle
-  cmQtAutoGen::CompilerFeaturesHandle res =
-    std::make_shared<cmQtAutoGen::CompilerFeatures>();
-  res->HelpOutput = std::move(stdOut);
+  res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>();
+  res.Default->HelpOutput = std::move(stdOut);
 
   // Register compiler features
-  this->CompilerFeatures_.emplace(executable, res);
+  this->CompilerFeatures_.Default.emplace(executable.Default, res.Default);
 
   return res;
 }
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index e8569a5..c1c4758 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -66,14 +66,17 @@
   void AddToGlobalAutoRcc(cmLocalGenerator* localGen,
                           std::string const& targetName);
 
-  cmQtAutoGen::CompilerFeaturesHandle GetCompilerFeatures(
-    std::string const& generator, std::string const& executable,
-    std::string& error);
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
+  GetCompilerFeatures(std::string const& generator,
+                      cmQtAutoGen::ConfigString const& executable,
+                      std::string& error, bool isMultiConfig,
+                      bool UseBetterGraph);
 
   std::vector<std::unique_ptr<cmQtAutoGenInitializer>> Initializers_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_;
-  std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>
+  cmQtAutoGen::ConfigStrings<
+    std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>>
     CompilerFeatures_;
   Keywords const Keywords_;
 };
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 66544d0..8542206 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenInitializer.h"
 
+#include <array>
 #include <cstddef>
 #include <deque>
 #include <initializer_list>
@@ -17,6 +18,7 @@
 #include <cm/algorithm>
 #include <cm/iterator>
 #include <cm/memory>
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -301,15 +303,22 @@
   return fileStream.Close();
 }
 
-void AddAutogenExecutableToDependencies(
-  cmQtAutoGenInitializer::GenVarsT const& genVars,
-  std::vector<std::string>& dependencies)
+cmQtAutoGen::ConfigStrings<std::vector<std::string>> generateListOptions(
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> const&
+    executableFeatures,
+  bool IsMultiConfig)
 {
-  if (genVars.ExecutableTarget != nullptr) {
-    dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
-  } else if (!genVars.Executable.empty()) {
-    dependencies.push_back(genVars.Executable);
+  cmQtAutoGen::ConfigStrings<std::vector<std::string>> tempListOptions;
+  if (IsMultiConfig) {
+    for (auto const& executableFeature : executableFeatures.Config) {
+      tempListOptions.Config[executableFeature.first] =
+        executableFeature.second->ListOptions;
+    }
+  } else {
+    tempListOptions.Default = executableFeatures.Default->ListOptions;
   }
+
+  return tempListOptions;
 }
 
 } // End of unnamed namespace
@@ -334,6 +343,42 @@
   this->Rcc.GlobalTarget = globalAutoRccTarget;
   this->CrossConfig =
     !this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty();
+  this->UseBetterGraph =
+    this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsSet()
+    ? this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsOn()
+    : (this->QtVersion >= IntegerVersion(6, 8));
+  // AUTOGEN_BETTER_GRAPH_MULTI_CONFIG is set explicitly because it is read by
+  // the qt library
+  this->GenTarget->Target->SetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG",
+                                       this->UseBetterGraph ? "ON" : "OFF");
+}
+
+void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies(
+  cmQtAutoGenInitializer::GenVarsT const& genVars,
+  std::vector<std::string>& dependencies) const
+{
+  if (genVars.ExecutableTarget != nullptr) {
+    dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
+  } else if (this->MultiConfig && this->UseBetterGraph) {
+    cm::string_view const& configGenexWithCommandConfig =
+      "$<COMMAND_CONFIG:$<$<CONFIG:";
+    cm::string_view const& configGenex = "$<$<CONFIG:";
+    cm::string_view const& configGenexEnd = ">";
+    cm::string_view const& configGenexEndWithCommandConfig = ">>";
+    auto genexBegin =
+      this->CrossConfig ? configGenexWithCommandConfig : configGenex;
+    auto genexEnd =
+      this->CrossConfig ? configGenexEndWithCommandConfig : configGenexEnd;
+    for (auto const& config : genVars.Executable.Config) {
+      auto executableWithConfig =
+        cmStrCat(genexBegin, config.first, ">:", config.second, genexEnd);
+      dependencies.emplace_back(std::move(executableWithConfig));
+    }
+  } else {
+    if (!genVars.Executable.Default.empty()) {
+      dependencies.push_back(genVars.Executable.Default);
+    }
+  }
 }
 
 bool cmQtAutoGenInitializer::InitCustomTargets()
@@ -485,6 +530,38 @@
       }
     }
 
+#ifdef _WIN32
+    {
+      const auto& value =
+        this->GenTarget->GetProperty("AUTOGEN_COMMAND_LINE_LENGTH_MAX");
+      if (value.IsSet()) {
+        using maxCommandLineLengthType =
+          decltype(this->AutogenTarget.MaxCommandLineLength);
+        unsigned long propInt = 0;
+        if (cmStrToULong(value, &propInt) && propInt > 0 &&
+            propInt <= std::numeric_limits<maxCommandLineLengthType>::max()) {
+          this->AutogenTarget.MaxCommandLineLength =
+            static_cast<maxCommandLineLengthType>(propInt);
+        } else {
+          // Warn the project author that AUTOGEN_PARALLEL is not valid.
+          this->Makefile->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat("AUTOGEN_COMMAND_LINE_LENGTH_MAX=\"", *value,
+                     "\" for target \"", this->GenTarget->GetName(),
+                     "\" is not valid. Using no limit for "
+                     "AUTOGEN_COMMAND_LINE_LENGTH_MAX"));
+          this->AutogenTarget.MaxCommandLineLength =
+            std::numeric_limits<maxCommandLineLengthType>::max();
+        }
+      } else {
+        // Actually 32767 (see
+        // https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but
+        // we allow for a small margin
+        this->AutogenTarget.MaxCommandLineLength = 32000;
+      }
+    }
+#endif
+
     // Autogen target info and settings files
     {
       // Info file
@@ -779,18 +856,51 @@
       return false;
     }
     // Evaluate test output on demand
-    CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
-    if (!features.Evaluated) {
-      // Look for list options
-      if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
-        if (features.HelpOutput.find("--list") != std::string::npos) {
-          features.ListOptions.emplace_back("--list");
-        } else if (features.HelpOutput.find("-list") != std::string::npos) {
-          features.ListOptions.emplace_back("-list");
+    auto& features = this->Rcc.ExecutableFeatures;
+    auto checkAndAddOptions = [this](CompilerFeaturesHandle& feature) {
+      if (!feature->Evaluated) {
+        // Look for list options
+        if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
+          static std::array<std::string, 2> const listOptions{ { "--list",
+                                                                 "-list" } };
+          for (std::string const& opt : listOptions) {
+            if (feature->HelpOutput.find(opt) != std::string::npos) {
+              feature->ListOptions.emplace_back(opt);
+              break;
+            }
+          }
         }
+        // Evaluation finished
+        feature->Evaluated = true;
       }
-      // Evaluation finished
-      features.Evaluated = true;
+    };
+    if (this->MultiConfig && this->UseBetterGraph) {
+      for (auto const& config : this->ConfigsList) {
+        checkAndAddOptions(features.Config[config]);
+      }
+    } else {
+      checkAndAddOptions(features.Default);
+    }
+  }
+
+  // Disable zstd if it is not supported
+  {
+    std::string const qtFeatureZSTD = "QT_FEATURE_zstd";
+    if (this->GenTarget->Target->GetMakefile()->IsDefinitionSet(
+          qtFeatureZSTD)) {
+      const auto zstdDef =
+        this->GenTarget->Target->GetMakefile()->GetSafeDefinition(
+          qtFeatureZSTD);
+      const auto zstdVal = cmValue(zstdDef);
+      if (zstdVal.IsOff()) {
+        auto const& kw = this->GlobalInitializer->kw();
+        auto rccOptions = this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS);
+        std::string const nozstd = "--no-zstd";
+        if (rccOptions.find(nozstd) == std::string::npos) {
+          rccOptions.append(";" + nozstd + ";");
+        }
+        this->GenTarget->Target->SetProperty(kw.AUTORCC_OPTIONS, rccOptions);
+      }
     }
   }
 
@@ -1126,8 +1236,14 @@
       // Path checksum
       qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
       // Output file name
-      qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
-                                "/qrc_", qrc.QrcName, ".cpp");
+      if (this->MultiConfig && !this->GlobalGen->IsXcode() &&
+          this->UseBetterGraph) {
+        qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                  "_$<CONFIG>", "/qrc_", qrc.QrcName, ".cpp");
+      } else {
+        qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                  "/qrc_", qrc.QrcName, ".cpp");
+      }
       std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
                                         qrc.QrcName, '_', qrc.QrcPathChecksum);
       qrc.LockFile = cmStrCat(base, "_Lock.lock");
@@ -1159,11 +1275,25 @@
     for (Qrc& qrc : this->Rcc.Qrcs) {
       if (!qrc.Generated) {
         std::string error;
-        RccLister const lister(this->Rcc.Executable,
-                               this->Rcc.ExecutableFeatures->ListOptions);
-        if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
-          cmSystemTools::Error(error);
-          return false;
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            RccLister const lister(
+              this->Rcc.Executable.Config[config],
+              this->Rcc.ExecutableFeatures.Config[config]->ListOptions);
+            if (!lister.list(qrc.QrcFile, qrc.Resources.Config[config],
+                             error)) {
+              cmSystemTools::Error(error);
+              return false;
+            }
+          }
+        } else {
+          RccLister const lister(
+            this->Rcc.Executable.Default,
+            this->Rcc.ExecutableFeatures.Default->ListOptions);
+          if (!lister.list(qrc.QrcFile, qrc.Resources.Default, error)) {
+            cmSystemTools::Error(error);
+            return false;
+          }
         }
       }
     }
@@ -1191,8 +1321,9 @@
   if (this->Moc.Enabled) {
     this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
     if (useDepfile) {
-      if (this->MultiConfig && this->CrossConfig &&
-          this->GlobalGen->GetName().find("Ninja") != std::string::npos) {
+      if (this->CrossConfig &&
+          this->GlobalGen->GetName().find("Ninja") != std::string::npos &&
+          !this->UseBetterGraph) {
         // Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
         // ${target}_autogen/timestamp custom command.
         // We cannot just use Moc.CompilationFileGenex here, because that
@@ -1235,28 +1366,11 @@
   // Compose command lines
   // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
   // instead of fiddling with the include directories
-  std::vector<std::string> configs;
-  this->GlobalGen->GetQtAutoGenConfigs(configs);
+
   bool constexpr stdPipesUTF8 = true;
   cmCustomCommandLines commandLines;
-  if (!this->CrossConfig) {
-    std::string autogenInfoFileConfig;
-    if (this->MultiConfig) {
-      autogenInfoFileConfig = "$<CONFIG>";
-    } else {
-      autogenInfoFileConfig = configs[0];
-    }
-    commandLines.push_back(cmMakeCommandLine(
-      { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
-        this->AutogenTarget.InfoFile, autogenInfoFileConfig }));
-
-  } else {
-    for (auto const& config : configs) {
-      commandLines.push_back(cmMakeCommandLine(
-        { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
-          this->AutogenTarget.InfoFile, config }));
-    }
-  }
+  AddCMakeProcessToCommandLines(this->AutogenTarget.InfoFile, "cmake_autogen",
+                                commandLines);
 
   // Use PRE_BUILD on demand
   bool usePRE_BUILD = false;
@@ -1433,18 +1547,47 @@
 
       AddAutogenExecutableToDependencies(this->Moc, dependencies);
       AddAutogenExecutableToDependencies(this->Uic, dependencies);
-
+      std::string outputFile;
+      std::string depFile;
       // Create the custom command that outputs the timestamp file.
-      const char timestampFileName[] = "timestamp";
-      const std::string outputFile =
-        cmStrCat(this->Dir.Build, "/", timestampFileName);
-      this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
-      this->AutogenTarget.DepFileRuleName =
-        cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
-      commandLines.push_back(cmMakeCommandLine(
-        { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
+      if (this->MultiConfig && this->UseBetterGraph) {
+        // create timestamp file with $<CONFIG> in the name so that
+        // every cmake_autogen target has its own timestamp file
+        std::string const configView = "$<CONFIG>";
+        std::string const timestampFileWithoutConfig = "timestamp_";
+        std::string const depFileWithoutConfig =
+          cmStrCat(this->Dir.Build, "/deps_");
+        std::string const timestampFileName =
+          timestampFileWithoutConfig + configView;
+        outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
+        auto const depFileWithConfig =
+          cmStrCat(depFileWithoutConfig, configView);
+        depFile = depFileWithConfig;
+        commandLines.push_back(cmMakeCommandLine(
+          { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
 
-      this->AddGeneratedSource(outputFile, this->Moc);
+        ConfigString outputFileWithConfig;
+        for (std::string const& config : this->ConfigsList) {
+          auto tempTimestampFileName = timestampFileWithoutConfig + config;
+          auto tempDepFile = depFileWithoutConfig + config;
+          outputFileWithConfig.Config[config] = tempTimestampFileName;
+          this->AutogenTarget.DepFileRuleName.Config[config] =
+            cmStrCat(this->Dir.RelativeBuild, "/", tempTimestampFileName);
+          this->AutogenTarget.DepFile.Config[config] = tempDepFile;
+        }
+        this->AddGeneratedSource(outputFileWithConfig, this->Moc);
+      } else {
+        cm::string_view const timestampFileName = "timestamp";
+        outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
+        this->AutogenTarget.DepFile.Default =
+          cmStrCat(this->Dir.Build, "/deps");
+        depFile = this->AutogenTarget.DepFile.Default;
+        this->AutogenTarget.DepFileRuleName.Default =
+          cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
+        commandLines.push_back(cmMakeCommandLine(
+          { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
+        this->AddGeneratedSource(outputFile, this->Moc);
+      }
       cc = cm::make_unique<cmCustomCommand>();
       cc->SetOutputs(outputFile);
       cc->SetByproducts(timestampByproducts);
@@ -1453,14 +1596,11 @@
       cc->SetComment(autogenComment.c_str());
       cc->SetWorkingDirectory(this->Dir.Work.c_str());
       cc->SetEscapeOldStyle(false);
-      cc->SetDepfile(this->AutogenTarget.DepFile);
+      cc->SetDepfile(depFile);
       cc->SetStdPipesUTF8(stdPipesUTF8);
       this->LocalGen->AddCustomCommandToOutput(std::move(cc));
-
-      // Alter variables for the autogen target which now merely wraps the
-      // custom command
       dependencies.clear();
-      dependencies.emplace_back(outputFile);
+      dependencies.emplace_back(std::move(outputFile));
       commandLines.clear();
       autogenComment.clear();
     }
@@ -1512,6 +1652,36 @@
   return true;
 }
 
+void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines(
+  std::string const& infoFile, std::string const& processName,
+  cmCustomCommandLines& commandLines)
+{
+  if (this->CrossConfig && this->UseBetterGraph) {
+    commandLines.push_back(cmMakeCommandLine(
+      { cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile,
+        "$<CONFIG>", "$<COMMAND_CONFIG:$<CONFIG>>" }));
+  } else if ((this->MultiConfig && this->GlobalGen->IsXcode()) ||
+             this->CrossConfig) {
+    for (std::string const& config : this->ConfigsList) {
+      commandLines.push_back(
+        cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
+                            processName, infoFile, config }));
+    }
+  } else {
+    std::string autoInfoFileConfig;
+    if (this->MultiConfig) {
+      autoInfoFileConfig = "$<CONFIG>";
+    } else {
+      std::vector<std::string> configs;
+      this->GlobalGen->GetQtAutoGenConfigs(configs);
+      autoInfoFileConfig = configs[0];
+    }
+    commandLines.push_back(
+      cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName,
+                          infoFile, autoInfoFileConfig }));
+  }
+}
+
 bool cmQtAutoGenInitializer::InitRccTargets()
 {
   for (Qrc const& qrc : this->Rcc.Qrcs) {
@@ -1532,18 +1702,7 @@
     ccDepends.push_back(qrc.InfoFile);
 
     cmCustomCommandLines commandLines;
-    if (this->MultiConfig) {
-      // Build for all configurations
-      for (std::string const& config : this->ConfigsList) {
-        commandLines.push_back(
-          cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
-                              "cmake_autorcc", qrc.InfoFile, config }));
-      }
-    } else {
-      commandLines.push_back(
-        cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
-                            "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
-    }
+    AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines);
 
     std::string const ccComment =
       cmStrCat("Automatic RCC for ",
@@ -1594,13 +1753,28 @@
       // Create custom rcc command
       {
         // Add the resource files to the dependencies
-        for (std::string const& fileName : qrc.Resources) {
-          // Add resource file to the custom command dependencies
-          ccDepends.push_back(fileName);
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            // Add resource file to the custom command dependencies
+            auto resourceFilesWithConfig = cmStrCat(
+              "$<$<CONFIG:", config,
+              ">:", cmList{ qrc.Resources.Config.at(config) }.to_string(),
+              ">");
+            ccDepends.emplace_back(std::move(resourceFilesWithConfig));
+          }
+        } else {
+          for (std::string const& fileName : qrc.Resources.Default) {
+            // Add resource file to the custom command dependencies
+            ccDepends.push_back(fileName);
+          }
         }
+
         if (!this->Rcc.ExecutableTargetName.empty()) {
           ccDepends.push_back(this->Rcc.ExecutableTargetName);
         }
+
+        AddAutogenExecutableToDependencies(this->Rcc, ccDepends);
+
         cc->SetOutputs(ccOutput);
         cc->SetDepends(ccDepends);
         this->LocalGen->AddCustomCommandToOutput(std::move(cc));
@@ -1700,7 +1874,13 @@
 
   // General
   info.SetBool("MULTI_CONFIG", this->MultiConfig);
+  info.SetBool("CROSS_CONFIG", this->CrossConfig);
+  info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
   info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
+#ifdef _WIN32
+  info.SetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
+               this->AutogenTarget.MaxCommandLineLength);
+#endif
   info.SetUInt("VERBOSITY", this->Verbosity);
 
   // Directories
@@ -1713,14 +1893,14 @@
 
   info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
   info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
-  info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
-  info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
+  info.SetConfig("QT_MOC_EXECUTABLE", this->Moc.Executable);
+  info.SetConfig("QT_UIC_EXECUTABLE", this->Uic.Executable);
 
   info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
   info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
   info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
-  info.Set("DEP_FILE", this->AutogenTarget.DepFile);
-  info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
+  info.SetConfig("DEP_FILE", this->AutogenTarget.DepFile);
+  info.SetConfig("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
   info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
   info.SetArray("HEADER_EXTENSIONS",
                 this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
@@ -1847,6 +2027,8 @@
 
     // General
     info.SetBool("MULTI_CONFIG", this->MultiConfig);
+    info.SetBool("CROSS_CONFIG", this->CrossConfig);
+    info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
     info.SetUInt("VERBOSITY", this->Verbosity);
     info.Set("GENERATOR", this->GlobalGen->GetName());
 
@@ -1863,16 +2045,17 @@
     info.SetConfig("INCLUDE_DIR", this->Dir.Include);
 
     // rcc executable
-    info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
-    info.SetArray("RCC_LIST_OPTIONS",
-                  this->Rcc.ExecutableFeatures->ListOptions);
+    info.SetConfig("RCC_EXECUTABLE", this->Rcc.Executable);
+    info.SetConfigArray(
+      "RCC_LIST_OPTIONS",
+      generateListOptions(this->Rcc.ExecutableFeatures, this->MultiConfig));
 
     // qrc file
     info.Set("SOURCE", qrc.QrcFile);
     info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
     info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
     info.SetArray("OPTIONS", qrc.Options);
-    info.SetArray("INPUTS", qrc.Resources);
+    info.SetConfigArray("INPUTS", qrc.Resources);
 
     info.Save(qrc.InfoFile);
   }
@@ -2217,16 +2400,32 @@
         cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
         cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
-        genVars.Executable = cge->Evaluate(this->LocalGen, "");
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] =
+              cge->Evaluate(this->LocalGen, config);
+          }
+        } else {
+          genVars.Executable.Default = cge->Evaluate(this->LocalGen, "");
+        }
       }
-      if (genVars.Executable.empty() && !ignoreMissingTarget) {
+
+      if (genVars.Executable.Default.empty() &&
+          genVars.Executable.Config.empty() && !ignoreMissingTarget) {
         print_err(prop + " evaluates to an empty value");
         return false;
       }
 
       // Create empty compiler features.
-      genVars.ExecutableFeatures =
-        std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      if (this->MultiConfig && this->UseBetterGraph) {
+        for (auto const& config : this->ConfigsList) {
+          genVars.ExecutableFeatures.Config[config] =
+            std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        }
+      } else {
+        genVars.ExecutableFeatures.Default =
+          std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      }
       return true;
     }
   }
@@ -2251,15 +2450,39 @@
       genVars.ExecutableTargetName = targetName;
       genVars.ExecutableTarget = genTarget;
       if (genTarget->IsImported()) {
-        genVars.Executable = genTarget->ImportedGetLocation("");
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] =
+              genTarget->ImportedGetLocation(config);
+          }
+        } else {
+          genVars.Executable.Default =
+            genTarget->ImportedGetLocation(this->ConfigDefault);
+        }
+
       } else {
-        genVars.Executable = genTarget->GetLocation("");
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] = genTarget->GetLocation(config);
+          }
+        } else {
+          genVars.Executable.Default =
+            genTarget->GetLocation(this->ConfigDefault);
+        }
       }
     } else {
       if (ignoreMissingTarget) {
         // Create empty compiler features.
-        genVars.ExecutableFeatures =
-          std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        if (this->MultiConfig && this->UseBetterGraph) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.ExecutableFeatures.Config[config] =
+              std::make_shared<cmQtAutoGen::CompilerFeatures>();
+          }
+        } else {
+          genVars.ExecutableFeatures.Default =
+            std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        }
+
         return true;
       }
       print_err(cmStrCat("Could not find ", executable, " executable target ",
@@ -2272,10 +2495,22 @@
   {
     std::string err;
     genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
-      executable, genVars.Executable, err);
-    if (!genVars.ExecutableFeatures) {
-      print_err(err);
-      return false;
+      executable, genVars.Executable, err, this->MultiConfig,
+      this->UseBetterGraph);
+    if (this->MultiConfig && this->UseBetterGraph) {
+      for (auto const& config : this->ConfigsList) {
+        if (!genVars.ExecutableFeatures.Config[config]) {
+          if (!genVars.ExecutableFeatures.Config[config]) {
+            print_err(err);
+            return false;
+          }
+        }
+      }
+    } else {
+      if (!genVars.ExecutableFeatures.Default) {
+        print_err(err);
+        return false;
+      }
     }
   }
 
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index a44d33f..1ab038b 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -5,6 +5,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <cstddef>
+#include <limits>
 #include <memory>
 #include <set>
 #include <string>
@@ -18,6 +19,7 @@
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 
+class cmCustomCommandLines;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmLocalGenerator;
@@ -32,23 +34,6 @@
 class cmQtAutoGenInitializer : public cmQtAutoGen
 {
 public:
-  /** String value with per configuration variants.  */
-  class ConfigString
-  {
-  public:
-    std::string Default;
-    std::unordered_map<std::string, std::string> Config;
-  };
-
-  /** String values with per configuration variants.  */
-  template <typename C>
-  class ConfigStrings
-  {
-  public:
-    C Default;
-    std::unordered_map<std::string, C> Config;
-  };
-
   /** rcc job.  */
   class Qrc
   {
@@ -63,7 +48,7 @@
     bool Generated = false;
     bool Unique = false;
     std::vector<std::string> Options;
-    std::vector<std::string> Resources;
+    ConfigStrings<std::vector<std::string>> Resources;
   };
 
   /** moc and/or uic file.  */
@@ -90,8 +75,8 @@
     // Executable
     std::string ExecutableTargetName;
     cmGeneratorTarget* ExecutableTarget = nullptr;
-    std::string Executable;
-    CompilerFeaturesHandle ExecutableFeatures;
+    ConfigString Executable;
+    ConfigStrings<CompilerFeaturesHandle> ExecutableFeatures;
 
     GenVarsT(GenT gen)
       : Gen(gen)
@@ -141,6 +126,9 @@
                           GenVarsT const& genVars, bool prepend = false);
   void AddToSourceGroup(std::string const& fileName,
                         cm::string_view genNameUpper);
+  void AddCMakeProcessToCommandLines(std::string const& infoFile,
+                                     std::string const& processName,
+                                     cmCustomCommandLines& commandLines);
   void AddCleanFile(std::string const& fileName);
 
   void ConfigFileNames(ConfigString& configString, cm::string_view prefix,
@@ -155,6 +143,9 @@
                        bool ignoreMissingTarget) const;
 
   void handleSkipPch(cmSourceFile* sf);
+  void AddAutogenExecutableToDependencies(
+    cmQtAutoGenInitializer::GenVarsT const& genVars,
+    std::vector<std::string>& dependencies) const;
 
   cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr;
   cmGeneratorTarget* GenTarget = nullptr;
@@ -168,6 +159,7 @@
   unsigned int Verbosity = 0;
   bool MultiConfig = false;
   bool CrossConfig = false;
+  bool UseBetterGraph = false;
   bool CMP0071Accept = false;
   bool CMP0071Warn = false;
   bool CMP0100Accept = false;
@@ -194,6 +186,8 @@
     bool GlobalTarget = false;
     // Settings
     unsigned int Parallel = 1;
+    unsigned int MaxCommandLineLength =
+      std::numeric_limits<unsigned int>::max();
     // Configuration files
     std::string InfoFile;
     ConfigString SettingsFile;
@@ -202,8 +196,8 @@
     bool DependOrigin = false;
     std::set<std::string> DependFiles;
     std::set<cmTarget*> DependTargets;
-    std::string DepFile;
-    std::string DepFileRuleName;
+    ConfigString DepFile;
+    ConfigString DepFileRuleName;
     // Sources to process
     std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
     std::unordered_map<cmSourceFile*, MUFileHandle> Sources;
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index c048312..ebdec12 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -430,10 +430,12 @@
   return cmQtAutoGen::Quoted(res);
 }
 
-bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config,
+                            cm::string_view executableConfig)
 {
   // Info config
   this->InfoConfig_ = std::string(config);
+  this->ExecutableConfig_ = std::string(executableConfig);
 
   // Info file
   this->InfoFile_ = std::string(infoFile);
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 5c3a8ad..4b15fc7 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -90,6 +90,10 @@
   std::string const& InfoDir() const { return this->InfoDir_; }
   cmFileTime const& InfoFileTime() const { return this->InfoFileTime_; }
   std::string const& InfoConfig() const { return this->InfoConfig_; }
+  std::string const& ExecutableConfig() const
+  {
+    return this->ExecutableConfig_;
+  }
 
   // -- Info file parsing
   /** Info file reader class. */
@@ -151,7 +155,8 @@
   std::string MessagePath(cm::string_view path) const;
 
   // -- Run
-  bool Run(cm::string_view infoFile, cm::string_view config);
+  bool Run(cm::string_view infoFile, cm::string_view config,
+           cm::string_view executableConfig);
 
 protected:
   // -- Abstract processing interface
@@ -170,6 +175,7 @@
   std::string InfoDir_;
   cmFileTime InfoFileTime_;
   std::string InfoConfig_;
+  std::string ExecutableConfig_;
   // -- Directories
   ProjectDirsT ProjectDirs_;
 };
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index ece657d..408a22c 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <atomic>
 #include <cstddef>
+#include <limits>
 #include <map>
 #include <mutex>
 #include <set>
@@ -170,8 +171,12 @@
     // -- Attributes
     // - Config
     bool MultiConfig = false;
+    bool CrossConfig = false;
+    bool UseBetterGraph = false;
     IntegerVersion QtVersion = { 4, 0 };
     unsigned int ThreadCount = 0;
+    unsigned int MaxCommandLineLength =
+      std::numeric_limits<unsigned int>::max();
     // - Directories
     std::string AutogenBuildDir;
     std::string AutogenIncludeDir;
@@ -190,7 +195,7 @@
   {
   public:
     // -- Parse Cache
-    std::atomic<bool> ParseCacheChanged = ATOMIC_VAR_INIT(false);
+    std::atomic<bool> ParseCacheChanged{ false };
     cmFileTime ParseCacheTime;
     ParseCacheT ParseCache;
 
@@ -333,6 +338,13 @@
                          std::vector<std::string> const& command,
                          std::string const& output) const;
 
+    /*
+     * Check if command line exceeds maximum length supported by OS
+     * (if on Windows) and switch to using a response file instead.
+     */
+    void MaybeWriteResponseFile(std::string const& outputFile,
+                                std::vector<std::string>& cmd) const;
+
     /** @brief Run an external process. Use only during Process() call!  */
     bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
                     std::vector<std::string> const& command,
@@ -498,10 +510,6 @@
 
   protected:
     ParseCacheT::FileHandleT CacheEntry;
-
-  private:
-    void MaybeWriteMocResponseFile(std::string const& outputFile,
-                                   std::vector<std::string>& cmd) const;
   };
 
   /** uic compiles a file.  */
@@ -583,7 +591,7 @@
   std::string SettingsStringMoc_;
   std::string SettingsStringUic_;
   // -- Worker thread pool
-  std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
+  std::atomic<bool> JobError_{ false };
   cmWorkerPool WorkerPool_;
   // -- Concurrent processing
   mutable std::mutex CMakeLibMutex_;
@@ -795,6 +803,51 @@
   this->Gen()->Log().ErrorCommand(genType, message, command, output);
 }
 
+/*
+ * Check if command line exceeds maximum length supported by OS
+ * (if on Windows) and switch to using a response file instead.
+ */
+void cmQtAutoMocUicT::JobT::MaybeWriteResponseFile(
+  std::string const& outputFile, std::vector<std::string>& cmd) const
+{
+#ifdef _WIN32
+  // Ensure cmd is less than CommandLineLengthMax characters
+  size_t commandLineLength = cmd.size(); // account for separating spaces
+  for (std::string const& str : cmd) {
+    commandLineLength += str.length();
+  }
+  if (commandLineLength >= this->BaseConst().MaxCommandLineLength) {
+    // Command line exceeds maximum size allowed by OS
+    // => create response file
+    std::string const responseFile = cmStrCat(outputFile, ".rsp");
+
+    cmsys::ofstream fout(responseFile.c_str());
+    if (!fout) {
+      this->LogError(
+        GenT::MOC,
+        cmStrCat("AUTOMOC was unable to create a response file at\n  ",
+                 this->MessagePath(responseFile)));
+      return;
+    }
+
+    auto it = cmd.begin();
+    while (++it != cmd.end()) {
+      fout << *it << "\n";
+    }
+    fout.close();
+
+    // Keep all but executable
+    cmd.resize(1);
+
+    // Specify response file
+    cmd.emplace_back(cmStrCat('@', responseFile));
+  }
+#else
+  static_cast<void>(outputFile);
+  static_cast<void>(cmd);
+#endif
+}
+
 bool cmQtAutoMocUicT::JobT::RunProcess(GenT genType,
                                        cmWorkerPool::ProcessResultT& result,
                                        std::vector<std::string> const& command,
@@ -836,6 +889,8 @@
       cm::append(cmd, this->MocConst().OptionsDefinitions);
       // Add includes
       cm::append(cmd, this->MocConst().OptionsIncludes);
+      // Check if response file is necessary
+      MaybeWriteResponseFile(this->MocConst().PredefsFileAbs, cmd);
       // Execute command
       if (!this->RunProcess(GenT::MOC, result, cmd, reason.get())) {
         this->LogCommandError(GenT::MOC,
@@ -2034,7 +2089,7 @@
     // Add source file
     cmd.push_back(sourceFile);
 
-    MaybeWriteMocResponseFile(outputFile, cmd);
+    MaybeWriteResponseFile(outputFile, cmd);
   }
 
   // Execute moc command
@@ -2080,51 +2135,6 @@
   }
 }
 
-/*
- * Check if command line exceeds maximum length supported by OS
- * (if on Windows) and switch to using a response file instead.
- */
-void cmQtAutoMocUicT::JobCompileMocT::MaybeWriteMocResponseFile(
-  std::string const& outputFile, std::vector<std::string>& cmd) const
-{
-#ifdef _WIN32
-  // Ensure cmd is less than CommandLineLengthMax characters
-  size_t commandLineLength = cmd.size(); // account for separating spaces
-  for (std::string const& str : cmd) {
-    commandLineLength += str.length();
-  }
-  if (commandLineLength >= CommandLineLengthMax) {
-    // Command line exceeds maximum size allowed by OS
-    // => create response file
-    std::string const responseFile = cmStrCat(outputFile, ".rsp");
-
-    cmsys::ofstream fout(responseFile.c_str());
-    if (!fout) {
-      this->LogError(
-        GenT::MOC,
-        cmStrCat("AUTOMOC was unable to create a response file at\n  ",
-                 this->MessagePath(responseFile)));
-      return;
-    }
-
-    auto it = cmd.begin();
-    while (++it != cmd.end()) {
-      fout << *it << "\n";
-    }
-    fout.close();
-
-    // Keep all but executable
-    cmd.resize(1);
-
-    // Specify response file
-    cmd.emplace_back(cmStrCat('@', responseFile));
-  }
-#else
-  static_cast<void>(outputFile);
-  static_cast<void>(cmd);
-#endif
-}
-
 void cmQtAutoMocUicT::JobCompileUicT::Process()
 {
   std::string const& sourceFile = this->Mapping->SourceFile->FileName;
@@ -2372,11 +2382,18 @@
 {
   // -- Required settings
   if (!info.GetBool("MULTI_CONFIG", this->BaseConst_.MultiConfig, true) ||
+      !info.GetBool("CROSS_CONFIG", this->BaseConst_.CrossConfig, true) ||
+      !info.GetBool("USE_BETTER_GRAPH", this->BaseConst_.UseBetterGraph,
+                    true) ||
       !info.GetUInt("QT_VERSION_MAJOR", this->BaseConst_.QtVersion.Major,
                     true) ||
       !info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor,
                     true) ||
       !info.GetUInt("PARALLEL", this->BaseConst_.ThreadCount, false) ||
+#ifdef _WIN32
+      !info.GetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
+                    this->BaseConst_.MaxCommandLineLength, false) ||
+#endif
       !info.GetString("BUILD_DIR", this->BaseConst_.AutogenBuildDir, true) ||
       !info.GetStringConfig("INCLUDE_DIR", this->BaseConst_.AutogenIncludeDir,
                             true) ||
@@ -2384,19 +2401,49 @@
                       true) ||
       !info.GetStringConfig("PARSE_CACHE_FILE",
                             this->BaseConst_.ParseCacheFile, true) ||
-      !info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) ||
-      !info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName,
-                      false) ||
       !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
       !info.GetArray("CMAKE_LIST_FILES", this->BaseConst_.ListFiles, true) ||
       !info.GetArray("HEADER_EXTENSIONS", this->BaseConst_.HeaderExtensions,
-                     true) ||
-      !info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
-                      false) ||
-      !info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
-                      false)) {
+                     true)) {
     return false;
   }
+  if (this->BaseConst().UseBetterGraph) {
+    if (!info.GetStringConfig("DEP_FILE", this->BaseConst_.DepFile, false) ||
+        !info.GetStringConfig("DEP_FILE_RULE_NAME",
+                              this->BaseConst_.DepFileRuleName, false)) {
+      return false;
+    }
+
+    if (this->BaseConst_.CrossConfig) {
+      std::string const mocExecutableWithConfig =
+        "QT_MOC_EXECUTABLE_" + this->ExecutableConfig();
+      std::string const uicExecutableWithConfig =
+        "QT_UIC_EXECUTABLE_" + this->ExecutableConfig();
+      if (!info.GetString(mocExecutableWithConfig, this->MocConst_.Executable,
+                          false) ||
+          !info.GetString(uicExecutableWithConfig, this->UicConst_.Executable,
+                          false)) {
+        return false;
+      }
+    } else {
+      if (!info.GetStringConfig("QT_MOC_EXECUTABLE",
+                                this->MocConst_.Executable, false) ||
+          !info.GetStringConfig("QT_UIC_EXECUTABLE",
+                                this->UicConst_.Executable, false)) {
+        return false;
+      }
+    }
+  } else {
+    if (!info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
+                        false) ||
+        !info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
+                        false) ||
+        !info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) ||
+        !info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName,
+                        false)) {
+      return false;
+    }
+  }
 
   // -- Checks
   if (!this->BaseConst_.CMakeExecutableTime.Load(
@@ -3063,7 +3110,8 @@
 
 } // End of unnamed namespace
 
-bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
+                    cm::string_view executableConfig)
 {
-  return cmQtAutoMocUicT().Run(infoFile, config);
+  return cmQtAutoMocUicT().Run(infoFile, config, executableConfig);
 }
diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h
index 20f9d6e..5cb4ff1 100644
--- a/Source/cmQtAutoMocUic.h
+++ b/Source/cmQtAutoMocUic.h
@@ -10,4 +10,5 @@
  * Process AUTOMOC and AUTOUIC
  * @return true on success
  */
-bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config);
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
+                    cm::string_view executableConfig);
diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index e288645..605dad5 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -35,6 +35,11 @@
 private:
   // -- Utility
   bool IsMultiConfig() const { return this->MultiConfig_; }
+  std::string const& GetGenerator() const { return this->Generator_; }
+  bool IsXcode() const
+  {
+    return this->GetGenerator().find("Xcode") != std::string::npos;
+  }
   std::string MultiConfigOutput() const;
 
   // -- Abstract processing interface
@@ -53,6 +58,9 @@
 
   // -- Config settings
   bool MultiConfig_ = false;
+  bool CrossConfig_ = false;
+  bool UseBetterGraph_ = false;
+  std::string Generator_;
   // -- Directories
   std::string AutogenBuildDir_;
   std::string IncludeDir_;
@@ -92,26 +100,57 @@
 {
   // -- Required settings
   if (!info.GetBool("MULTI_CONFIG", this->MultiConfig_, true) ||
+      !info.GetString("GENERATOR", this->Generator_, true) ||
+      !info.GetBool("CROSS_CONFIG", this->CrossConfig_, true) ||
+      !info.GetBool("USE_BETTER_GRAPH", this->UseBetterGraph_, true) ||
       !info.GetString("BUILD_DIR", this->AutogenBuildDir_, true) ||
       !info.GetStringConfig("INCLUDE_DIR", this->IncludeDir_, true) ||
-      !info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) ||
-      !info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
+      !info.GetArrayConfig("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
       !info.GetString("LOCK_FILE", this->LockFile_, true) ||
       !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
       !info.GetString("SOURCE", this->QrcFile_, true) ||
       !info.GetString("OUTPUT_CHECKSUM", this->RccPathChecksum_, true) ||
       !info.GetString("OUTPUT_NAME", this->RccFileName_, true) ||
-      !info.GetArray("OPTIONS", this->Options_, false) ||
-      !info.GetArray("INPUTS", this->Inputs_, false)) {
+      !info.GetArray("OPTIONS", this->Options_, false)) {
     return false;
   }
+  if (this->UseBetterGraph_) {
+    if (!info.GetArrayConfig("INPUTS", this->Inputs_, false)) {
+      return false;
+    }
+    if (this->CrossConfig_) {
+      std::string const rccExecutableWithConfig =
+        "RCC_EXECUTABLE_" + this->ExecutableConfig();
+      if (!info.GetString(rccExecutableWithConfig, this->RccExecutable_,
+                          true)) {
+        return false;
+      }
+    } else {
+      if (!info.GetStringConfig("RCC_EXECUTABLE", this->RccExecutable_,
+                                true)) {
+        return false;
+      }
+    }
+  } else {
+    if (!info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) ||
+        !info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
+        !info.GetArray("INPUTS", this->Inputs_, false)) {
+      return false;
+    }
+  }
 
   // -- Derive information
   this->QrcFileName_ = cmSystemTools::GetFilenameName(this->QrcFile_);
   this->QrcFileDir_ = cmSystemTools::GetFilenamePath(this->QrcFile_);
-  this->RccFilePublic_ =
-    cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
-             this->RccFileName_);
+  if (IsMultiConfig() && !this->IsXcode() && this->UseBetterGraph_) {
+    this->RccFilePublic_ =
+      cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, "_",
+               this->InfoConfig(), '/', this->RccFileName_);
+  } else {
+    this->RccFilePublic_ =
+      cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
+               this->RccFileName_);
+  }
 
   // rcc output file name
   if (this->IsMultiConfig()) {
@@ -520,7 +559,8 @@
 
 } // End of unnamed namespace
 
-bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
+                 cm::string_view executableConfig)
 {
-  return cmQtAutoRccT().Run(infoFile, config);
+  return cmQtAutoRccT().Run(infoFile, config, executableConfig);
 }
diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h
index d525efa..9c0a4e9 100644
--- a/Source/cmQtAutoRcc.h
+++ b/Source/cmQtAutoRcc.h
@@ -10,4 +10,5 @@
  * Process AUTORCC
  * @return true on success
  */
-bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config);
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
+                 cm::string_view executableConfig);
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index f48330d..3934a29 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -3,7 +3,6 @@
 #include "cmRST.h"
 
 #include <algorithm>
-#include <cctype>
 #include <cstddef>
 #include <iterator>
 #include <utility>
@@ -159,7 +158,7 @@
   // A line starting in .. is an explicit markup start.
   if (line == ".." ||
       (line.size() >= 3 && line[0] == '.' && line[1] == '.' &&
-       isspace(line[2]))) {
+       cmIsSpace(line[2]))) {
     this->Reset();
     this->MarkupType =
       (line.find_first_not_of(" \t", 2) == std::string::npos ? Markup::Empty
@@ -219,7 +218,7 @@
   }
   // Indented lines following an explicit markup start are explicit markup.
   else if (this->MarkupType != Markup::None &&
-           (line.empty() || isspace(line[0]))) {
+           (line.empty() || cmIsSpace(line[0]))) {
     this->MarkupType = Markup::Normal;
     // Record markup lines if the start line was recorded.
     if (!this->MarkupLines.empty()) {
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 638bb42..a8c81d0 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -27,6 +27,19 @@
       return this->ReplaceValues->LinkFlags;
     }
   }
+  if (this->ReplaceValues->Linker) {
+    if (variable == "CMAKE_LINKER") {
+      auto result = this->OutputConverter->ConvertToOutputForExisting(
+        this->ReplaceValues->Linker);
+      if (this->ReplaceValues->Launcher) {
+        // Add launcher as part of expansion so that it always appears
+        // immediately before the command itself, regardless of whether the
+        // overall rule template contains other content at the front.
+        result = cmStrCat(this->ReplaceValues->Launcher, " ", result);
+      }
+      return result;
+    }
+  }
   if (this->ReplaceValues->Manifests) {
     if (variable == "MANIFESTS") {
       return this->ReplaceValues->Manifests;
@@ -325,17 +338,7 @@
   auto mapIt = this->VariableMappings.find(variable);
   if (mapIt != this->VariableMappings.end()) {
     if (variable.find("_FLAG") == std::string::npos) {
-      std::string ret =
-        this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
-
-      if (this->ReplaceValues->Launcher && variable == "CMAKE_LINKER") {
-        // Add launcher as part of expansion so that it always appears
-        // immediately before the command itself, regardless of whether the
-        // overall rule template contains other content at the front.
-        ret = cmStrCat(this->ReplaceValues->Launcher, " ", ret);
-      }
-
-      return ret;
+      return this->OutputConverter->ConvertToOutputForExisting(mapIt->second);
     }
     return mapIt->second;
   }
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 5d1f199..225abd4 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -53,6 +53,7 @@
     const char* SONameFlag = nullptr;
     const char* TargetSOName = nullptr;
     const char* TargetInstallNameDir = nullptr;
+    const char* Linker = nullptr;
     const char* LinkFlags = nullptr;
     const char* Manifests = nullptr;
     const char* LanguageCompileFlags = nullptr;
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 4a9840b..55a1e46 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -44,7 +44,10 @@
 /** Returns true if the character @a ch is a whitespace character.  **/
 inline bool cmIsSpace(char ch)
 {
-  return ((ch & 0x80) == 0) && std::isspace(ch);
+  // isspace takes 'int' but documents that the value must be representable
+  // by 'unsigned char', or be EOF.  Cast to 'unsigned char' to avoid sign
+  // extension while converting to 'int'.
+  return std::isspace(static_cast<unsigned char>(ch));
 }
 
 /** Returns a string that has whitespace removed from the start and the end. */
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 5a64588..ab87f34 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -6,7 +6,6 @@
 #include "cmStringCommand.h"
 
 #include <algorithm>
-#include <cctype>
 #include <cstdio>
 #include <cstdlib>
 #include <limits>
@@ -660,7 +659,7 @@
   const char* ptr = stringValue.c_str();
   size_t cc;
   for (cc = 0; cc < inStringLength; ++cc) {
-    if (!isspace(*ptr)) {
+    if (!cmIsSpace(*ptr)) {
       if (startPos > inStringLength) {
         startPos = cc;
       }
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 2bdc928..3b70543 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -31,6 +31,9 @@
 #include "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -59,12 +62,14 @@
 #include <cassert>
 #include <cctype>
 #include <cerrno>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
 #include <functional>
 #include <iostream>
+#include <memory>
 #include <sstream>
 #include <utility>
 #include <vector>
@@ -479,7 +484,7 @@
   const char* c = command.c_str();
 
   // Skip leading whitespace.
-  while (isspace(static_cast<unsigned char>(*c))) {
+  while (cmIsSpace(*c)) {
     ++c;
   }
 
@@ -509,7 +514,7 @@
       in_double = true;
     } else if (*c == '\'') {
       in_single = true;
-    } else if (isspace(static_cast<unsigned char>(*c))) {
+    } else if (cmIsSpace(*c)) {
       break;
     } else {
       program += *c;
@@ -568,85 +573,111 @@
                                      const char* dir, OutputOption outputflag,
                                      cmDuration timeout, Encoding encoding)
 {
-  std::vector<const char*> argv;
-  argv.reserve(command.size() + 1);
-  for (std::string const& cmd : command) {
-    argv.push_back(cmd.c_str());
-  }
-  argv.push_back(nullptr);
-
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  cmUVProcessChainBuilder builder;
+  builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin)
+    .AddCommand(command);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
   }
 
   if (outputflag == OUTPUT_PASSTHROUGH) {
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
     captureStdOut = nullptr;
     captureStdErr = nullptr;
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
   } else if (outputflag == OUTPUT_MERGE ||
              (captureStdErr && captureStdErr == captureStdOut)) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
+    builder.SetMergedBuiltinStreams();
     captureStdErr = nullptr;
+  } else {
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
   assert(!captureStdErr || captureStdErr != captureStdOut);
 
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+  bool timedOut = false;
+  cm::uv_timer_ptr timer;
+  if (timeout.count()) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+  }
 
   std::vector<char> tempStdOut;
   std::vector<char> tempStdErr;
-  char* data;
-  int length;
-  int pipe;
+  cm::uv_pipe_ptr outStream;
+  bool outFinished = true;
+  cm::uv_pipe_ptr errStream;
+  bool errFinished = true;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
   if (outputflag != OUTPUT_PASSTHROUGH &&
       (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
-    while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
-           0) {
-      // Translate NULL characters in the output into valid text.
-      for (int i = 0; i < length; ++i) {
-        if (data[i] == '\0') {
-          data[i] = ' ';
-        }
+    auto startRead =
+      [&outputflag, &processOutput,
+       &chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
+               std::vector<char>& tempStd, int id,
+               void (*outputFunc)(const std::string&),
+               bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
+      if (stream < 0) {
+        return nullptr;
       }
 
-      if (pipe == cmsysProcess_Pipe_STDOUT) {
-        if (outputflag != OUTPUT_NONE) {
-          processOutput.DecodeText(data, length, strdata, 1);
-          cmSystemTools::Stdout(strdata);
-        }
-        if (captureStdOut) {
-          cm::append(tempStdOut, data, data + length);
-        }
-      } else if (pipe == cmsysProcess_Pipe_STDERR) {
-        if (outputflag != OUTPUT_NONE) {
-          processOutput.DecodeText(data, length, strdata, 2);
-          cmSystemTools::Stderr(strdata);
-        }
-        if (captureStdErr) {
-          cm::append(tempStdErr, data, data + length);
-        }
-      }
-    }
+      pipe.init(chain.GetLoop(), 0);
+      uv_pipe_open(pipe, stream);
 
-    if (outputflag != OUTPUT_NONE) {
-      processOutput.DecodeText(std::string(), strdata, 1);
-      if (!strdata.empty()) {
-        cmSystemTools::Stdout(strdata);
-      }
-      processOutput.DecodeText(std::string(), strdata, 2);
-      if (!strdata.empty()) {
-        cmSystemTools::Stderr(strdata);
-      }
+      finished = false;
+      return cmUVStreamRead(
+        pipe,
+        [outputflag, &processOutput, captureStd, &tempStd, id,
+         outputFunc](std::vector<char> data) {
+          // Translate NULL characters in the output into valid text.
+          for (auto& c : data) {
+            if (c == '\0') {
+              c = ' ';
+            }
+          }
+
+          if (outputflag != OUTPUT_NONE) {
+            std::string strdata;
+            processOutput.DecodeText(data.data(), data.size(), strdata, id);
+            outputFunc(strdata);
+          }
+          if (captureStd) {
+            cm::append(tempStd, data.data(), data.data() + data.size());
+          }
+        },
+        [&finished, outputflag, &processOutput, id, outputFunc]() {
+          finished = true;
+          if (outputflag != OUTPUT_NONE) {
+            std::string strdata;
+            processOutput.DecodeText(std::string(), strdata, id);
+            if (!strdata.empty()) {
+              outputFunc(strdata);
+            }
+          }
+        });
+    };
+
+    outputHandle =
+      startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
+                cmSystemTools::Stdout, outFinished);
+    if (chain.OutputStream() != chain.ErrorStream()) {
+      errorHandle =
+        startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
+                  cmSystemTools::Stderr, errFinished);
     }
   }
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
 
   if (captureStdOut) {
     captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
@@ -658,37 +689,7 @@
   }
 
   bool result = true;
-  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-    } else {
-      if (cmsysProcess_GetExitValue(cp) != 0) {
-        result = false;
-      }
-    }
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
-    const char* exception_str = cmsysProcess_GetExceptionString(cp);
-    if (outputflag != OUTPUT_NONE) {
-      std::cerr << exception_str << std::endl;
-    }
-    if (captureStdErr) {
-      captureStdErr->append(exception_str, strlen(exception_str));
-    } else if (captureStdOut) {
-      captureStdOut->append(exception_str, strlen(exception_str));
-    }
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
-    const char* error_str = cmsysProcess_GetErrorString(cp);
-    if (outputflag != OUTPUT_NONE) {
-      std::cerr << error_str << std::endl;
-    }
-    if (captureStdErr) {
-      captureStdErr->append(error_str, strlen(error_str));
-    } else if (captureStdOut) {
-      captureStdOut->append(error_str, strlen(error_str));
-    }
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
+  if (timedOut) {
     const char* error_str = "Process terminated due to timeout\n";
     if (outputflag != OUTPUT_NONE) {
       std::cerr << error_str << std::endl;
@@ -697,9 +698,34 @@
       captureStdErr->append(error_str, strlen(error_str));
     }
     result = false;
+  } else {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+        } else {
+          if (status.ExitStatus != 0) {
+            result = false;
+          }
+        }
+        break;
+      default: {
+        if (outputflag != OUTPUT_NONE) {
+          std::cerr << exception.second << std::endl;
+        }
+        if (captureStdErr) {
+          captureStdErr->append(exception.second);
+        } else if (captureStdOut) {
+          captureStdOut->append(exception.second);
+        }
+        result = false;
+      } break;
+    }
   }
 
-  cmsysProcess_Delete(cp);
   return result;
 }
 
@@ -1641,7 +1667,7 @@
 
 void cmSystemTools::EnvDiff::UnPutEnv(const std::string& env)
 {
-  diff[env] = {};
+  diff[env] = cm::nullopt;
 }
 
 bool cmSystemTools::EnvDiff::ParseOperation(const std::string& envmod)
@@ -1696,7 +1722,7 @@
   } else if (op == "set"_s) {
     diff[name] = value;
   } else if (op == "unset"_s) {
-    diff[name] = {};
+    diff[name] = cm::nullopt;
   } else if (op == "string_append"_s) {
     apply_diff(name, [&value](std::string& output) { output += value; });
   } else if (op == "string_prepend"_s) {
@@ -2213,9 +2239,10 @@
 #endif
 }
 
-int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
-                               cmDuration timeout, std::vector<char>& out,
-                               std::vector<char>& err)
+cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
+  uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
+  std::string& line, cmDuration timeout, std::vector<char>& out,
+  std::vector<char>& err)
 {
   line.clear();
   auto outiter = out.begin();
@@ -2237,7 +2264,7 @@
           line.append(out.data(), length);
         }
         out.erase(out.begin(), outiter + 1);
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
     }
 
@@ -2255,33 +2282,66 @@
           line.append(err.data(), length);
         }
         err.erase(err.begin(), erriter + 1);
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
     }
 
     // No newlines found.  Wait for more data from the process.
-    int length;
-    char* data;
-    double timeoutAsDbl = timeout.count();
-    int pipe =
-      cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
-    if (pipe == cmsysProcess_Pipe_Timeout) {
+    struct ReadData
+    {
+      uv_stream_t* Stream;
+      std::vector<char> Buffer;
+      bool Read = false;
+      bool Finished = false;
+    };
+    auto startRead =
+      [](uv_stream_t* stream,
+         ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
+      data.Stream = stream;
+      return cmUVStreamRead(
+        stream,
+        [&data](std::vector<char> buf) {
+          data.Buffer = std::move(buf);
+          data.Read = true;
+          uv_read_stop(data.Stream);
+        },
+        [&data]() { data.Finished = true; });
+    };
+    ReadData outData;
+    auto outHandle = startRead(outPipe, outData);
+    ReadData errData;
+    auto errHandle = startRead(errPipe, errData);
+
+    cm::uv_timer_ptr timer;
+    bool timedOut = false;
+    timer.init(*loop, &timedOut);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timedOutPtr = static_cast<bool*>(handle->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+
+    uv_run(loop, UV_RUN_ONCE);
+    if (timedOut) {
       // Timeout has been exceeded.
-      return pipe;
+      return WaitForLineResult::Timeout;
     }
-    if (pipe == cmsysProcess_Pipe_STDOUT) {
-      processOutput.DecodeText(data, length, strdata, 1);
+    if (outData.Read) {
+      processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
+                               strdata, 1);
       // Append to the stdout buffer.
       std::vector<char>::size_type size = out.size();
       cm::append(out, strdata);
       outiter = out.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 2);
+    } else if (errData.Read) {
+      processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
+                               strdata, 2);
       // Append to the stderr buffer.
       std::vector<char>::size_type size = err.size();
       cm::append(err, strdata);
       erriter = err.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_None) {
+    } else if (outData.Finished && errData.Finished) {
       // Both stdout and stderr pipes have broken.  Return leftover data.
       processOutput.DecodeText(std::string(), strdata, 1);
       if (!strdata.empty()) {
@@ -2298,56 +2358,49 @@
       if (!out.empty()) {
         line.append(out.data(), outiter - out.begin());
         out.erase(out.begin(), out.end());
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
       if (!err.empty()) {
         line.append(err.data(), erriter - err.begin());
         err.erase(err.begin(), err.end());
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
-      return cmsysProcess_Pipe_None;
+      return WaitForLineResult::None;
+    }
+    if (!outData.Finished) {
+      uv_read_stop(outPipe);
+    }
+    if (!errData.Finished) {
+      uv_read_stop(errPipe);
     }
   }
 }
 
 #ifdef _WIN32
-static void EnsureStdPipe(DWORD fd)
+static void EnsureStdPipe(int stdFd, DWORD nStdHandle, FILE* stream,
+                          const wchar_t* mode)
 {
-  if (GetStdHandle(fd) != INVALID_HANDLE_VALUE) {
+  if (fileno(stream) >= 0) {
     return;
   }
-  SECURITY_ATTRIBUTES sa;
-  sa.nLength = sizeof(sa);
-  sa.lpSecurityDescriptor = nullptr;
-  sa.bInheritHandle = TRUE;
-
-  HANDLE h = CreateFileW(
-    L"NUL",
-    fd == STD_INPUT_HANDLE ? FILE_GENERIC_READ
-                           : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
-    FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, nullptr);
-
-  if (h == INVALID_HANDLE_VALUE) {
-    LPSTR message = nullptr;
-    DWORD size = FormatMessageA(
-      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
-        FORMAT_MESSAGE_IGNORE_INSERTS,
-      nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-      (LPSTR)&message, 0, nullptr);
-    std::string msg = std::string(message, size);
-    LocalFree(message);
-    std::cerr << "failed to open NUL for missing stdio pipe: " << msg;
+  _close(stdFd);
+  _wfreopen(L"NUL", mode, stream);
+  int fd = fileno(stream);
+  if (fd < 0) {
+    perror("failed to open NUL for missing stdio pipe");
     abort();
   }
-
-  SetStdHandle(fd, h);
+  if (fd != stdFd) {
+    _dup2(fd, stdFd);
+  }
+  SetStdHandle(nStdHandle, reinterpret_cast<HANDLE>(_get_osfhandle(fd)));
 }
 
 void cmSystemTools::EnsureStdPipes()
 {
-  EnsureStdPipe(STD_INPUT_HANDLE);
-  EnsureStdPipe(STD_OUTPUT_HANDLE);
-  EnsureStdPipe(STD_ERROR_HANDLE);
+  EnsureStdPipe(0, STD_INPUT_HANDLE, stdin, L"rb");
+  EnsureStdPipe(1, STD_OUTPUT_HANDLE, stdout, L"wb");
+  EnsureStdPipe(2, STD_ERROR_HANDLE, stderr, L"wb");
 }
 #else
 static void EnsureStdPipe(int fd)
@@ -3644,6 +3697,10 @@
 {
 #if defined(_WIN32)
   return "Windows";
+#elif defined(__MSYS__)
+  return "MSYS";
+#elif defined(__CYGWIN__)
+  return "CYGWIN";
 #elif defined(__ANDROID__)
   return "Android";
 #else
@@ -3672,15 +3729,6 @@
     if (systemName.find("kFreeBSD") != cm::string_view::npos) {
       systemName = "kFreeBSD";
     }
-
-    // fix for CYGWIN and MSYS which have windows version in them
-    if (systemName.find("CYGWIN") != cm::string_view::npos) {
-      systemName = "CYGWIN";
-    }
-
-    if (systemName.find("MSYS") != cm::string_view::npos) {
-      systemName = "MSYS";
-    }
     return systemName;
   }
   return "";
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 9563fd6..7e33e58 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -18,7 +18,8 @@
 #include <cm/optional>
 #include <cm/string_view>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
+
 #include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
 
@@ -339,10 +340,20 @@
    */
   static void ReportLastSystemError(const char* m);
 
-  /** a general output handler for cmsysProcess  */
-  static int WaitForLine(cmsysProcess* process, std::string& line,
-                         cmDuration timeout, std::vector<char>& out,
-                         std::vector<char>& err);
+  enum class WaitForLineResult
+  {
+    None,
+    STDOUT,
+    STDERR,
+    Timeout,
+  };
+
+  /** a general output handler for libuv  */
+  static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe,
+                                       uv_stream_t* errPipe, std::string& line,
+                                       cmDuration timeout,
+                                       std::vector<char>& out,
+                                       std::vector<char>& err);
 
   static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }
   static bool GetForceUnixPaths() { return s_ForceUnixPaths; }
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index abbf29e..e6e1ac4 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -8,6 +8,7 @@
 #include <map>
 #include <set>
 #include <sstream>
+#include <unordered_map>
 #include <unordered_set>
 
 #include <cm/memory>
@@ -20,6 +21,7 @@
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmFileSet.h"
+#include "cmFindPackageStack.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -438,6 +440,7 @@
   // ---- Swift
   { "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
   { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
+  { "Swift_COMPILATION_MODE"_s, IC::CanCompileSources },
   // ---- moc
   { "AUTOMOC"_s, IC::CanCompileSources },
   { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
@@ -456,6 +459,7 @@
   { "AUTORCC_EXECUTABLE"_s, IC::CanCompileSources },
 
   // Linking properties
+  { "LINKER_TYPE"_s, IC::CanCompileSources },
   { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
   { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
   { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
@@ -549,9 +553,11 @@
   { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources },
   { "ANDROID_SKIP_ANT_STEP"_s, IC::CanCompileSources },
   // -- Autogen
+  { "AUTOGEN_COMMAND_LINE_LENGTH_MAX"_s, IC::CanCompileSources },
   { "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources },
   { "AUTOGEN_PARALLEL"_s, IC::CanCompileSources },
   { "AUTOGEN_USE_SYSTEM_INCLUDE"_s, IC::CanCompileSources },
+  { "AUTOGEN_BETTER_GRAPH_MULTI_CONFIG"_s, IC::CanCompileSources },
   // -- moc
   { "AUTOMOC_DEPEND_FILTERS"_s, IC::CanCompileSources },
   // -- C++
@@ -584,11 +590,13 @@
   // Usage requirement properties
   { "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources },
   { "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig },
+  { "EXPORT_FIND_PACKAGE_NAME"_s, IC::NormalTarget },
 
   // Metadata
   { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget },
   { "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources },
   { "FOLDER"_s },
+  { "TEST_LAUNCHER"_s, IC::ExecutableTarget },
 
   // Xcode properties
   { "XCODE_GENERATE_SCHEME"_s, IC::NeedsXcode },
@@ -660,6 +668,7 @@
     TLLCommands;
   std::map<std::string, cmFileSet> FileSets;
   cmListFileBacktrace Backtrace;
+  cmFindPackageStack FindPackageStack;
 
   UsageRequirementProperty IncludeDirectories;
   UsageRequirementProperty CompileOptions;
@@ -794,18 +803,6 @@
     }
     return true;
   }
-  if (prop == this->SelfEntries.PropertyName) {
-    impl->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      cmStrCat(this->SelfEntries.PropertyName, " property is read-only\n"));
-    return true;
-  }
-  if (prop == this->InterfaceEntries.PropertyName) {
-    impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                 cmStrCat(this->InterfaceEntries.PropertyName,
-                                          " property is read-only\n"));
-    return true;
-  }
   return false;
 }
 
@@ -960,6 +957,9 @@
 
   // Save the backtrace of target construction.
   this->impl->Backtrace = this->impl->Makefile->GetBacktrace();
+  if (this->impl->IsImported()) {
+    this->impl->FindPackageStack = this->impl->Makefile->GetFindPackageStack();
+  }
 
   if (this->IsNormal()) {
     // Initialize the INCLUDE_DIRECTORIES property based on the current value
@@ -1247,6 +1247,11 @@
   return this->impl->Backtrace;
 }
 
+cmFindPackageStack const& cmTarget::GetFindPackageStack() const
+{
+  return this->impl->FindPackageStack;
+}
+
 bool cmTarget::IsExecutableWithExports() const
 {
   return (this->GetType() == cmStateEnums::EXECUTABLE &&
@@ -1965,7 +1970,6 @@
 MAKE_PROP(CUDA_FATBIN_COMPILATION);
 MAKE_PROP(CUDA_OPTIX_COMPILATION);
 MAKE_PROP(CUDA_PTX_COMPILATION);
-MAKE_PROP(EXPORT_NAME);
 MAKE_PROP(IMPORTED);
 MAKE_PROP(IMPORTED_GLOBAL);
 MAKE_PROP(INCLUDE_DIRECTORIES);
@@ -1991,43 +1995,118 @@
 #undef MAKE_PROP
 }
 
+namespace {
+
+enum class ReadOnlyCondition
+{
+  All,
+  Imported,
+  NonImported,
+};
+
+struct ReadOnlyProperty
+{
+  ReadOnlyProperty(ReadOnlyCondition cond)
+    : Condition{ cond }
+    , Policy{} {};
+  ReadOnlyProperty(ReadOnlyCondition cond, cmPolicies::PolicyID id)
+    : Condition{ cond }
+    , Policy{ id } {};
+
+  ReadOnlyCondition Condition;
+  cm::optional<cmPolicies::PolicyID> Policy;
+
+  std::string message(const std::string& prop, cmTarget* target) const
+  {
+    std::string msg;
+    if (this->Condition == ReadOnlyCondition::All) {
+      msg = " property is read-only for target(\"";
+    } else if (this->Condition == ReadOnlyCondition::Imported) {
+      msg = " property can't be set on imported targets(\"";
+    } else if (this->Condition == ReadOnlyCondition::NonImported) {
+      msg = " property can't be set on non-imported targets(\"";
+    }
+    return cmStrCat(prop, msg, target->GetName(), "\")\n");
+  }
+
+  bool isReadOnly(const std::string& prop, cmMakefile* context,
+                  cmTarget* target) const
+  {
+    auto importedTarget = target->IsImported();
+    bool matchingCondition = true;
+    if ((!importedTarget && this->Condition == ReadOnlyCondition::Imported) ||
+        (importedTarget &&
+         this->Condition == ReadOnlyCondition::NonImported)) {
+      matchingCondition = false;
+    }
+    if (!matchingCondition) {
+      // Not read-only in this scenario
+      return false;
+    }
+
+    bool readOnly = true;
+    if (!this->Policy) {
+      // No policy associated, so is always read-only
+      context->IssueMessage(MessageType::FATAL_ERROR,
+                            this->message(prop, target));
+    } else {
+      switch (target->GetPolicyStatus(*this->Policy)) {
+        case cmPolicies::WARN:
+          context->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmPolicies::GetPolicyWarning(cmPolicies::CMP0160) + "\n" +
+              this->message(prop, target));
+          CM_FALLTHROUGH;
+        case cmPolicies::OLD:
+          readOnly = false;
+          break;
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::REQUIRED_IF_USED:
+        case cmPolicies::NEW:
+          context->IssueMessage(MessageType::FATAL_ERROR,
+                                this->message(prop, target));
+          break;
+      }
+    }
+    return readOnly;
+  }
+};
+
+bool IsSetableProperty(cmMakefile* context, cmTarget* target,
+                       const std::string& prop)
+{
+  using ROC = ReadOnlyCondition;
+  static std::unordered_map<std::string, ReadOnlyProperty> const readOnlyProps{
+    { "EXPORT_NAME", { ROC::Imported } },
+    { "HEADER_SETS", { ROC::All } },
+    { "IMPORTED_GLOBAL", { ROC::NonImported } },
+    { "INTERFACE_HEADER_SETS", { ROC::All } },
+    { "MANUALLY_ADDED_DEPENDENCIES", { ROC::All } },
+    { "NAME", { ROC::All } },
+    { "SOURCES", { ROC::Imported } },
+    { "TYPE", { ROC::All } },
+    { "ALIAS_GLOBAL", { ROC::All, cmPolicies::CMP0160 } },
+    { "BINARY_DIR", { ROC::All, cmPolicies::CMP0160 } },
+    { "CXX_MODULE_SETS", { ROC::All, cmPolicies::CMP0160 } },
+    { "IMPORTED", { ROC::All, cmPolicies::CMP0160 } },
+    { "INTERFACE_CXX_MODULE_SETS", { ROC::All, cmPolicies::CMP0160 } },
+    { "LOCATION", { ROC::All, cmPolicies::CMP0160 } },
+    { "LOCATION_CONFIG", { ROC::All, cmPolicies::CMP0160 } },
+    { "SOURCE_DIR", { ROC::All, cmPolicies::CMP0160 } }
+  };
+
+  auto it = readOnlyProps.find(prop);
+
+  if (it != readOnlyProps.end()) {
+    return !(it->second.isReadOnly(prop, context, target));
+  }
+  return true;
+}
+}
+
 void cmTarget::SetProperty(const std::string& prop, cmValue value)
 {
-  if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
-    this->impl->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "MANUALLY_ADDED_DEPENDENCIES property is read-only\n");
-    return;
-  }
-  if (prop == propNAME) {
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                       "NAME property is read-only\n");
-    return;
-  }
-  if (prop == propTYPE) {
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                       "TYPE property is read-only\n");
-    return;
-  }
-  if (prop == propEXPORT_NAME && this->IsImported()) {
-    std::ostringstream e;
-    e << "EXPORT_NAME property can't be set on imported targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
-  }
-  if (prop == propSOURCES && this->IsImported()) {
-    std::ostringstream e;
-    e << "SOURCES property can't be set on imported targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
-  }
-  if (prop == propIMPORTED_GLOBAL && !this->IsImported()) {
-    std::ostringstream e;
-    e << "IMPORTED_GLOBAL property can't be set on non-imported targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  if (!IsSetableProperty(this->impl->Makefile, this, prop)) {
     return;
   }
 
@@ -2172,40 +2251,23 @@
                               cm::optional<cmListFileBacktrace> const& bt,
                               bool asString)
 {
-  if (prop == "NAME") {
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
-                                       "NAME property is read-only\n");
-    return;
-  }
-  if (prop == "EXPORT_NAME" && this->IsImported()) {
-    std::ostringstream e;
-    e << "EXPORT_NAME property can't be set on imported targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
-  }
-  if (prop == "SOURCES" && this->IsImported()) {
-    std::ostringstream e;
-    e << "SOURCES property can't be set on imported targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  if (!IsSetableProperty(this->impl->Makefile, this, prop)) {
     return;
   }
   if (prop == "IMPORTED_GLOBAL") {
-    std::ostringstream e;
-    e << "IMPORTED_GLOBAL property can't be appended, only set on imported "
-         "targets (\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return;
+    this->impl->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("IMPORTED_GLOBAL property can't be appended, only set on "
+               "imported targets (\"",
+               this->impl->Name, "\")\n"));
   }
   if (prop == propPRECOMPILE_HEADERS &&
       this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
-    std::ostringstream e;
-    e << "PRECOMPILE_HEADERS_REUSE_FROM property is already set on target "
-         "(\""
-      << this->impl->Name << "\")\n";
-    this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    this->impl->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "PRECOMPILE_HEADERS_REUSE_FROM property is already set on target (\"",
+        this->impl->Name, "\")\n"));
     return;
   }
 
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 584856a..385dfe7 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -24,6 +24,7 @@
 
 class cmCustomCommand;
 class cmFileSet;
+class cmFindPackageStack;
 class cmGlobalGenerator;
 class cmInstallTargetGenerator;
 class cmMakefile;
@@ -239,6 +240,9 @@
   //! Get a backtrace from the creation of the target.
   cmListFileBacktrace const& GetBacktrace() const;
 
+  //! Get a find_package call stack from the creation of the target.
+  cmFindPackageStack const& GetFindPackageStack() const;
+
   void InsertInclude(BT<std::string> const& entry, bool before = false);
   void InsertCompileOption(BT<std::string> const& entry, bool before = false);
   void InsertCompileDefinition(BT<std::string> const& entry);
diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h
index 1cef888..caeb54d 100644
--- a/Source/cmTargetExport.h
+++ b/Source/cmTargetExport.h
@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
 #include <string>
 
 class cmFileSet;
@@ -37,4 +38,5 @@
   ///@}
 
   bool NamelinkOnly = false;
+  std::string XcFrameworkLocation;
 };
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index b0d9c2d..7c9969c 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -9,6 +9,7 @@
 
 cmTest::cmTest(cmMakefile* mf)
   : Backtrace(mf->GetBacktrace())
+  , PolicyStatusCMP0158(mf->GetPolicyStatus(cmPolicies::CMP0158))
 {
   this->Makefile = mf;
   this->OldStyle = true;
diff --git a/Source/cmTest.h b/Source/cmTest.h
index 8b50b87..480966a 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "cmListFileCache.h"
+#include "cmPolicies.h"
 #include "cmPropertyMap.h"
 #include "cmValue.h"
 
@@ -60,6 +61,12 @@
   bool GetOldStyle() const { return this->OldStyle; }
   void SetOldStyle(bool b) { this->OldStyle = b; }
 
+  /** Get/Set if CMP0158 policy is NEW */
+  bool GetCMP0158IsNew() const
+  {
+    return this->PolicyStatusCMP0158 == cmPolicies::NEW;
+  }
+
   /** Set/Get whether lists in command lines should be expanded. */
   bool GetCommandExpandLists() const;
   void SetCommandExpandLists(bool b);
@@ -74,4 +81,5 @@
 
   cmMakefile* Makefile;
   cmListFileBacktrace Backtrace;
+  cmPolicies::PolicyStatus PolicyStatusCMP0158;
 };
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index ca1226a..840d8cf 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -168,16 +168,32 @@
     // Use the target file on disk.
     exe = target->GetFullPath(config);
 
-    // Prepend with the emulator when cross compiling if required.
-    cmValue emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
-    if (cmNonempty(emulator)) {
-      cmList emulatorWithArgs{ *emulator };
-      std::string emulatorExe(emulatorWithArgs[0]);
-      cmSystemTools::ConvertToUnixSlashes(emulatorExe);
-      os << cmOutputConverter::EscapeForCMake(emulatorExe) << " ";
-      for (std::string const& arg : cmMakeRange(emulatorWithArgs).advance(1)) {
-        os << cmOutputConverter::EscapeForCMake(arg) << " ";
+    auto addLauncher = [this, &config, &ge, &os,
+                        target](std::string const& propertyName) {
+      cmValue launcher = target->GetProperty(propertyName);
+      if (!cmNonempty(launcher)) {
+        return;
       }
+      cmList launcherWithArgs{ ge.Parse(*launcher)->Evaluate(this->LG,
+                                                             config) };
+      if (!launcherWithArgs.empty() && !launcherWithArgs[0].empty()) {
+        std::string launcherExe(launcherWithArgs[0]);
+        cmSystemTools::ConvertToUnixSlashes(launcherExe);
+        os << cmOutputConverter::EscapeForCMake(launcherExe) << " ";
+        for (std::string const& arg :
+             cmMakeRange(launcherWithArgs).advance(1)) {
+          os << cmOutputConverter::EscapeForCMake(arg) << " ";
+        }
+      }
+    };
+
+    // Prepend with the test launcher if specified.
+    addLauncher("TEST_LAUNCHER");
+
+    // Prepend with the emulator when cross compiling if required.
+    if (!this->GetTest()->GetCMP0158IsNew() ||
+        this->LG->GetMakefile()->IsOn("CMAKE_CROSSCOMPILING")) {
+      addLauncher("CROSSCOMPILING_EMULATOR");
     }
   } else {
     // Use the command name given.
diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx
index 914172b..ffc4de9 100644
--- a/Source/cmTransformDepfile.cxx
+++ b/Source/cmTransformDepfile.cxx
@@ -16,6 +16,9 @@
 #include "cmGccDepfileReaderTypes.h"
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 namespace {
@@ -121,6 +124,10 @@
       return false;
     }
     content = *std::move(result);
+  } else {
+    lg.GetMakefile()->IssueMessage(
+      MessageType::WARNING,
+      cmStrCat("Expected depfile does not exist.\n  ", infile));
   }
 
   cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outfile));
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index 34e6a70..168f1a6 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -6,6 +6,9 @@
 #include <cassert>
 #include <cstdlib>
 #include <mutex>
+#include <utility>
+
+#include <cm/memory>
 
 #include <cm3p/uv.h>
 
@@ -44,7 +47,7 @@
   this->loop.reset();
 }
 
-uv_loop_ptr::operator uv_loop_t*()
+uv_loop_ptr::operator uv_loop_t*() const
 {
   return this->loop.get();
 }
@@ -54,6 +57,11 @@
   return this->loop.get();
 }
 
+uv_loop_t& uv_loop_ptr::operator*() const
+{
+  return *this->loop;
+}
+
 uv_loop_t* uv_loop_ptr::get() const
 {
   return this->loop.get();
@@ -97,13 +105,19 @@
 }
 
 template <typename T>
+uv_handle_ptr_base_<T>::operator bool() const
+{
+  return this->handle.get();
+}
+
+template <typename T>
 void uv_handle_ptr_base_<T>::reset()
 {
   this->handle.reset();
 }
 
 template <typename T>
-uv_handle_ptr_base_<T>::operator uv_handle_t*()
+uv_handle_ptr_base_<T>::operator uv_handle_t*() const
 {
   return reinterpret_cast<uv_handle_t*>(this->handle.get());
 }
@@ -235,6 +249,12 @@
   return uv_timer_start(*this, cb, timeout, repeat);
 }
 
+void uv_timer_ptr::stop()
+{
+  assert(this->handle);
+  uv_timer_stop(*this);
+}
+
 #ifndef CMAKE_BOOTSTRAP
 uv_tty_ptr::operator uv_stream_t*() const
 {
@@ -248,12 +268,32 @@
 }
 #endif
 
+int uv_idle_ptr::init(uv_loop_t& loop, void* data)
+{
+  this->allocate(data);
+  return uv_idle_init(&loop, *this);
+}
+
+int uv_idle_ptr::start(uv_idle_cb cb)
+{
+  assert(this->handle);
+  return uv_idle_start(*this, cb);
+}
+
+void uv_idle_ptr::stop()
+{
+  assert(this->handle);
+  uv_idle_stop(*this);
+}
+
 template class uv_handle_ptr_base_<uv_handle_t>;
 
 #define UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(NAME)                              \
   template class uv_handle_ptr_base_<uv_##NAME##_t>;                          \
   template class uv_handle_ptr_<uv_##NAME##_t>;
 
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(idle)
+
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(signal)
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe)
@@ -269,4 +309,38 @@
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
 #endif
+
+namespace {
+struct write_req : public uv_write_t
+{
+  std::weak_ptr<std::function<void(int)>> cb_;
+  write_req(std::weak_ptr<std::function<void(int)>> wcb)
+    : cb_(std::move(wcb))
+  {
+  }
+};
+
+void write_req_cb(uv_write_t* req, int status)
+{
+  // Ownership has been transferred from the event loop.
+  std::unique_ptr<write_req> self(static_cast<write_req*>(req));
+
+  // Notify the original uv_write caller if it is still interested.
+  if (auto cb = self->cb_.lock()) {
+    (*cb)(status);
+  }
+}
+}
+
+int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
+             std::weak_ptr<std::function<void(int)>> cb)
+{
+  auto req = cm::make_unique<write_req>(std::move(cb));
+  int status = uv_write(req.get(), handle, bufs, nbufs, write_req_cb);
+  if (status == 0) {
+    // Ownership has been transferred to the event loop.
+    static_cast<void>(req.release());
+  }
+  return status;
+}
 }
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
index 027d690..b8b3491 100644
--- a/Source/cmUVHandlePtr.h
+++ b/Source/cmUVHandlePtr.h
@@ -5,6 +5,7 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <type_traits>
 
@@ -61,10 +62,11 @@
    * Allow less verbose calling of uv_loop_* functions
    * @return reinterpreted handle
    */
-  operator uv_loop_t*();
+  operator uv_loop_t*() const;
 
   uv_loop_t* get() const;
   uv_loop_t* operator->() const noexcept;
+  uv_loop_t& operator*() const;
 };
 
 /***
@@ -130,6 +132,16 @@
   uv_handle_ptr_base_(std::nullptr_t) {}
   ~uv_handle_ptr_base_() { this->reset(); }
 
+#if defined(__SUNPRO_CC)
+  // The Oracle Studio compiler recognizes 'explicit operator bool()' in
+  // 'if(foo)' but not 'if(foo && ...)'.  The purpose of 'explicit' here
+  // is to avoid accidental conversion in non-boolean contexts.  Just
+  // leave it out on this compiler so we can compile valid code.
+  operator bool() const;
+#else
+  explicit operator bool() const;
+#endif
+
   /**
    * Properly close the handle if needed and sets the inner handle to nullptr
    */
@@ -139,7 +151,7 @@
    * Allow less verbose calling of uv_handle_* functions
    * @return reinterpreted handle
    */
-  operator uv_handle_t*();
+  operator uv_handle_t*() const;
 
   T* get() const;
   T* operator->() const noexcept;
@@ -194,6 +206,17 @@
   void send();
 };
 
+struct uv_idle_ptr : public uv_handle_ptr_<uv_idle_t>
+{
+  CM_INHERIT_CTOR(uv_idle_ptr, uv_handle_ptr_, <uv_idle_t>);
+
+  int init(uv_loop_t& loop, void* data = nullptr);
+
+  int start(uv_idle_cb cb);
+
+  void stop();
+};
+
 struct uv_signal_ptr : public uv_handle_ptr_<uv_signal_t>
 {
   CM_INHERIT_CTOR(uv_signal_ptr, uv_handle_ptr_, <uv_signal_t>);
@@ -229,6 +252,8 @@
   int init(uv_loop_t& loop, void* data = nullptr);
 
   int start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat);
+
+  void stop();
 };
 
 struct uv_tty_ptr : public uv_handle_ptr_<uv_tty_t>
@@ -253,6 +278,8 @@
 
 UV_HANDLE_PTR_INSTANTIATE_EXTERN(async)
 
+UV_HANDLE_PTR_INSTANTIATE_EXTERN(idle)
+
 UV_HANDLE_PTR_INSTANTIATE_EXTERN(signal)
 
 UV_HANDLE_PTR_INSTANTIATE_EXTERN(pipe)
@@ -268,4 +295,27 @@
 #  undef UV_HANDLE_PTR_INSTANTIATE_EXTERN
 
 #endif
+
+/**
+ * Wraps uv_write to add synchronous cancellation.
+ *
+ * libuv provides no way to synchronously cancel a write request.
+ * Closing a write handle will cancel its pending write request, but its
+ * callback will still be called asynchronously later with UV_ECANCELED.
+ *
+ * This wrapper provides a solution by handing ownership of the uv_write_t
+ * request object to the event loop and taking it back in the callback.
+ * Use this in combination with uv_loop_ptr to ensure the event loop
+ * runs to completion and cleans up all resources.
+ *
+ * The caller may optionally provide a callback it owns with std::shared_ptr.
+ * If the caller's lifetime ends before the write request completes, the
+ * callback can be safely deleted and will not be called.
+ *
+ * The bufs array does not need to live beyond this call, but the memory
+ * referenced by the uv_buf_t values must remain alive until the callback
+ * is made or the stream is closed.
+ */
+int uv_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs,
+             std::weak_ptr<std::function<void(int)>> cb);
 }
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
index b0f1cf1..b787f19 100644
--- a/Source/cmUVProcessChain.cxx
+++ b/Source/cmUVProcessChain.cxx
@@ -40,7 +40,8 @@
 
   bool Valid = false;
 
-  cm::uv_loop_ptr Loop;
+  cm::uv_loop_ptr BuiltinLoop;
+  uv_loop_t* Loop;
 
   StreamData InputStreamData;
   StreamData OutputStreamData;
@@ -71,6 +72,19 @@
   return *this;
 }
 
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinLoop()
+{
+  this->Loop = nullptr;
+  return *this;
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalLoop(
+  uv_loop_t& loop)
+{
+  this->Loop = &loop;
+  return *this;
+}
+
 cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
 {
   switch (stdio) {
@@ -142,6 +156,11 @@
   return *this;
 }
 
+uv_loop_t* cmUVProcessChainBuilder::GetLoop() const
+{
+  return this->Loop;
+}
+
 cmUVProcessChain cmUVProcessChainBuilder::Start() const
 {
   cmUVProcessChain chain;
@@ -165,6 +184,13 @@
 {
   this->Builder = builder;
 
+  if (this->Builder->Loop) {
+    this->Loop = this->Builder->Loop;
+  } else {
+    this->BuiltinLoop.init();
+    this->Loop = this->BuiltinLoop;
+  }
+
   auto const& input =
     this->Builder->Stdio[cmUVProcessChainBuilder::Stream_INPUT];
   auto& inputData = this->InputStreamData;
@@ -365,7 +391,6 @@
 cmUVProcessChain::cmUVProcessChain()
   : Data(cm::make_unique<InternalData>())
 {
-  this->Data->Loop.init();
 }
 
 cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
index aa63ba1..65816e2 100644
--- a/Source/cmUVProcessChain.h
+++ b/Source/cmUVProcessChain.h
@@ -31,6 +31,8 @@
 
   cmUVProcessChainBuilder& AddCommand(
     const std::vector<std::string>& arguments);
+  cmUVProcessChainBuilder& SetBuiltinLoop();
+  cmUVProcessChainBuilder& SetExternalLoop(uv_loop_t& loop);
   cmUVProcessChainBuilder& SetNoStream(Stream stdio);
   cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio);
   cmUVProcessChainBuilder& SetMergedBuiltinStreams();
@@ -38,6 +40,8 @@
   cmUVProcessChainBuilder& SetExternalStream(Stream stdio, FILE* stream);
   cmUVProcessChainBuilder& SetWorkingDirectory(std::string dir);
 
+  uv_loop_t* GetLoop() const;
+
   cmUVProcessChain Start() const;
 
 private:
@@ -65,6 +69,7 @@
   std::vector<ProcessConfiguration> Processes;
   std::string WorkingDirectory;
   bool MergedBuiltinStreams = false;
+  uv_loop_t* Loop = nullptr;
 };
 
 class cmUVProcessChain
diff --git a/Source/cmUVSignalHackRAII.h b/Source/cmUVSignalHackRAII.h
deleted file mode 100644
index 60e4ca8..0000000
--- a/Source/cmUVSignalHackRAII.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#pragma once
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <cm3p/uv.h>
-
-#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) &&                    \
-  UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19
-#  define CMAKE_UV_SIGNAL_HACK
-#  include "cmUVHandlePtr.h"
-/*
-   libuv does not use SA_RESTART on its signal handler, but C++ streams
-   depend on it for reliable i/o operations.  This RAII helper convinces
-   libuv to install its handler, and then revises the handler to add the
-   SA_RESTART flag.  We use a distinct uv loop that never runs to avoid
-   ever really getting a callback.  libuv may fill the hack loop's signal
-   pipe and then stop writing, but that won't break any real loops.
- */
-class cmUVSignalHackRAII
-{
-  uv_loop_t HackLoop;
-  cm::uv_signal_ptr HackSignal;
-  static void HackCB(uv_signal_t*, int) {}
-
-public:
-  cmUVSignalHackRAII()
-  {
-    uv_loop_init(&this->HackLoop);
-    this->HackSignal.init(this->HackLoop);
-    this->HackSignal.start(HackCB, SIGCHLD);
-    struct sigaction hack_sa;
-    sigaction(SIGCHLD, nullptr, &hack_sa);
-    if (!(hack_sa.sa_flags & SA_RESTART)) {
-      hack_sa.sa_flags |= SA_RESTART;
-      sigaction(SIGCHLD, &hack_sa, nullptr);
-    }
-  }
-  ~cmUVSignalHackRAII()
-  {
-    this->HackSignal.stop();
-    uv_loop_close(&this->HackLoop);
-  }
-};
-#endif
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 708cbaf..d572f30 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -2994,6 +2994,16 @@
       e1.WritePlatformConfigTag(
         "IntDir", cond, R"($(Platform)\$(Configuration)\$(ProjectName)\)");
     } else {
+      if (ttype == cmStateEnums::SHARED_LIBRARY ||
+          ttype == cmStateEnums::MODULE_LIBRARY ||
+          ttype == cmStateEnums::EXECUTABLE) {
+        auto linker = this->GeneratorTarget->GetLinkerTool(config);
+        if (!linker.empty()) {
+          ConvertToWindowsSlash(linker);
+          e1.WritePlatformConfigTag("LinkToolExe", cond, linker);
+        }
+      }
+
       std::string intermediateDir = cmStrCat(
         this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget), '/',
         config, '/');
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index 6188134..9dd2e6c 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -137,14 +137,18 @@
 bool cmVisualStudioGeneratorOptions::UsingUnicode() const
 {
   // Look for a _UNICODE definition.
-  return std::any_of(this->Defines.begin(), this->Defines.end(),
-                     [](std::string const& di) { return di == "_UNICODE"_s; });
+  return std::any_of(
+    this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
+      return di == "_UNICODE"_s || cmHasLiteralPrefix(di, "_UNICODE=");
+    });
 }
 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
 {
   // Look for a _SBCS definition.
-  return std::any_of(this->Defines.begin(), this->Defines.end(),
-                     [](std::string const& di) { return di == "_SBCS"_s; });
+  return std::any_of(
+    this->Defines.begin(), this->Defines.end(), [](std::string const& di) {
+      return di == "_SBCS"_s || cmHasLiteralPrefix(di, "_SBCS=");
+    });
 }
 
 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index e80d1fc..6b454d7 100644
--- a/Source/cmWhileCommand.cxx
+++ b/Source/cmWhileCommand.cxx
@@ -105,6 +105,10 @@
       if (status.GetContinueInvoked()) {
         break;
       }
+      if (status.HasExitCode()) {
+        inStatus.SetExitCode(status.GetExitCode());
+        return true;
+      }
       if (cmSystemTools::GetFatalErrorOccurred()) {
         return true;
       }
diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx
index 27cdbba..dd8f459 100644
--- a/Source/cmWorkerPool.cxx
+++ b/Source/cmWorkerPool.cxx
@@ -18,7 +18,6 @@
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
 
 /**
  * @brief libuv pipe buffer class
@@ -516,9 +515,6 @@
   static void UVSlotEnd(uv_async_t* handle);
 
   // -- UV loop
-#ifdef CMAKE_UV_SIGNAL_HACK
-  std::unique_ptr<cmUVSignalHackRAII> UVHackRAII;
-#endif
   std::unique_ptr<uv_loop_t> UVLoop;
   cm::uv_async_ptr UVRequestBegin;
   cm::uv_async_ptr UVRequestEnd;
@@ -563,9 +559,6 @@
 {
   // Initialize libuv loop
   uv_disable_stdio_inheritance();
-#ifdef CMAKE_UV_SIGNAL_HACK
-  UVHackRAII = cm::make_unique<cmUVSignalHackRAII>();
-#endif
   this->UVLoop = cm::make_unique<uv_loop_t>();
   uv_loop_init(this->UVLoop.get());
 }
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx
index 80327e4..8c067ef 100644
--- a/Source/cmXCodeScheme.cxx
+++ b/Source/cmXCodeScheme.cxx
@@ -77,7 +77,7 @@
   WriteBuildAction(xout, container);
   WriteTestAction(xout, FindConfiguration("Debug"), container);
   WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container);
-  WriteProfileAction(xout, FindConfiguration("Release"));
+  WriteProfileAction(xout, FindConfiguration("Release"), container);
   WriteAnalyzeAction(xout, FindConfiguration("Debug"));
   WriteArchiveAction(xout, FindConfiguration("Release"));
 
@@ -240,18 +240,13 @@
   // Diagnostics tab end
 
   if (IsExecutable(this->Target)) {
-    xout.StartElement("BuildableProductRunnable");
-    xout.BreakAttributes();
-    xout.Attribute("runnableDebuggingMode", "0");
-
+    WriteBuildableProductRunnable(xout, this->Target, container);
   } else {
     xout.StartElement("MacroExpansion");
+    WriteBuildableReference(xout, this->Target, container);
+    xout.EndElement();
   }
 
-  WriteBuildableReference(xout, this->Target, container);
-
-  xout.EndElement(); // MacroExpansion
-
   // Info tab begin
 
   if (cmValue exe =
@@ -404,7 +399,8 @@
 }
 
 void cmXCodeScheme::WriteProfileAction(cmXMLWriter& xout,
-                                       const std::string& configuration)
+                                       const std::string& configuration,
+                                       const std::string& container)
 {
   xout.StartElement("ProfileAction");
   xout.BreakAttributes();
@@ -415,6 +411,11 @@
   WriteLaunchActionBooleanAttribute(xout, "debugDocumentVersioning",
                                     "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING",
                                     true);
+
+  if (IsExecutable(this->Target)) {
+    WriteBuildableProductRunnable(xout, this->Target, container);
+  }
+
   xout.EndElement();
 }
 
@@ -437,6 +438,17 @@
   xout.EndElement();
 }
 
+void cmXCodeScheme::WriteBuildableProductRunnable(cmXMLWriter& xout,
+                                                  const cmXCodeObject* xcObj,
+                                                  const std::string& container)
+{
+  xout.StartElement("BuildableProductRunnable");
+  xout.BreakAttributes();
+  xout.Attribute("runnableDebuggingMode", "0");
+  WriteBuildableReference(xout, xcObj, container);
+  xout.EndElement();
+}
+
 void cmXCodeScheme::WriteBuildableReference(cmXMLWriter& xout,
                                             const cmXCodeObject* xcObj,
                                             const std::string& container)
diff --git a/Source/cmXCodeScheme.h b/Source/cmXCodeScheme.h
index 07fdedb..c933a6e 100644
--- a/Source/cmXCodeScheme.h
+++ b/Source/cmXCodeScheme.h
@@ -57,10 +57,14 @@
                                          const std::string& value,
                                          const std::string& varName);
 
-  void WriteProfileAction(cmXMLWriter& xout, const std::string& configuration);
+  void WriteProfileAction(cmXMLWriter& xout, const std::string& configuration,
+                          const std::string& container);
   void WriteAnalyzeAction(cmXMLWriter& xout, const std::string& configuration);
   void WriteArchiveAction(cmXMLWriter& xout, const std::string& configuration);
 
+  void WriteBuildableProductRunnable(cmXMLWriter& xout,
+                                     const cmXCodeObject* xcObj,
+                                     const std::string& container);
   void WriteBuildableReference(cmXMLWriter& xout, const cmXCodeObject* xcObj,
                                const std::string& container);
 
diff --git a/Source/cmXMLParser.cxx b/Source/cmXMLParser.cxx
index 24da8c6..f4433e3 100644
--- a/Source/cmXMLParser.cxx
+++ b/Source/cmXMLParser.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmXMLParser.h"
 
-#include <cctype>
 #include <cstring>
 #include <iostream>
 #include <sstream>
@@ -143,11 +142,6 @@
 {
 }
 
-int cmXMLParser::IsSpace(char c)
-{
-  return isspace(c);
-}
-
 const char* cmXMLParser::FindAttribute(const char** atts,
                                        const char* attribute)
 {
diff --git a/Source/cmXMLParser.h b/Source/cmXMLParser.h
index 176252d..d35e44f 100644
--- a/Source/cmXMLParser.h
+++ b/Source/cmXMLParser.h
@@ -89,10 +89,6 @@
   /** Called by ReportXmlParseError with basic error info.  */
   virtual void ReportError(int line, int column, const char* msg);
 
-  //! Utility for convenience of subclasses.  Wraps isspace C library
-  // routine.
-  static int IsSpace(char c);
-
   //! Send the given buffer to the XML parser.
   virtual int ParseBuffer(const char* buffer, std::string::size_type length);
 
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 636a0da..88fac8d 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1575,14 +1575,15 @@
     if (!expandedPreset->ArchitectureStrategy ||
         expandedPreset->ArchitectureStrategy ==
           cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
-      if (!this->GeneratorPlatformSet) {
+      if (!this->GeneratorPlatformSet &&
+          !expandedPreset->Architecture.empty()) {
         this->SetGeneratorPlatform(expandedPreset->Architecture);
       }
     }
     if (!expandedPreset->ToolsetStrategy ||
         expandedPreset->ToolsetStrategy ==
           cmCMakePresetsGraph::ArchToolsetStrategy::Set) {
-      if (!this->GeneratorToolsetSet) {
+      if (!this->GeneratorToolsetSet && !expandedPreset->Toolset.empty()) {
         this->SetGeneratorToolset(expandedPreset->Toolset);
       }
     }
@@ -2394,8 +2395,15 @@
   cmSystemTools::RemoveADirectory(redirectsDir);
   if (!cmSystemTools::MakeDirectory(redirectsDir)) {
     cmSystemTools::Error(
-      "Unable to (re)create the private pkgRedirects directory:\n" +
-      redirectsDir);
+      cmStrCat("Unable to (re)create the private pkgRedirects directory:\n  ",
+               redirectsDir,
+               "\n"
+               "This may be caused by not having read/write access to "
+               "the build directory.\n"
+               "Try specifying a location with read/write access like:\n"
+               "  cmake -B build\n"
+               "If using a CMake presets file, ensure that preset parameter\n"
+               "'binaryDir' expands to a writable directory.\n"));
     return -1;
   }
   this->AddCacheEntry("CMAKE_FIND_PACKAGE_REDIRECTS_DIR", redirectsDir,
@@ -2507,6 +2515,16 @@
                         "Name of generator toolset.", cmStateEnums::INTERNAL);
   }
 
+  if (!this->State->GetInitializedCacheValue("CMAKE_TEST_LAUNCHER")) {
+    cm::optional<std::string> testLauncher =
+      cmSystemTools::GetEnvVar("CMAKE_TEST_LAUNCHER");
+    if (testLauncher && !testLauncher->empty()) {
+      std::string message = "Test launcher to run tests executable.";
+      this->AddCacheEntry("CMAKE_TEST_LAUNCHER", *testLauncher, message,
+                          cmStateEnums::STRING);
+    }
+  }
+
   if (!this->State->GetInitializedCacheValue(
         "CMAKE_CROSSCOMPILING_EMULATOR")) {
     cm::optional<std::string> emulator =
@@ -2801,7 +2819,7 @@
     if (cmSystemTools::GetErrorOccurredFlag()) {
       return -1;
     }
-    return 0;
+    return this->HasScriptModeExitCode() ? this->GetScriptModeExitCode() : 0;
   }
 
   // If MAKEFLAGS are given in the environment, remove the environment
diff --git a/Source/cmake.h b/Source/cmake.h
index 58f90c9..ccf5154 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -835,7 +835,13 @@
   std::string DebuggerDapLogFile;
 #endif
 
+  cm::optional<int> ScriptModeExitCode;
+
 public:
+  bool HasScriptModeExitCode() const { return ScriptModeExitCode.has_value(); }
+  void SetScriptModeExitCode(int code) { ScriptModeExitCode = code; }
+  int GetScriptModeExitCode() const { return ScriptModeExitCode.value_or(-1); }
+
   static cmDocumentationEntry CMAKE_STANDARD_OPTIONS_TABLE[18];
 };
 
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index ced83dc..8462734 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -389,13 +389,16 @@
     }
   }
 
-  // Always return a non-negative value.  Windows tools do not always
-  // interpret negative return values as errors.
+  // Always return a non-negative value (except exit code from SCRIPT_MODE).
+  // Windows tools do not always interpret negative return values as errors.
   if (res != 0) {
+    auto scriptModeExitCode =
+      cm.HasScriptModeExitCode() ? cm.GetScriptModeExitCode() : 0;
+    res = scriptModeExitCode ? scriptModeExitCode : 1;
 #ifdef CMake_ENABLE_DEBUGGER
-    cm.StopDebuggerIfNeeded(1);
+    cm.StopDebuggerIfNeeded(res);
 #endif
-    return 1;
+    return res;
   }
 #ifdef CMake_ENABLE_DEBUGGER
   cm.StopDebuggerIfNeeded(0);
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 9d7bf50..25b2ced 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -72,7 +72,6 @@
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/Terminal.h"
 
@@ -202,11 +201,16 @@
 void cmCatFile(const std::string& fileToAppend)
 {
 #ifdef _WIN32
+  _setmode(fileno(stdin), _O_BINARY);
   _setmode(fileno(stdout), _O_BINARY);
 #endif
-  cmsys::ifstream source(fileToAppend.c_str(),
-                         (std::ios::binary | std::ios::in));
-  std::cout << source.rdbuf();
+  std::streambuf* buf = std::cin.rdbuf();
+  cmsys::ifstream source;
+  if (fileToAppend != "-") {
+    source.open(fileToAppend.c_str(), (std::ios::binary | std::ios::in));
+    buf = source.rdbuf();
+  }
+  std::cout << buf;
 }
 
 bool cmRemoveDirectory(const std::string& dir, bool recursive = true)
@@ -295,14 +299,8 @@
     }
   }
 
-  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp(
-    cmsysProcess_New(), cmsysProcess_Delete);
-  std::vector<const char*> argv(command.size() + 1);
-  std::transform(command.begin(), command.end(), argv.begin(),
-                 [](std::string const& s) { return s.c_str(); });
-  argv.back() = nullptr;
-  cmsysProcess_SetCommand(cp.get(), argv.data());
-  cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir);
 
   cmsys::ofstream fout(depFile.c_str());
   if (!fout) {
@@ -313,22 +311,18 @@
   CLOutputLogger errLogger(std::cerr);
 
   // Start the process.
-  cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
+  auto result =
+    cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
+  auto const& subStatus = result.front();
 
   int status = 0;
   // handle status of process
-  switch (cmsysProcess_GetState(cp.get())) {
-    case cmsysProcess_State_Exited:
-      status = cmsysProcess_GetExitValue(cp.get());
-      break;
-    case cmsysProcess_State_Exception:
-      status = 1;
-      break;
-    case cmsysProcess_State_Error:
-      status = 2;
-      break;
-    default:
-      break;
+  if (subStatus.SpawnResult != 0) {
+    status = 2;
+  } else if (subStatus.TermSignal != 0) {
+    status = 1;
+  } else {
+    status = static_cast<int>(subStatus.ExitStatus);
   }
 
   if (status != 0) {
@@ -1116,7 +1110,8 @@
 
       int ret = 0;
       auto time_start = std::chrono::steady_clock::now();
-      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
+      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr,
+                                      cmSystemTools::OUTPUT_PASSTHROUGH);
       auto time_finish = std::chrono::steady_clock::now();
 
       std::chrono::duration<double> time_elapsed = time_finish - time_start;
@@ -1155,7 +1150,12 @@
       int return_value = 0;
       bool doing_options = true;
       for (auto const& arg : cmMakeRange(args).advance(2)) {
-        if (doing_options && cmHasLiteralPrefix(arg, "-")) {
+        if (arg == "-") {
+          doing_options = false;
+          // Destroy console buffers to drop cout/cerr encoding transform.
+          consoleBuf.reset();
+          cmCatFile(arg);
+        } else if (doing_options && cmHasLiteralPrefix(arg, "-")) {
           if (arg == "--") {
             doing_options = false;
           } else {
@@ -1441,13 +1441,17 @@
     if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
       cm::string_view const infoFile = args[2];
       cm::string_view const config = args[3];
-      return cmQtAutoMocUic(infoFile, config) ? 0 : 1;
+      cm::string_view const executableConfig =
+        (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
+      return cmQtAutoMocUic(infoFile, config, executableConfig) ? 0 : 1;
     }
     if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
       cm::string_view const infoFile = args[2];
       cm::string_view const config =
         (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view();
-      return cmQtAutoRcc(infoFile, config) ? 0 : 1;
+      cm::string_view const executableConfig =
+        (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
+      return cmQtAutoRcc(infoFile, config, executableConfig) ? 0 : 1;
     }
 #endif
 
@@ -1890,21 +1894,6 @@
     }
   }
 
-  // Allocate a process instance.
-  cmsysProcess* cp = cmsysProcess_New();
-  if (!cp) {
-    std::cerr << "Error allocating process instance in link script."
-              << std::endl;
-    return 1;
-  }
-
-  // Children should share stdout and stderr with this process.
-  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
-
-  // Run the command lines verbatim.
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
-
   // Read command lines from the script.
   cmsys::ifstream fin(args[2].c_str());
   if (!fin) {
@@ -1922,9 +1911,21 @@
       continue;
     }
 
+    // Allocate a process instance.
+    cmUVProcessChainBuilder builder;
+
+    // Children should share stdout and stderr with this process.
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
+
     // Setup this command line.
-    const char* cmd[2] = { command.c_str(), nullptr };
-    cmsysProcess_SetCommand(cp, cmd);
+    std::vector<std::string> args2;
+#ifdef _WIN32
+    cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2);
+#else
+    cmSystemTools::ParseUnixCommandLine(command.c_str(), args2);
+#endif
+    builder.AddCommand(args2);
 
     // Report the command if verbose output is enabled.
     if (verbose) {
@@ -1932,35 +1933,29 @@
     }
 
     // Run the command and wait for it to exit.
-    cmsysProcess_Execute(cp);
-    cmsysProcess_WaitForExit(cp, nullptr);
+    auto chain = builder.Start();
+    chain.Wait();
 
     // Report failure if any.
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        int value = cmsysProcess_GetExitValue(cp);
-        if (value != 0) {
-          result = value;
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (status.ExitStatus != 0) {
+          result = static_cast<int>(status.ExitStatus);
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        std::cerr << "Error running link command: "
-                  << cmsysProcess_GetExceptionString(cp) << std::endl;
-        result = 1;
         break;
-      case cmsysProcess_State_Error:
-        std::cerr << "Error running link command: "
-                  << cmsysProcess_GetErrorString(cp) << std::endl;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        std::cerr << "Error running link command: " << exception.second;
         result = 2;
         break;
       default:
+        std::cerr << "Error running link command: " << exception.second;
+        result = 1;
         break;
     }
   }
 
-  // Free the process instance.
-  cmsysProcess_Delete(cp);
-
   // Return the final resulting return value.
   return result;
 }
diff --git a/Source/ctest.cxx b/Source/ctest.cxx
index fa38a65..c1c2071 100644
--- a/Source/ctest.cxx
+++ b/Source/ctest.cxx
@@ -25,7 +25,7 @@
 
 const cmDocumentationEntry cmDocumentationUsage = { {}, "  ctest [options]" };
 
-const cmDocumentationEntry cmDocumentationOptions[74] = {
+const cmDocumentationEntry cmDocumentationOptions[] = {
   { "--preset <preset>, --preset=<preset>",
     "Read arguments from a test preset." },
   { "--list-presets", "List available test presets." },
@@ -107,6 +107,9 @@
     "Run a specific number of tests by number." },
   { "-U, --union", "Take the Union of -I and -R" },
   { "--rerun-failed", "Run only the tests that failed previously" },
+  { "--tests-from-file <file>", "Run the tests listed in the given file" },
+  { "--exclude-from-file <file>",
+    "Run tests except those listed in the given file" },
   { "--repeat until-fail:<n>, --repeat-until-fail <n>",
     "Require each test to run <n> times without failing in order to pass" },
   { "--repeat until-pass:<n>",
@@ -143,6 +146,7 @@
   { "--tomorrow-tag", "Nightly or experimental starts with next day tag." },
   { "--overwrite", "Overwrite CTest configuration option." },
   { "--extra-submit <file>[;<file>]", "Submit extra files to the dashboard." },
+  { "--http-header <header>", "Append HTTP header when submitting" },
   { "--force-new-ctest-process",
     "Run child CTest instances as new processes" },
   { "--schedule-random", "Use a random order for scheduling tests" },
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 2b7f2cc..562d5e6 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -298,14 +298,6 @@
 set(KWSYS_HEADER_INSTALL_DIR)
 set(KWSYS_LIBRARY_INSTALL_DIR)
 
-# Generated source files will need this header.
-string(COMPARE EQUAL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}"
-  KWSYS_IN_SOURCE_BUILD)
-if(NOT KWSYS_IN_SOURCE_BUILD)
-  configure_file(${PROJECT_SOURCE_DIR}/kwsysPrivate.h
-    ${PROJECT_BINARY_DIR}/kwsysPrivate.h COPYONLY IMMEDIATE)
-endif()
-
 # Select plugin module file name convention.
 if(NOT KWSYS_DynamicLoader_PREFIX)
   set(KWSYS_DynamicLoader_PREFIX ${CMAKE_SHARED_MODULE_PREFIX})
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx
index 50171dd..61703ad 100644
--- a/Source/kwsys/CommandLineArguments.cxx
+++ b/Source/kwsys/CommandLineArguments.cxx
@@ -132,7 +132,7 @@
 
   // Does the argument match to any we know about?
   for (it = this->Internals->Callbacks.begin();
-       it != this->Internals->Callbacks.end(); it++) {
+       it != this->Internals->Callbacks.end(); ++it) {
     const CommandLineArguments::Internal::String& parg = it->first;
     CommandLineArgumentsCallbackStructure* cs = &it->second;
     if (cs->ArgumentType == CommandLineArguments::NO_ARGUMENT ||
@@ -467,7 +467,7 @@
   MapArgs mp;
   MapArgs::iterator mpit, smpit;
   for (it = this->Internals->Callbacks.begin();
-       it != this->Internals->Callbacks.end(); it++) {
+       it != this->Internals->Callbacks.end(); ++it) {
     CommandLineArgumentsCallbackStructure* cs = &(it->second);
     mpit = mp.find(cs->Help);
     if (mpit != mp.end()) {
@@ -478,14 +478,14 @@
     }
   }
   for (it = this->Internals->Callbacks.begin();
-       it != this->Internals->Callbacks.end(); it++) {
+       it != this->Internals->Callbacks.end(); ++it) {
     CommandLineArgumentsCallbackStructure* cs = &(it->second);
     mpit = mp.find(cs->Help);
     if (mpit != mp.end()) {
       mpit->second.insert(it->first);
       smpit = mp.find(it->first);
       CommandLineArguments::Internal::SetOfStrings::iterator sit;
-      for (sit = smpit->second.begin(); sit != smpit->second.end(); sit++) {
+      for (sit = smpit->second.begin(); sit != smpit->second.end(); ++sit) {
         mpit->second.insert(*sit);
       }
       mp.erase(smpit);
@@ -496,9 +496,9 @@
 
   // Find the length of the longest string
   CommandLineArguments::Internal::String::size_type maxlen = 0;
-  for (mpit = mp.begin(); mpit != mp.end(); mpit++) {
+  for (mpit = mp.begin(); mpit != mp.end(); ++mpit) {
     CommandLineArguments::Internal::SetOfStrings::iterator sit;
-    for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) {
+    for (sit = mpit->second.begin(); sit != mpit->second.end(); ++sit) {
       CommandLineArguments::Internal::String::size_type clen = sit->size();
       switch (this->Internals->Callbacks[*sit].ArgumentType) {
         case CommandLineArguments::NO_ARGUMENT:
@@ -524,9 +524,9 @@
   maxlen += 4; // For the space before and after the option
 
   // Print help for each option
-  for (mpit = mp.begin(); mpit != mp.end(); mpit++) {
+  for (mpit = mp.begin(); mpit != mp.end(); ++mpit) {
     CommandLineArguments::Internal::SetOfStrings::iterator sit;
-    for (sit = mpit->second.begin(); sit != mpit->second.end(); sit++) {
+    for (sit = mpit->second.begin(); sit != mpit->second.end(); ++sit) {
       str << std::endl;
       std::string argument = *sit;
       switch (this->Internals->Callbacks[*sit].ArgumentType) {
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index 369ff9a..37526dc 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -4890,7 +4890,7 @@
   args.reserve(3 + args_string.size());
   args.push_back("kstat");
   args.push_back("-p");
-  for (auto& i : args_string) {
+  for (const auto& i : args_string) {
     args.push_back(i.c_str());
   }
   args.push_back(nullptr);
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 3bb7869..3f714de 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -412,18 +412,6 @@
 }
 #endif
 
-#if !defined(_WIN32) && defined(__COMO__)
-// Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE.
-extern "C" {
-extern FILE* popen(__const char* __command, __const char* __modes) __THROW;
-extern int pclose(FILE* __stream) __THROW;
-extern char* realpath(__const char* __restrict __name,
-                      char* __restrict __resolved) __THROW;
-extern char* strdup(__const char* __s) __THROW;
-extern int putenv(char* __string) __THROW;
-}
-#endif
-
 namespace KWSYS_NAMESPACE {
 
 double SystemTools::GetTime()
@@ -777,12 +765,16 @@
 bool SystemTools::GetEnv(const char* key, std::string& result)
 {
 #if defined(_WIN32)
-  const std::wstring wkey = Encoding::ToWide(key);
-  const wchar_t* wv = _wgetenv(wkey.c_str());
-  if (wv) {
-    result = Encoding::ToNarrow(wv);
-    return true;
+  auto wide_key = Encoding::ToWide(key);
+  auto result_size = GetEnvironmentVariableW(wide_key.data(), nullptr, 0);
+  if (result_size <= 0) {
+    return false;
   }
+  std::wstring wide_result;
+  wide_result.resize(result_size - 1);
+  GetEnvironmentVariableW(wide_key.data(), &wide_result[0], result_size);
+  result = Encoding::ToNarrow(wide_result);
+  return true;
 #else
   const char* v = getenv(key);
   if (v) {
@@ -2802,14 +2794,14 @@
 
 Status SystemTools::RemoveADirectory(std::string const& source)
 {
-  // Add write permission to the directory so we can modify its
-  // content to remove files and directories from it.
+  // Add read and write permission to the directory so we can read
+  // and modify its content to remove files and directories from it.
   mode_t mode = 0;
   if (SystemTools::GetPermissions(source, mode)) {
 #if defined(_WIN32) && !defined(__CYGWIN__)
-    mode |= S_IWRITE;
+    mode |= S_IREAD | S_IWRITE;
 #else
-    mode |= S_IWUSR;
+    mode |= S_IRUSR | S_IWUSR;
 #endif
     SystemTools::SetPermissions(source, mode);
   }
@@ -2875,9 +2867,8 @@
   path.reserve(path.size() + userPaths.size());
   path.insert(path.end(), userPaths.begin(), userPaths.end());
   // now look for the file
-  std::string tryPath;
   for (std::string const& p : path) {
-    tryPath = p;
+    std::string tryPath = p;
     if (tryPath.empty() || tryPath.back() != '/') {
       tryPath += '/';
     }
@@ -2946,8 +2937,6 @@
                                      const std::vector<std::string>& userPaths,
                                      bool no_system_path)
 {
-  std::string tryPath;
-
 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
   std::vector<std::string> extensions;
   // check to see if the name already has a .xxx at
@@ -2959,7 +2948,7 @@
 
     // first try with extensions if the os supports them
     for (std::string const& ext : extensions) {
-      tryPath = name;
+      std::string tryPath = name;
       tryPath += ext;
       if (SystemTools::FileIsExecutable(tryPath)) {
         return SystemTools::CollapseFullPath(tryPath);
@@ -2996,7 +2985,7 @@
 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
     // first try with extensions
     for (std::string const& ext : extensions) {
-      tryPath = p;
+      std::string tryPath = p;
       tryPath += name;
       tryPath += ext;
       if (SystemTools::FileIsExecutable(tryPath)) {
@@ -3005,7 +2994,7 @@
     }
 #endif
     // now try it without them
-    tryPath = p;
+    std::string tryPath = p;
     tryPath += name;
     if (SystemTools::FileIsExecutable(tryPath)) {
       return SystemTools::CollapseFullPath(tryPath);
@@ -3160,16 +3149,13 @@
 #if defined(_WIN32)
   DWORD attr =
     GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
-  if (attr != INVALID_FILE_ATTRIBUTES) {
-    return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
+  return (attr != INVALID_FILE_ATTRIBUTES) &&
+    (attr & FILE_ATTRIBUTE_DIRECTORY);
 #else
   struct stat fs;
-  if (stat(name, &fs) == 0) {
-    return S_ISDIR(fs.st_mode);
+
+  return (stat(name, &fs) == 0) && S_ISDIR(fs.st_mode);
 #endif
-  } else {
-    return false;
-  }
 }
 
 bool SystemTools::FileIsExecutable(const std::string& inName)
@@ -3234,11 +3220,7 @@
   return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
 #else
   struct stat fs;
-  if (lstat(name.c_str(), &fs) == 0) {
-    return S_ISLNK(fs.st_mode);
-  } else {
-    return false;
-  }
+  return (lstat(name.c_str(), &fs) == 0) && S_ISLNK(fs.st_mode);
 #endif
 }
 
@@ -3256,11 +3238,7 @@
   return type == FILE_TYPE_PIPE;
 #else
   struct stat fs;
-  if (lstat(name.c_str(), &fs) == 0) {
-    return S_ISFIFO(fs.st_mode);
-  } else {
-    return false;
-  }
+  return (lstat(name.c_str(), &fs) == 0) && S_ISFIFO(fs.st_mode);
 #endif
 }
 
diff --git a/Tests/Architecture/bar.c b/Tests/Architecture/bar.c
index 37946c7..18ddb78 100644
--- a/Tests/Architecture/bar.c
+++ b/Tests/Architecture/bar.c
@@ -1,5 +1,5 @@
 extern int foo(void);
-int main()
+int main(void)
 {
   return foo();
 }
diff --git a/Tests/BuildDepends/Project/link_depends_no_shared_exe.c b/Tests/BuildDepends/Project/link_depends_no_shared_exe.c
index cfaf051..8830776 100644
--- a/Tests/BuildDepends/Project/link_depends_no_shared_exe.c
+++ b/Tests/BuildDepends/Project/link_depends_no_shared_exe.c
@@ -3,7 +3,7 @@
 __declspec(dllimport)
 #endif
   int link_depends_no_shared_lib(void);
-int main()
+int main(void)
 {
   return link_depends_no_shared_lib() + link_depends_no_shared_exe_value;
 }
diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.c b/Tests/CMakeCommands/target_compile_definitions/consumer.c
index bb65e01..08554f4 100644
--- a/Tests/CMakeCommands/target_compile_definitions/consumer.c
+++ b/Tests/CMakeCommands/target_compile_definitions/consumer.c
@@ -39,6 +39,6 @@
 #  error Expected LANG_IS_C_OR_CXX
 #endif
 
-void consumer_c()
+void consumer_c(void)
 {
 }
diff --git a/Tests/CMakeCommands/target_compile_features/main.c b/Tests/CMakeCommands/target_compile_features/main.c
index 76e98c4..60f0f9e 100644
--- a/Tests/CMakeCommands/target_compile_features/main.c
+++ b/Tests/CMakeCommands/target_compile_features/main.c
@@ -6,7 +6,7 @@
   return 0;
 }
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/CMakeCommands/target_compile_features/restrict_user.c b/Tests/CMakeCommands/target_compile_features/restrict_user.c
index 76c956f..42e3efb 100644
--- a/Tests/CMakeCommands/target_compile_features/restrict_user.c
+++ b/Tests/CMakeCommands/target_compile_features/restrict_user.c
@@ -8,7 +8,7 @@
   return foo(a, b);
 }
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/CMakeCommands/target_compile_options/consumer.c b/Tests/CMakeCommands/target_compile_options/consumer.c
index f9b6654..0984166 100644
--- a/Tests/CMakeCommands/target_compile_options/consumer.c
+++ b/Tests/CMakeCommands/target_compile_options/consumer.c
@@ -35,6 +35,6 @@
 #  endif
 #endif
 
-void consumer_c()
+void consumer_c(void)
 {
 }
diff --git a/Tests/CMakeCommands/target_include_directories/consumer.c b/Tests/CMakeCommands/target_include_directories/consumer.c
index 7fd694b..1975050 100644
--- a/Tests/CMakeCommands/target_include_directories/consumer.c
+++ b/Tests/CMakeCommands/target_include_directories/consumer.c
@@ -15,7 +15,7 @@
 #  endif
 #endif
 
-int consumer_c()
+int consumer_c(void)
 {
   return 0;
 }
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index b44c8dd..2c1d22b 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -25,6 +25,8 @@
   testXMLParser.cxx
   testXMLSafe.cxx
   testFindPackageCommand.cxx
+  testUVHandlePtr.cxx
+  testUVJobServerClient.cxx
   testUVProcessChain.cxx
   testUVRAII.cxx
   testUVStreambuf.cxx
@@ -42,6 +44,7 @@
     testDebuggerVariables.cxx
     testDebuggerVariablesHelper.cxx
     testDebuggerVariablesManager.cxx
+    testDebuggerThread.cxx
     )
 endif()
 if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM)
diff --git a/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt b/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt
index e47b9db..f3de7e1 100644
--- a/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt
+++ b/Tests/CMakeLib/PseudoMemcheck/NoLog/CMakeLists.txt
@@ -1,7 +1,7 @@
 # A dummy checker implementation that does not write the requested output file
 # so it triggers an error for every checker.
 
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in" "int main(){return 0;}\n")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in" "int main(void){return 0;}\n")
 
 configure_file(
   "${CMAKE_CURRENT_BINARY_DIR}/ret0.c.in"
diff --git a/Tests/CMakeLib/testDebuggerNamedPipe.cxx b/Tests/CMakeLib/testDebuggerNamedPipe.cxx
index 1ae3f64..67e1372 100644
--- a/Tests/CMakeLib/testDebuggerNamedPipe.cxx
+++ b/Tests/CMakeLib/testDebuggerNamedPipe.cxx
@@ -194,7 +194,7 @@
     R"("command" *: *"disconnect".*"success" *: *true.*"type" *: *"response")"
   };
 
-  for (auto& regexString : expectedResponses) {
+  for (const auto& regexString : expectedResponses) {
     cmsys::RegularExpression regex(regexString);
     if (!regex.find(debuggerResponse)) {
       std::cout << "Expected response not found: " << regexString << std::endl;
diff --git a/Tests/CMakeLib/testDebuggerThread.cxx b/Tests/CMakeLib/testDebuggerThread.cxx
new file mode 100644
index 0000000..0ea95b6
--- /dev/null
+++ b/Tests/CMakeLib/testDebuggerThread.cxx
@@ -0,0 +1,33 @@
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cm3p/cppdap/protocol.h>
+#include <cm3p/cppdap/types.h>
+
+#include "cmDebuggerThread.h"
+#include "cmListFileCache.h"
+
+#include "testCommon.h"
+
+static bool testStackFrameFunctionName()
+{
+  auto thread = std::make_shared<cmDebugger::cmDebuggerThread>(0, "name");
+  const auto* functionName = "function_name";
+  auto arguments = std::vector<cmListFileArgument>{};
+  cmListFileFunction func(functionName, 10, 20, arguments);
+  thread->PushStackFrame(nullptr, "CMakeLists.txt", func);
+
+  auto stackTrace = GetStackTraceResponse(thread);
+
+  ASSERT_TRUE(stackTrace.stackFrames[0].name == functionName);
+  return true;
+}
+
+int testDebuggerThread(int, char*[])
+{
+  return runTests(std::vector<std::function<bool()>>{
+    testStackFrameFunctionName,
+  });
+}
diff --git a/Tests/CMakeLib/testUVHandlePtr.cxx b/Tests/CMakeLib/testUVHandlePtr.cxx
new file mode 100644
index 0000000..17f672d
--- /dev/null
+++ b/Tests/CMakeLib/testUVHandlePtr.cxx
@@ -0,0 +1,153 @@
+#include <functional>
+#include <iostream>
+#include <memory>
+
+#include <cm3p/uv.h>
+
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+
+static bool testBool()
+{
+  cm::uv_async_ptr async;
+  cm::uv_handle_ptr handle;
+  cm::uv_idle_ptr idle;
+  cm::uv_pipe_ptr pipe;
+  cm::uv_process_ptr process;
+  cm::uv_signal_ptr signal;
+  cm::uv_stream_ptr stream;
+  cm::uv_timer_ptr timer;
+  cm::uv_tty_ptr tty;
+  return !async && !handle && !idle && !pipe && !process && !signal &&
+    !stream && !timer && !tty;
+}
+
+static bool testIdle()
+{
+  bool idled = false;
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+
+  auto cb = [](uv_idle_t* handle) {
+    auto idledPtr = static_cast<bool*>(handle->data);
+    *idledPtr = true;
+    uv_idle_stop(handle);
+  };
+
+  cm::uv_idle_ptr idle;
+  idle.init(*loop, &idled);
+  idle.start(cb);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (!idled) {
+    std::cerr << "uv_idle_ptr did not trigger callback" << std::endl;
+    return false;
+  }
+
+  idled = false;
+
+  idle.start(cb);
+  idle.stop();
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (idled) {
+    std::cerr << "uv_idle_ptr::stop did not stop callback" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+static bool testTimer()
+{
+  bool timed = false;
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+
+  auto cb = [](uv_timer_t* handle) {
+    auto timedPtr = static_cast<bool*>(handle->data);
+    *timedPtr = true;
+    uv_timer_stop(handle);
+  };
+
+  cm::uv_timer_ptr timer;
+  timer.init(*loop, &timed);
+  timer.start(cb, 10, 0);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (!timed) {
+    std::cerr << "uv_timer_ptr did not trigger callback" << std::endl;
+    return false;
+  }
+
+  timed = false;
+  timer.start(cb, 10, 0);
+  timer.stop();
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (timed) {
+    std::cerr << "uv_timer_ptr::stop did not stop callback" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+static bool testWriteCallback()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+
+  cm::uv_pipe_ptr pipeRead;
+  pipeRead.init(*loop, 0);
+  uv_pipe_open(pipeRead, pipe[0]);
+
+  cm::uv_pipe_ptr pipeWrite;
+  pipeWrite.init(*loop, 0);
+  uv_pipe_open(pipeWrite, pipe[1]);
+
+  char c = '.';
+  uv_buf_t buf = uv_buf_init(&c, sizeof(c));
+  int status = -1;
+  auto cb = std::make_shared<std::function<void(int)>>(
+    [&status](int s) { status = s; });
+
+  // Test getting a callback after the write is done.
+  cm::uv_write(pipeWrite, &buf, 1, cb);
+  uv_run(loop, UV_RUN_DEFAULT);
+  if (status != 0) {
+    std::cout << "cm::uv_write non-zero status: " << status << std::endl;
+    return false;
+  }
+
+  // Test deleting the callback before it is made.
+  status = -1;
+  cm::uv_write(pipeWrite, &buf, 1, cb);
+  cb.reset();
+  uv_run(loop, UV_RUN_DEFAULT);
+  if (status != -1) {
+    std::cout << "cm::uv_write callback incorrectly called with status: "
+              << status << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+int testUVHandlePtr(int, char** const)
+{
+  bool passed = true;
+  passed = testBool() && passed;
+  passed = testIdle() && passed;
+  passed = testTimer() && passed;
+  passed = testWriteCallback() && passed;
+  return passed ? 0 : -1;
+}
diff --git a/Tests/CMakeLib/testUVJobServerClient.cxx b/Tests/CMakeLib/testUVJobServerClient.cxx
new file mode 100644
index 0000000..13f0f40
--- /dev/null
+++ b/Tests/CMakeLib/testUVJobServerClient.cxx
@@ -0,0 +1,179 @@
+#include <cassert>
+#include <cstddef>
+#include <deque>
+#include <iostream>
+#include <vector>
+
+#include <cm/optional>
+
+#include <cm3p/uv.h>
+
+#ifndef _WIN32
+#  include <unistd.h>
+#endif
+
+#include "cmGetPipes.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVJobServerClient.h"
+
+namespace {
+
+const std::size_t kTOTAL_JOBS = 10;
+const std::size_t kTOTAL_TOKENS = 3;
+
+struct Job
+{
+  cm::uv_timer_ptr Timer;
+};
+
+struct JobRunner
+{
+  cm::uv_loop_ptr Loop;
+  cm::optional<cmUVJobServerClient> JSC;
+  std::vector<Job> Jobs;
+  std::size_t NextJobIndex = 0;
+
+  std::size_t ActiveJobs = 0;
+
+  std::deque<std::size_t> Queue;
+
+  bool Okay = true;
+
+  JobRunner()
+    : Jobs(kTOTAL_JOBS)
+  {
+    this->Loop.init(nullptr);
+    this->JSC = cmUVJobServerClient::Connect(
+      *this->Loop, [this]() { this->StartQueuedJob(); }, nullptr);
+    if (!this->JSC) {
+      std::cerr << "Failed to connect to job server.\n";
+      this->Okay = false;
+    }
+  }
+
+  ~JobRunner() {}
+
+  bool Run()
+  {
+    if (this->Okay) {
+      this->QueueNextJobs();
+      uv_run(this->Loop, UV_RUN_DEFAULT);
+      std::cerr << "HeldTokens: " << this->JSC->GetHeldTokens() << '\n';
+      std::cerr << "NeedTokens: " << this->JSC->GetNeedTokens() << '\n';
+    }
+#ifdef _WIN32
+    // FIXME: Windows job server client not yet implemented.
+    return true;
+#else
+    return this->Okay;
+#endif
+  }
+
+  void QueueNextJobs()
+  {
+    std::cerr << "QueueNextJobs()\n";
+    std::size_t queued = 0;
+    while (queued < 2 && this->NextJobIndex < this->Jobs.size()) {
+      this->QueueJob(this->NextJobIndex);
+      ++this->NextJobIndex;
+      ++queued;
+    }
+    std::cerr << "QueueNextJobs done\n";
+  }
+
+  void StartQueuedJob()
+  {
+    std::cerr << "StartQueuedJob()\n";
+    assert(!this->Queue.empty());
+
+    std::size_t index = this->Queue.front();
+    this->Queue.pop_front();
+    this->StartJob(index);
+
+    std::cerr << "StartQueuedJob done\n";
+  }
+
+  void StartJob(std::size_t index)
+  {
+    cm::uv_timer_ptr& job = this->Jobs[index].Timer;
+    job.init(*this->Loop, this);
+    uv_timer_start(
+      job,
+      [](uv_timer_t* handle) {
+        uv_timer_stop(handle);
+        auto self = static_cast<JobRunner*>(handle->data);
+        self->FinishJob();
+      },
+      /*timeout_ms=*/10 * (1 + (index % 3)), /*repeat_ms=*/0);
+    ++this->ActiveJobs;
+    std::cerr << "  StartJob(" << index
+              << "): Active jobs: " << this->ActiveJobs << '\n';
+
+    if (this->ActiveJobs > kTOTAL_TOKENS) {
+      std::cerr << "Started more than " << kTOTAL_TOKENS << " jobs at once!\n";
+      this->Okay = false;
+      return;
+    }
+  }
+
+  void QueueJob(std::size_t index)
+  {
+    this->JSC->RequestToken();
+    this->Queue.push_back(index);
+    std::cerr << "  QueueJob(" << index
+              << "): Queue length: " << this->Queue.size() << '\n';
+  }
+
+  void FinishJob()
+  {
+    --this->ActiveJobs;
+    std::cerr << "FinishJob: Active jobs: " << this->ActiveJobs << '\n';
+
+    this->JSC->ReleaseToken();
+    this->QueueNextJobs();
+  }
+};
+
+bool testJobServer()
+{
+#ifdef _WIN32
+  // FIXME: Windows job server client not yet implemented.
+#else
+  // Create a job server pipe.
+  int jobServerPipe[2];
+  if (cmGetPipes(jobServerPipe) < 0) {
+    std::cerr << "Failed to create job server pipe\n";
+    return false;
+  }
+
+  // Write N-1 tokens to the pipe.
+  std::vector<char> jobServerInit(kTOTAL_TOKENS - 1, '.');
+  if (write(jobServerPipe[1], jobServerInit.data(), jobServerInit.size()) !=
+      kTOTAL_TOKENS - 1) {
+    std::cerr << "Failed to initialize job server pipe\n";
+    return false;
+  }
+
+  // Establish the job server client context.
+  // Add a bogus server spec to verify we use the last spec.
+  cmSystemTools::PutEnv(cmStrCat("MAKEFLAGS=--flags-before"
+                                 " --jobserver-auth=bogus"
+                                 " --flags-between"
+                                 " --jobserver-fds=",
+                                 jobServerPipe[0], ',', jobServerPipe[1],
+                                 " --flags-after"));
+#endif
+
+  JobRunner jobRunner;
+  return jobRunner.Run();
+}
+}
+
+int testUVJobServerClient(int, char** const)
+{
+  bool passed = true;
+  passed = testJobServer() && passed;
+  return passed ? 0 : -1;
+}
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx
index c6404ac..b669f65 100644
--- a/Tests/CMakeLib/testUVProcessChain.cxx
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -295,7 +295,12 @@
     .AddCommand({ helperCommand, "capitalize" })
     .AddCommand({ helperCommand, "dedup" })
     .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
+    .SetBuiltinLoop();
+  if (builder.GetLoop()) {
+    std::cout << "GetLoop() should return null" << std::endl;
+    return false;
+  }
 
   if (!checkExecution(builder, chain)) {
     return false;
@@ -398,6 +403,10 @@
     .AddCommand({ helperCommand, "dedup" })
     .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1])
     .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]);
+  if (builder.GetLoop()) {
+    std::cout << "GetLoop() should return null" << std::endl;
+    return false;
+  }
 
   if (!checkExecution(builder, chain)) {
     return false;
@@ -665,6 +674,43 @@
   return true;
 }
 
+bool testUVProcessChainExternalLoop(const char* helperCommand)
+{
+  cm::uv_loop_ptr loop;
+  loop.init();
+
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand({ helperCommand, "echo" })
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetExternalLoop(*loop);
+  if (builder.GetLoop() != loop) {
+    std::cout << "GetLoop() should return external loop" << std::endl;
+    return false;
+  }
+
+  auto chain = builder.Start();
+
+  if (&chain.GetLoop() != loop) {
+    std::cout << "GetLoop() should return external loop" << std::endl;
+    return false;
+  }
+
+  if (!chain.Wait()) {
+    std::cout << "Wait() timed out" << std::endl;
+    return false;
+  }
+
+  cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
+  std::string output = getInput(stream);
+  if (output != "HELLO world!") {
+    std::cout << "Output was \"" << output << "\", expected \"HELLO world!\""
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
 int testUVProcessChain(int argc, char** const argv)
 {
   if (argc < 2) {
@@ -717,5 +763,10 @@
     return -1;
   }
 
+  if (!testUVProcessChainExternalLoop(argv[1])) {
+    std::cout << "While executing testUVProcessChainExternalLoop().\n";
+    return -1;
+  }
+
   return 0;
 }
diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx
index 0bdd44c..9e79d5c 100644
--- a/Tests/CMakeLib/testUVRAII.cxx
+++ b/Tests/CMakeLib/testUVRAII.cxx
@@ -37,7 +37,7 @@
       return false;
     }
 
-    if (signal.get()) {
+    if (signal) {
       std::cerr << "Loop exited with signal not being cleaned up" << std::endl;
       return false;
     }
@@ -125,13 +125,13 @@
     pipe.init(Loop, 0);
 
     cm::uv_stream_ptr stream = std::move(pipe);
-    if (pipe.get()) {
+    if (pipe) {
       std::cerr << "Move should be sure to invalidate the previous ptr"
                 << std::endl;
       return false;
     }
     cm::uv_handle_ptr handle = std::move(stream);
-    if (stream.get()) {
+    if (stream) {
       std::cerr << "Move should be sure to invalidate the previous ptr"
                 << std::endl;
       return false;
@@ -162,6 +162,7 @@
     uv_async_ptr _13;
     uv_signal_ptr _14;
     uv_handle_ptr _15;
+    uv_idle_ptr _16;
   };
 
   allTypes a;
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index a1619ec..1ee45d6 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -412,6 +412,7 @@
     PASS_REGULAR_EXPRESSION "CMake Error at CMakeLists.txt:3 \\(add_executable\\):[ \r\n]*Cannot find source file:[ \r\n]*DoesNotExist/MissingSourceFile.c")
   if(CMake_TEST_Swift)
     ADD_TEST_MACRO(SwiftOnly SwiftOnly)
+    ADD_TEST_MACRO(SwiftMixPCH SwiftMixPCH)
     if(CMake_TEST_XCODE_SWIFT)
       ADD_TEST_MACRO(SwiftMix SwiftMix)
     endif()
@@ -421,6 +422,7 @@
   endif()
   if(CMAKE_Fortran_COMPILER)
     ADD_TEST_MACRO(FortranOnly FortranOnly)
+    set_property(TEST FortranOnly APPEND PROPERTY LABELS "Fortran")
   endif()
   # test Visual Studio GNU Fortran mixing with cmake_add_fortran_subdirectory
   # run this project if we have a working fortran compiler or
@@ -454,6 +456,7 @@
     endif()
     if(NOT CMAKE_SKIP_VSGNUFortran)
       ADD_TEST_MACRO(VSGNUFortran ${CMAKE_CMAKE_COMMAND} -P runtest.cmake)
+      set_property(TEST VSGNUFortran APPEND PROPERTY LABELS "Fortran")
     endif()
   endif()
 
@@ -508,15 +511,18 @@
   ADD_TEST_MACRO(PolicyScope PolicyScope)
   ADD_TEST_MACRO(EmptyLibrary EmptyLibrary)
   ADD_TEST_MACRO(CompileDefinitions CompileDefinitions)
-  if(CMAKE_Fortran_COMPILER)
-    set(CompileOptions_BUILD_OPTIONS -DTEST_FORTRAN=1)
-  endif()
+
   if(_isMultiConfig)
     set(CompileOptions_CTEST_OPTIONS --build-config $<CONFIGURATION>)
   else()
     set(CompileOptions_BUILD_OPTIONS -DCMAKE_BUILD_TYPE=$<CONFIGURATION>)
   endif()
+  if(CMAKE_Fortran_COMPILER)
+    list(APPEND CompileOptions_BUILD_OPTIONS -DTEST_FORTRAN=1)
+  endif()
   ADD_TEST_MACRO(CompileOptions CompileOptions)
+  set_property(TEST CompileOptions APPEND PROPERTY LABELS "Fortran")
+
   ADD_TEST_MACRO(CompatibleInterface CompatibleInterface)
   ADD_TEST_MACRO(AliasTarget AliasTarget)
   ADD_TEST_MACRO(StagingPrefix StagingPrefix)
@@ -659,6 +665,7 @@
   if(CMAKE_Fortran_COMPILER)
     set(Module.CheckIPOSupported-Fortran_BUILD_OPTIONS -DCMake_TEST_IPO_WORKS_Fortran=${CMake_TEST_IPO_WORKS_Fortran})
     ADD_TEST_MACRO(Module.CheckIPOSupported-Fortran CheckIPOSupported-Fortran)
+    set_property(TEST Module.CheckIPOSupported-Fortran APPEND PROPERTY LABELS "Fortran")
   endif()
 
   add_test(Module.ExternalData ${CMAKE_CTEST_COMMAND}
@@ -2159,7 +2166,7 @@
     endif()
     ADD_TEST_MACRO(MSVCDebugInformationFormat)
     set_property(TEST MSVCDebugInformationFormat APPEND
-      PROPERTY LABELS "CUDA")
+      PROPERTY LABELS "CUDA" "Fortran")
 
     set(MSVCRuntimeLibrary_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
     ADD_TEST_MACRO(MSVCRuntimeLibrary)
@@ -2167,6 +2174,7 @@
       PROPERTY LABELS "CUDA")
     if(CMAKE_Fortran_COMPILER)
       ADD_TEST_MACRO(MSVCRuntimeLibrary.Fortran)
+      set_property(TEST MSVCRuntimeLibrary.Fortran APPEND PROPERTY LABELS "Fortran")
     endif()
   endif()
   if(MSVC OR
@@ -3361,6 +3369,7 @@
       --build-two-config
       --test-command testf)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Fortran")
+    set_property(TEST Fortran APPEND PROPERTY LABELS "Fortran")
 
     if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
       add_test(FortranModules ${CMAKE_CTEST_COMMAND}
@@ -3375,6 +3384,7 @@
           ${CMake_TEST_FortranModules_BUILD_OPTIONS}
         )
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/FortranModules")
+      set_property(TEST FortranModules APPEND PROPERTY LABELS "Fortran")
     endif()
 
     # FortranCInterface tests.
@@ -3384,6 +3394,7 @@
       add_test(FortranC.Flags ${CMAKE_CMAKE_COMMAND} -P
         ${CMAKE_CURRENT_BINARY_DIR}/FortranC/Flags.cmake)
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/FortranC/Flags")
+      set_property(TEST FortranC.Flags APPEND PROPERTY LABELS "Fortran")
     else()
       add_test(FortranC ${CMAKE_CTEST_COMMAND}
         --build-and-test
@@ -3394,6 +3405,7 @@
         --build-two-config
         --test-command CMakeFiles/FortranCInterface/FortranCInterface)
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/FortranC")
+      set_property(TEST FortranC APPEND PROPERTY LABELS "Fortran")
     endif()
   endif()
 
diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt
index 30cabf1..ea06464 100644
--- a/Tests/CMakeOnly/CMakeLists.txt
+++ b/Tests/CMakeOnly/CMakeLists.txt
@@ -25,8 +25,8 @@
 add_CMakeOnly_test(CheckCXXCompilerFlag)
 
 add_CMakeOnly_test(CheckLanguage)
+set_property(TEST CMakeOnly.CheckLanguage APPEND PROPERTY LABELS "HIP" "Fortran")
 if (CMake_TEST_HIP)
-  set_property(TEST CMakeOnly.CheckLanguage APPEND PROPERTY LABELS "HIP")
   add_CMakeOnly_test(CheckLanguageHIPPlatform)
   set_property(TEST CMakeOnly.CheckLanguageHIPPlatform APPEND PROPERTY LABELS "HIP")
   add_CMakeOnly_test(CheckLanguageHIPPlatform2)
@@ -52,6 +52,7 @@
 
 if(CMAKE_Fortran_COMPILER)
   add_CMakeOnly_test(CompilerIdFortran)
+  set_property(TEST CMakeOnly.CompilerIdFortran APPEND PROPERTY LABELS "Fortran")
 endif()
 if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
   add_CMakeOnly_test(CompilerIdCSharp)
diff --git a/Tests/COnly/conly.c b/Tests/COnly/conly.c
index 2ae8a1a..c5f18ad 100644
--- a/Tests/COnly/conly.c
+++ b/Tests/COnly/conly.c
@@ -4,7 +4,7 @@
 #include "libc1.h"
 #include "libc2.h"
 
-int main()
+int main(void)
 {
   int class = 0;
   if (LibC1Func() != 2.0) {
diff --git a/Tests/COnly/libc1.c b/Tests/COnly/libc1.c
index b01e1e1..e1f7e27 100644
--- a/Tests/COnly/libc1.c
+++ b/Tests/COnly/libc1.c
@@ -1,4 +1,4 @@
-float LibC1Func()
+float LibC1Func(void)
 {
   return 2.0;
 }
diff --git a/Tests/COnly/libc2.c b/Tests/COnly/libc2.c
index 0fd8956..28d46f3 100644
--- a/Tests/COnly/libc2.c
+++ b/Tests/COnly/libc2.c
@@ -1,6 +1,6 @@
 #include "libc2.h"
 
-float LibC2Func()
+float LibC2Func(void)
 {
   return 1.0;
 }
diff --git a/Tests/CPackInnoSetupGenerator/main.c b/Tests/CPackInnoSetupGenerator/main.c
index 413899c..9165c97 100644
--- a/Tests/CPackInnoSetupGenerator/main.c
+++ b/Tests/CPackInnoSetupGenerator/main.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   printf("Hello, World!\n");
   return 42;
diff --git a/Tests/CTestTestVerboseOutput/nop.c b/Tests/CTestTestVerboseOutput/nop.c
index f8b643a..8488f4e 100644
--- a/Tests/CTestTestVerboseOutput/nop.c
+++ b/Tests/CTestTestVerboseOutput/nop.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/CTestUpdateGIT.cmake.in b/Tests/CTestUpdateGIT.cmake.in
index 7fddaa3..5e3448b 100644
--- a/Tests/CTestUpdateGIT.cmake.in
+++ b/Tests/CTestUpdateGIT.cmake.in
@@ -193,7 +193,7 @@
   )
 run_child(
   WORKING_DIRECTORY ${TOP}/user-source
-  COMMAND ${GIT} push origin
+  COMMAND ${GIT} push origin HEAD
   )
 
 #-----------------------------------------------------------------------------
@@ -214,7 +214,7 @@
   )
 run_child(
   WORKING_DIRECTORY ${TOP}/user-source
-  COMMAND ${GIT} push origin
+  COMMAND ${GIT} push origin HEAD
   )
 
 #-----------------------------------------------------------------------------
diff --git a/Tests/CommandLength/test.c b/Tests/CommandLength/test.c
index f8b643a..8488f4e 100644
--- a/Tests/CommandLength/test.c
+++ b/Tests/CommandLength/test.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/CompileDefinitions/runtest.c b/Tests/CompileDefinitions/runtest.c
index c6dac4d..37317b8 100644
--- a/Tests/CompileDefinitions/runtest.c
+++ b/Tests/CompileDefinitions/runtest.c
@@ -6,7 +6,7 @@
 #  error "BUILD_CONFIG_NAME not defined!"
 #endif
 
-int main()
+int main(void)
 {
   char build_config_name[] = BUILD_CONFIG_NAME;
   char* c;
diff --git a/Tests/CompileFeatures/c_variadic_macros.c b/Tests/CompileFeatures/c_variadic_macros.c
index 4da111e..7a21902 100644
--- a/Tests/CompileFeatures/c_variadic_macros.c
+++ b/Tests/CompileFeatures/c_variadic_macros.c
@@ -9,7 +9,7 @@
 
 #define FUNC_WRAPPER(...) someFunc(__VA_ARGS__)
 
-void otherFunc()
+void otherFunc(void)
 {
   FUNC_WRAPPER(42, 'a', 7);
 }
diff --git a/Tests/CompileFeatures/default_dialect.c b/Tests/CompileFeatures/default_dialect.c
index c696c83..cae107b 100644
--- a/Tests/CompileFeatures/default_dialect.c
+++ b/Tests/CompileFeatures/default_dialect.c
@@ -26,7 +26,7 @@
 #  endif
 #endif
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/CompileFeatures/genex_test.c b/Tests/CompileFeatures/genex_test.c
index de408ce..8ccad35 100644
--- a/Tests/CompileFeatures/genex_test.c
+++ b/Tests/CompileFeatures/genex_test.c
@@ -38,6 +38,6 @@
 #  endif
 #endif
 
-int main()
+int main(void)
 {
 }
diff --git a/Tests/Complex/Executable/Sub1/NameConflictTest.c b/Tests/Complex/Executable/Sub1/NameConflictTest.c
index 8720386..740c236 100644
--- a/Tests/Complex/Executable/Sub1/NameConflictTest.c
+++ b/Tests/Complex/Executable/Sub1/NameConflictTest.c
@@ -1,4 +1,4 @@
-int NameConflictTest1()
+int NameConflictTest1(void)
 {
   return 0;
 }
diff --git a/Tests/Complex/Executable/Sub2/NameConflictTest.c b/Tests/Complex/Executable/Sub2/NameConflictTest.c
index 4a32572..cee9f6f 100644
--- a/Tests/Complex/Executable/Sub2/NameConflictTest.c
+++ b/Tests/Complex/Executable/Sub2/NameConflictTest.c
@@ -1,4 +1,4 @@
-int NameConflictTest2()
+int NameConflictTest2(void)
 {
   return 0;
 }
diff --git a/Tests/Complex/Library/TestLink.c b/Tests/Complex/Library/TestLink.c
index 25dee08..f4bc255 100644
--- a/Tests/Complex/Library/TestLink.c
+++ b/Tests/Complex/Library/TestLink.c
@@ -1,4 +1,4 @@
-int TestLinkGetType()
+int TestLinkGetType(void)
 {
 #ifdef CMakeTestLinkShared_EXPORTS
   return 0;
diff --git a/Tests/Complex/Library/testConly.c b/Tests/Complex/Library/testConly.c
index eb933a2..05ecc18 100644
--- a/Tests/Complex/Library/testConly.c
+++ b/Tests/Complex/Library/testConly.c
@@ -2,7 +2,7 @@
 
 #include <stdio.h>
 
-int CsharedFunction()
+int CsharedFunction(void)
 {
 #ifndef TEST_C_FLAGS
   printf("TEST_C_FLAGS failed\n");
diff --git a/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c b/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c
index 8720386..740c236 100644
--- a/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c
+++ b/Tests/ComplexOneConfig/Executable/Sub1/NameConflictTest.c
@@ -1,4 +1,4 @@
-int NameConflictTest1()
+int NameConflictTest1(void)
 {
   return 0;
 }
diff --git a/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c b/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c
index 4a32572..cee9f6f 100644
--- a/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c
+++ b/Tests/ComplexOneConfig/Executable/Sub2/NameConflictTest.c
@@ -1,4 +1,4 @@
-int NameConflictTest2()
+int NameConflictTest2(void)
 {
   return 0;
 }
diff --git a/Tests/ComplexOneConfig/Library/TestLink.c b/Tests/ComplexOneConfig/Library/TestLink.c
index 25dee08..f4bc255 100644
--- a/Tests/ComplexOneConfig/Library/TestLink.c
+++ b/Tests/ComplexOneConfig/Library/TestLink.c
@@ -1,4 +1,4 @@
-int TestLinkGetType()
+int TestLinkGetType(void)
 {
 #ifdef CMakeTestLinkShared_EXPORTS
   return 0;
diff --git a/Tests/ComplexOneConfig/Library/testConly.c b/Tests/ComplexOneConfig/Library/testConly.c
index eb933a2..05ecc18 100644
--- a/Tests/ComplexOneConfig/Library/testConly.c
+++ b/Tests/ComplexOneConfig/Library/testConly.c
@@ -2,7 +2,7 @@
 
 #include <stdio.h>
 
-int CsharedFunction()
+int CsharedFunction(void)
 {
 #ifndef TEST_C_FLAGS
   printf("TEST_C_FLAGS failed\n");
diff --git a/Tests/ConfigSources/CMakeLists.txt b/Tests/ConfigSources/CMakeLists.txt
index 38475f8..770afb3 100644
--- a/Tests/ConfigSources/CMakeLists.txt
+++ b/Tests/ConfigSources/CMakeLists.txt
@@ -154,7 +154,15 @@
 endif()
 add_library(OneConfigOnly OBJECT "$<$<CONFIG:${one_config}>:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>")
 set_property(TARGET OneConfigOnly PROPERTY LINKER_LANGUAGE CXX)
+add_executable(ConfigSourcesUseOne main_one_config.cpp)
+target_compile_definitions(ConfigSourcesUseOne PRIVATE "$<$<CONFIG:${one_config}>:CFG_ONE>")
+target_link_libraries(ConfigSourcesUseOne PRIVATE "$<$<CONFIG:${one_config}>:OneConfigOnly>")
 
+add_library(OneConfigOnlyIface INTERFACE)
+target_sources(OneConfigOnlyIface INTERFACE "$<$<CONFIG:${one_config}>:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>")
+add_executable(ConfigSourcesUseOneIface main_one_config.cpp)
+target_compile_definitions(ConfigSourcesUseOneIface PRIVATE "$<$<CONFIG:${one_config}>:CFG_ONE>")
+target_link_libraries(ConfigSourcesUseOneIface PRIVATE "$<$<CONFIG:${one_config}>:OneConfigOnlyIface>")
 
 # ---------------------------------------------------------------------------
 # Makes sure that each configuration uses the correct generated file.
diff --git a/Tests/ConfigSources/main_one_config.cpp b/Tests/ConfigSources/main_one_config.cpp
new file mode 100644
index 0000000..318944b
--- /dev/null
+++ b/Tests/ConfigSources/main_one_config.cpp
@@ -0,0 +1,8 @@
+#include "iface.h"
+int main()
+{
+#ifdef CFG_ONE
+  iface_src();
+#endif
+  return 0;
+}
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt
index efe0358..04ef86d 100644
--- a/Tests/Cuda/CMakeLists.txt
+++ b/Tests/Cuda/CMakeLists.txt
@@ -9,8 +9,10 @@
 add_cuda_test_macro(Cuda.MixedStandardLevels1 MixedStandardLevels1)
 add_cuda_test_macro(Cuda.MixedStandardLevels2 MixedStandardLevels2)
 add_cuda_test_macro(Cuda.MixedStandardLevels3 MixedStandardLevels3)
-add_cuda_test_macro(Cuda.MixedStandardLevels4 MixedStandardLevels4)
-add_cuda_test_macro(Cuda.MixedStandardLevels5 MixedStandardLevels5)
+if(NOT WIN32 OR NOT CMake_TEST_CUDA STREQUAL "Clang") # MSVC std lib needs C++14
+  add_cuda_test_macro(Cuda.MixedStandardLevels4 MixedStandardLevels4)
+  add_cuda_test_macro(Cuda.MixedStandardLevels5 MixedStandardLevels5)
+endif()
 add_cuda_test_macro(Cuda.NotEnabled CudaNotEnabled)
 add_cuda_test_macro(Cuda.SeparableCompCXXOnly SeparableCompCXXOnly)
 add_cuda_test_macro(Cuda.StubRPATH StubRPATH)
diff --git a/Tests/Cuda/Complex/main.cpp b/Tests/Cuda/Complex/main.cpp
index da09b44..032f910 100644
--- a/Tests/Cuda/Complex/main.cpp
+++ b/Tests/Cuda/Complex/main.cpp
@@ -1,5 +1,3 @@
-#include <iostream>
-
 #include "file1.h"
 #include "file2.h"
 
diff --git a/Tests/Cuda/ObjectLibrary/main.cpp b/Tests/Cuda/ObjectLibrary/main.cpp
index e28f088..cc727c6 100644
--- a/Tests/Cuda/ObjectLibrary/main.cpp
+++ b/Tests/Cuda/ObjectLibrary/main.cpp
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 int cpp_sq_func(int);
 int cu1_sq_func(int);
 int cu2_sq_func(int);
diff --git a/Tests/Cuda/ProperLinkFlags/main.cxx b/Tests/Cuda/ProperLinkFlags/main.cxx
index 7c0ee9e..665f31c 100644
--- a/Tests/Cuda/ProperLinkFlags/main.cxx
+++ b/Tests/Cuda/ProperLinkFlags/main.cxx
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #include "file1.h"
 
 int main(int argc, char** argv)
diff --git a/Tests/Cuda/SharedRuntimePlusToolkit/nppif.cpp b/Tests/Cuda/SharedRuntimePlusToolkit/nppif.cpp
index ac5341c..1e7350a 100644
--- a/Tests/Cuda/SharedRuntimePlusToolkit/nppif.cpp
+++ b/Tests/Cuda/SharedRuntimePlusToolkit/nppif.cpp
@@ -8,7 +8,6 @@
 #endif
 
 #include <cstdio>
-#include <iostream>
 
 #include <assert.h>
 #include <cuda_runtime_api.h>
diff --git a/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp b/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
index ac5341c..1e7350a 100644
--- a/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
+++ b/Tests/Cuda/StaticRuntimePlusToolkit/nppif.cpp
@@ -8,7 +8,6 @@
 #endif
 
 #include <cstdio>
-#include <iostream>
 
 #include <assert.h>
 #include <cuda_runtime_api.h>
diff --git a/Tests/Cuda/Toolkit/CMakeLists.txt b/Tests/Cuda/Toolkit/CMakeLists.txt
index 4255b82..08ba20a 100644
--- a/Tests/Cuda/Toolkit/CMakeLists.txt
+++ b/Tests/Cuda/Toolkit/CMakeLists.txt
@@ -70,7 +70,11 @@
   endif()
 endforeach()
 
-foreach (cuda_lib nvrtc nvToolsExt OpenCL)
+set(nv_libs nvrtc OpenCL)
+if(CUDAToolkit_VERSION_MAJOR VERSION_LESS 12)
+  list(APPEND nv_libs nvToolsExt)
+endif()
+foreach (cuda_lib IN LISTS nv_libs)
   if(NOT TARGET CUDA::${cuda_lib})
     message(FATAL_ERROR "The CUDA::${cuda_lib} target was expected but couldn't be found")
   endif()
diff --git a/Tests/Cuda/WithC/main.c b/Tests/Cuda/WithC/main.c
index 5f3c781..ed64427 100644
--- a/Tests/Cuda/WithC/main.c
+++ b/Tests/Cuda/WithC/main.c
@@ -4,7 +4,7 @@
 #  include <windows.h>
 #endif
 
-int main()
+int main(void)
 {
 #ifdef _WIN32
   /* Use an API that requires CMake's "standard" C libraries.  */
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index 82366df..128d371 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -11,11 +11,14 @@
 add_cuda_test_macro(CudaOnly.ExportPTX CudaOnlyExportPTX)
 add_cuda_test_macro(CudaOnly.SharedRuntimePlusToolkit CudaOnlySharedRuntimePlusToolkit)
 add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
-add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98)
+if(NOT WIN32 OR NOT CMake_TEST_CUDA STREQUAL "Clang") # MSVC std lib needs C++14
+  add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98)
+endif()
 set(CudaOnly.Toolkit_BUILD_OPTIONS -DHAS_CUPTI:BOOL=${CMake_TEST_CUDA_CUPTI})
 add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit)
 add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang)
 add_cuda_test_macro(CudaOnly.ToolkitMultipleDirs CudaOnlyToolkitMultipleDirs)
+add_cuda_test_macro(CudaOnly.TryCompileTargetStatic CudaOnlyTryCompileTargetStatic)
 add_cuda_test_macro(CudaOnly.WithDefs CudaOnlyWithDefs)
 add_cuda_test_macro(CudaOnly.CircularLinkLine CudaOnlyCircularLinkLine)
 add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols)
diff --git a/Tests/CudaOnly/DeviceLTO/file1.cu b/Tests/CudaOnly/DeviceLTO/file1.cu
index 703927c..5ca000d 100644
--- a/Tests/CudaOnly/DeviceLTO/file1.cu
+++ b/Tests/CudaOnly/DeviceLTO/file1.cu
@@ -1,16 +1,10 @@
-#ifdef _WIN32
-#  define EXPORT __declspec(dllexport)
-#else
-#  define EXPORT
-#endif
-
 extern __device__ int file2_func(int);
 void __global__ kernel(int x)
 {
   file2_func(x);
 }
 
-EXPORT int launch_kernel(int x)
+int launch_kernel(int x)
 {
   kernel<<<1, 1>>>(x);
   return x;
diff --git a/Tests/CudaOnly/DeviceLTO/main.cu b/Tests/CudaOnly/DeviceLTO/main.cu
index 8ef4873..bb72471 100644
--- a/Tests/CudaOnly/DeviceLTO/main.cu
+++ b/Tests/CudaOnly/DeviceLTO/main.cu
@@ -2,13 +2,7 @@
 
 #include "cuda.h"
 
-#ifdef _WIN32
-#  define IMPORT __declspec(dllimport)
-#else
-#  define IMPORT
-#endif
-
-IMPORT int launch_kernel(int x);
+int launch_kernel(int x);
 
 int choose_cuda_device()
 {
diff --git a/Tests/CudaOnly/DontResolveDeviceSymbols/main.cu b/Tests/CudaOnly/DontResolveDeviceSymbols/main.cu
index 84a7a19..d123e09 100644
--- a/Tests/CudaOnly/DontResolveDeviceSymbols/main.cu
+++ b/Tests/CudaOnly/DontResolveDeviceSymbols/main.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 int main(int argc, char** argv)
 {
   return 0;
diff --git a/Tests/CudaOnly/EnableStandard/main.cu b/Tests/CudaOnly/EnableStandard/main.cu
index 740c832..acbbee3 100644
--- a/Tests/CudaOnly/EnableStandard/main.cu
+++ b/Tests/CudaOnly/EnableStandard/main.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #ifdef _WIN32
 #  define IMPORT __declspec(dllimport)
 #else
diff --git a/Tests/CudaOnly/ExportPTX/main.cu b/Tests/CudaOnly/ExportPTX/main.cu
index 132377c..d2a9b90 100644
--- a/Tests/CudaOnly/ExportPTX/main.cu
+++ b/Tests/CudaOnly/ExportPTX/main.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 /*
   Define GENERATED_HEADER macro to allow c++ files to include headers
   generated based on different configuration types.
diff --git a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
index ca73b1a..f2eb406 100644
--- a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
+++ b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
@@ -20,7 +20,7 @@
                       POSITION_INDEPENDENT_CODE ON)
 unset(CMAKE_CUDA_SEPARABLE_COMPILATION)
 
-if(CMAKE_CUDA_SIMULATE_ID STREQUAL "MSVC")
+if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_SIMULATE_ID STREQUAL "MSVC")
   # Test adding a flag that is not in our CUDA flag table for VS.
   if(NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 8)
     string(APPEND CMAKE_CUDA_FLAGS " --ftemplate-depth 50")
diff --git a/Tests/CudaOnly/SeparateCompilation/file4.cu b/Tests/CudaOnly/SeparateCompilation/file4.cu
index cc24a46..f2ef8e7 100644
--- a/Tests/CudaOnly/SeparateCompilation/file4.cu
+++ b/Tests/CudaOnly/SeparateCompilation/file4.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #include "file1.h"
 #include "file2.h"
 
diff --git a/Tests/CudaOnly/SeparateCompilation/file5.cu b/Tests/CudaOnly/SeparateCompilation/file5.cu
index 38cbeb2..9b2c92a 100644
--- a/Tests/CudaOnly/SeparateCompilation/file5.cu
+++ b/Tests/CudaOnly/SeparateCompilation/file5.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #include "file1.h"
 #include "file2.h"
 
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu
index 234586f..2e824ec 100644
--- a/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #ifdef _WIN32
 #  define EXPORT __declspec(dllexport)
 #else
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu
index 75c04af..88acaca 100644
--- a/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu
@@ -1,6 +1,3 @@
-
-#include <iostream>
-
 #ifdef _WIN32
 #  define EXPORT __declspec(dllexport)
 #else
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu
index 78b10b1..367c488 100644
--- a/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu
@@ -1,6 +1,3 @@
-// main.cu
-#include <iostream>
-
 #ifdef _WIN32
 #  define IMPORT __declspec(dllimport)
 #else
diff --git a/Tests/CudaOnly/SharedRuntimePlusToolkit/nppif.cu b/Tests/CudaOnly/SharedRuntimePlusToolkit/nppif.cu
index ac5341c..1e7350a 100644
--- a/Tests/CudaOnly/SharedRuntimePlusToolkit/nppif.cu
+++ b/Tests/CudaOnly/SharedRuntimePlusToolkit/nppif.cu
@@ -8,7 +8,6 @@
 #endif
 
 #include <cstdio>
-#include <iostream>
 
 #include <assert.h>
 #include <cuda_runtime_api.h>
diff --git a/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu b/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
index ac5341c..1e7350a 100644
--- a/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
+++ b/Tests/CudaOnly/StaticRuntimePlusToolkit/nppif.cu
@@ -8,7 +8,6 @@
 #endif
 
 #include <cstdio>
-#include <iostream>
 
 #include <assert.h>
 #include <cuda_runtime_api.h>
diff --git a/Tests/CudaOnly/Toolkit/CMakeLists.txt b/Tests/CudaOnly/Toolkit/CMakeLists.txt
index b2694bf..477c816 100644
--- a/Tests/CudaOnly/Toolkit/CMakeLists.txt
+++ b/Tests/CudaOnly/Toolkit/CMakeLists.txt
@@ -57,7 +57,7 @@
 if(CUDAToolkit_VERSION_MAJOR VERSION_LESS 11)
   list(APPEND npp_libs nppicom)
 endif()
-foreach (cuda_lib )
+foreach (cuda_lib IN LISTS npp_libs)
   if(NOT CUDA_${cuda_lib}_LIBRARY)
     message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found")
   elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]])
@@ -68,7 +68,11 @@
   endif()
 endforeach()
 
-foreach (cuda_lib nvrtc nvToolsExt OpenCL)
+set(nv_libs nvrtc OpenCL)
+if(CUDAToolkit_VERSION_MAJOR VERSION_LESS 12)
+  list(APPEND nv_libs nvToolsExt)
+endif()
+foreach (cuda_lib IN LISTS nv_libs)
   if(NOT CUDA_${cuda_lib}_LIBRARY)
     message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found")
   elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]])
diff --git a/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt b/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt
new file mode 100644
index 0000000..340c1c9
--- /dev/null
+++ b/Tests/CudaOnly/TryCompileTargetStatic/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.25)
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+project(TryCompileTargetStatic LANGUAGES CUDA)
+
+add_executable(CudaOnlyTryCompileTargetStatic main.cu)
diff --git a/Tests/CudaOnly/TryCompileTargetStatic/main.cu b/Tests/CudaOnly/TryCompileTargetStatic/main.cu
new file mode 100644
index 0000000..766b775
--- /dev/null
+++ b/Tests/CudaOnly/TryCompileTargetStatic/main.cu
@@ -0,0 +1,5 @@
+
+int main()
+{
+  return 0;
+}
diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt
index 25df300..d46ee08 100644
--- a/Tests/CustomCommand/CMakeLists.txt
+++ b/Tests/CustomCommand/CMakeLists.txt
@@ -214,11 +214,13 @@
 
 add_executable(CustomCommandUsingTargetTest main.cxx ${CMAKE_CURRENT_BINARY_DIR}/generated_extern.cxx )
 
+add_executable(CustomCommandUsingTargetTestAlias ALIAS CustomCommandUsingTargetTest )
+
 add_custom_target(RunTarget
   COMMAND generator_extern ${CMAKE_CURRENT_BINARY_DIR}/run_target.cxx
   )
 
-add_custom_command(TARGET CustomCommandUsingTargetTest POST_BUILD
+add_custom_command(TARGET CustomCommandUsingTargetTestAlias POST_BUILD
                    COMMAND dummy_generator ${CMAKE_CURRENT_BINARY_DIR}/generated_dummy.cxx)
 
 add_subdirectory(GeneratorInExtraDir)
diff --git a/Tests/CustomCommand/gen_redirect_in.c b/Tests/CustomCommand/gen_redirect_in.c
index 6af364b..c71123a 100644
--- a/Tests/CustomCommand/gen_redirect_in.c
+++ b/Tests/CustomCommand/gen_redirect_in.c
@@ -1,6 +1,6 @@
 #if 1
 
-int gen_redirect()
+int gen_redirect(void)
 {
   return 3;
 }
diff --git a/Tests/CustomCommandWorkingDirectory/customTarget.c b/Tests/CustomCommandWorkingDirectory/customTarget.c
index 8dbf0d4..9735287 100644
--- a/Tests/CustomCommandWorkingDirectory/customTarget.c
+++ b/Tests/CustomCommandWorkingDirectory/customTarget.c
@@ -1,4 +1,4 @@
-int customTarget()
+int customTarget(void)
 {
   return 0;
 }
diff --git a/Tests/Dependency/1/OneSrc.c b/Tests/Dependency/1/OneSrc.c
index 9801c25..7f527f4 100644
--- a/Tests/Dependency/1/OneSrc.c
+++ b/Tests/Dependency/1/OneSrc.c
@@ -1,3 +1,3 @@
-void OneFunction()
+void OneFunction(void)
 {
 }
diff --git a/Tests/Dependency/Case1/a.c b/Tests/Dependency/Case1/a.c
index 262f523..bf1c773 100644
--- a/Tests/Dependency/Case1/a.c
+++ b/Tests/Dependency/Case1/a.c
@@ -1,4 +1,4 @@
-int a()
+int a(void)
 {
   return 5;
 }
diff --git a/Tests/Dependency/Case1/b.c b/Tests/Dependency/Case1/b.c
index deda685..f4ef707 100644
--- a/Tests/Dependency/Case1/b.c
+++ b/Tests/Dependency/Case1/b.c
@@ -1,6 +1,6 @@
-extern int a();
+extern int a(void);
 
-int b()
+int b(void)
 {
   return a() + 17;
 }
diff --git a/Tests/Dependency/Case1/b2.c b/Tests/Dependency/Case1/b2.c
index f341da7..108e76a 100644
--- a/Tests/Dependency/Case1/b2.c
+++ b/Tests/Dependency/Case1/b2.c
@@ -1,4 +1,4 @@
-int b2()
+int b2(void)
 {
   return 3;
 }
diff --git a/Tests/Dependency/Case1/c.c b/Tests/Dependency/Case1/c.c
index a3ec162..ddda855 100644
--- a/Tests/Dependency/Case1/c.c
+++ b/Tests/Dependency/Case1/c.c
@@ -1,6 +1,6 @@
-extern int b();
+extern int b(void);
 
-int c()
+int c(void)
 {
   return b() + 42;
 }
diff --git a/Tests/Dependency/Case1/c2.c b/Tests/Dependency/Case1/c2.c
index 317bb0f..826c746 100644
--- a/Tests/Dependency/Case1/c2.c
+++ b/Tests/Dependency/Case1/c2.c
@@ -1,6 +1,6 @@
-extern int b2();
+extern int b2(void);
 
-int c2()
+int c2(void)
 {
   return b2() + 1;
 }
diff --git a/Tests/Dependency/Case1/d.c b/Tests/Dependency/Case1/d.c
index f67aef7..fdeb2bf 100644
--- a/Tests/Dependency/Case1/d.c
+++ b/Tests/Dependency/Case1/d.c
@@ -1,6 +1,6 @@
-extern int c2();
+extern int c2(void);
 
-int d()
+int d(void)
 {
   return c2() + 2;
 }
diff --git a/Tests/Dependency/Case1/main.c b/Tests/Dependency/Case1/main.c
index 07191cc..6847f7d 100644
--- a/Tests/Dependency/Case1/main.c
+++ b/Tests/Dependency/Case1/main.c
@@ -2,7 +2,7 @@
 extern int c();
 extern int d();
 
-int main()
+int main(void)
 {
   c();
   b();
diff --git a/Tests/Dependency/Case4/bar.c b/Tests/Dependency/Case4/bar.c
index 08092f9..18ddb78 100644
--- a/Tests/Dependency/Case4/bar.c
+++ b/Tests/Dependency/Case4/bar.c
@@ -1,5 +1,5 @@
-extern int foo();
-int main()
+extern int foo(void);
+int main(void)
 {
   return foo();
 }
diff --git a/Tests/Dependency/Case4/foo.c b/Tests/Dependency/Case4/foo.c
index e05eb7e..c83d856 100644
--- a/Tests/Dependency/Case4/foo.c
+++ b/Tests/Dependency/Case4/foo.c
@@ -1,4 +1,4 @@
-int foo()
+int foo(void)
 {
   return 0;
 }
diff --git a/Tests/Dependency/Eight/EightSrc.c b/Tests/Dependency/Eight/EightSrc.c
index 7bfa481..16605ce 100644
--- a/Tests/Dependency/Eight/EightSrc.c
+++ b/Tests/Dependency/Eight/EightSrc.c
@@ -1,6 +1,6 @@
-void SevenFunction();
+void SevenFunction(void);
 
-void EightFunction()
+void EightFunction(void)
 {
   SevenFunction();
 }
diff --git a/Tests/Dependency/Exec/ExecMain.c b/Tests/Dependency/Exec/ExecMain.c
index 9572afd..793bc18 100644
--- a/Tests/Dependency/Exec/ExecMain.c
+++ b/Tests/Dependency/Exec/ExecMain.c
@@ -5,7 +5,7 @@
 void SixAFunction();
 void SixBFunction();
 
-int main()
+int main(void)
 {
   SixAFunction();
   SixBFunction();
diff --git a/Tests/Dependency/Exec2/ExecMain.c b/Tests/Dependency/Exec2/ExecMain.c
index 385cce1..085f30f 100644
--- a/Tests/Dependency/Exec2/ExecMain.c
+++ b/Tests/Dependency/Exec2/ExecMain.c
@@ -3,7 +3,7 @@
 void FiveFunction();
 void EightFunction();
 
-int main()
+int main(void)
 {
   FiveFunction();
   EightFunction();
diff --git a/Tests/Dependency/Exec3/ExecMain.c b/Tests/Dependency/Exec3/ExecMain.c
index 385cce1..085f30f 100644
--- a/Tests/Dependency/Exec3/ExecMain.c
+++ b/Tests/Dependency/Exec3/ExecMain.c
@@ -3,7 +3,7 @@
 void FiveFunction();
 void EightFunction();
 
-int main()
+int main(void)
 {
   FiveFunction();
   EightFunction();
diff --git a/Tests/Dependency/Exec4/ExecMain.c b/Tests/Dependency/Exec4/ExecMain.c
index 0cfcce9..48552d3 100644
--- a/Tests/Dependency/Exec4/ExecMain.c
+++ b/Tests/Dependency/Exec4/ExecMain.c
@@ -3,7 +3,7 @@
 void FiveFunction();
 void TwoFunction();
 
-int main()
+int main(void)
 {
   FiveFunction();
   TwoFunction();
diff --git a/Tests/Dependency/Five/FiveSrc.c b/Tests/Dependency/Five/FiveSrc.c
index 33d8ad7..b35b05b 100644
--- a/Tests/Dependency/Five/FiveSrc.c
+++ b/Tests/Dependency/Five/FiveSrc.c
@@ -1,6 +1,6 @@
-void TwoFunction();
+void TwoFunction(void);
 
-void FiveFunction()
+void FiveFunction(void)
 {
   TwoFunction();
 }
diff --git a/Tests/Dependency/Four/FourSrc.c b/Tests/Dependency/Four/FourSrc.c
index 4ea996d..b91c5fd 100644
--- a/Tests/Dependency/Four/FourSrc.c
+++ b/Tests/Dependency/Four/FourSrc.c
@@ -1,9 +1,9 @@
 #include <two-test.h> /* Requires TwoCustom to be built first.  */
-void NoDepAFunction();
-void OneFunction();
-void TwoFunction();
+void NoDepAFunction(void);
+void OneFunction(void);
+void TwoFunction(void);
 
-void FourFunction()
+void FourFunction(void)
 {
   static int count = 0;
   if (count == 0) {
diff --git a/Tests/Dependency/NoDepA/NoDepASrc.c b/Tests/Dependency/NoDepA/NoDepASrc.c
index 8c4072b..e972df2 100644
--- a/Tests/Dependency/NoDepA/NoDepASrc.c
+++ b/Tests/Dependency/NoDepA/NoDepASrc.c
@@ -1,3 +1,3 @@
-void NoDepAFunction()
+void NoDepAFunction(void)
 {
 }
diff --git a/Tests/Dependency/NoDepB/NoDepBSrc.c b/Tests/Dependency/NoDepB/NoDepBSrc.c
index ddc71c5..81dc5ed 100644
--- a/Tests/Dependency/NoDepB/NoDepBSrc.c
+++ b/Tests/Dependency/NoDepB/NoDepBSrc.c
@@ -1,6 +1,6 @@
-void NoDepAFunction();
+void NoDepAFunction(void);
 
-void NoDepBFunction()
+void NoDepBFunction(void)
 {
   NoDepAFunction();
 }
diff --git a/Tests/Dependency/NoDepC/NoDepCSrc.c b/Tests/Dependency/NoDepC/NoDepCSrc.c
index b478c59..d90007e 100644
--- a/Tests/Dependency/NoDepC/NoDepCSrc.c
+++ b/Tests/Dependency/NoDepC/NoDepCSrc.c
@@ -1,6 +1,6 @@
-void NoDepAFunction();
+void NoDepAFunction(void);
 
-void NoDepCFunction()
+void NoDepCFunction(void)
 {
   NoDepAFunction();
 }
diff --git a/Tests/Dependency/Seven/SevenSrc.c b/Tests/Dependency/Seven/SevenSrc.c
index e1f3329..9c74ec8 100644
--- a/Tests/Dependency/Seven/SevenSrc.c
+++ b/Tests/Dependency/Seven/SevenSrc.c
@@ -1,6 +1,6 @@
-void TwoFunction();
+void TwoFunction(void);
 
-void SevenFunction()
+void SevenFunction(void)
 {
   TwoFunction();
 }
diff --git a/Tests/Dependency/Six/SixASrc.c b/Tests/Dependency/Six/SixASrc.c
index 7ea3711..ddd2d7d 100644
--- a/Tests/Dependency/Six/SixASrc.c
+++ b/Tests/Dependency/Six/SixASrc.c
@@ -1,7 +1,7 @@
-void FiveFunction();
-void TwoFunction();
+void FiveFunction(void);
+void TwoFunction(void);
 
-void SixAFunction()
+void SixAFunction(void)
 {
   FiveFunction();
   TwoFunction();
diff --git a/Tests/Dependency/Six/SixBSrc.c b/Tests/Dependency/Six/SixBSrc.c
index 92f9607..42f2de6 100644
--- a/Tests/Dependency/Six/SixBSrc.c
+++ b/Tests/Dependency/Six/SixBSrc.c
@@ -1,8 +1,8 @@
-void TwoFunction();
-void FiveFunction();
-void FourFunction();
+void TwoFunction(void);
+void FiveFunction(void);
+void FourFunction(void);
 
-void SixBFunction()
+void SixBFunction(void)
 {
   TwoFunction();
   FiveFunction();
diff --git a/Tests/Dependency/Three/ThreeSrc.c b/Tests/Dependency/Three/ThreeSrc.c
index 3e814f3..85c51fc 100644
--- a/Tests/Dependency/Three/ThreeSrc.c
+++ b/Tests/Dependency/Three/ThreeSrc.c
@@ -1,7 +1,7 @@
-void OneFunction();
-void FourFunction();
+void OneFunction(void);
+void FourFunction(void);
 
-void ThreeFunction()
+void ThreeFunction(void)
 {
   static int count = 0;
   if (count == 0) {
diff --git a/Tests/Dependency/Two/TwoCustomSrc.c b/Tests/Dependency/Two/TwoCustomSrc.c
index ac31dcf..432dca1 100644
--- a/Tests/Dependency/Two/TwoCustomSrc.c
+++ b/Tests/Dependency/Two/TwoCustomSrc.c
@@ -1,10 +1,10 @@
-extern void NoFunction();
+extern void NoFunction(void);
 
 /* Provide a function that is supposed to be found in the Three
    library.  If Two links to TwoCustom then TwoCustom will come before
    Three and this symbol will be used.  Since NoFunction is not
    defined, that will cause a link failure.  */
-void ThreeFunction()
+void ThreeFunction(void)
 {
   NoFunction();
 }
diff --git a/Tests/Dependency/Two/TwoSrc.c b/Tests/Dependency/Two/TwoSrc.c
index dbdf524..dadac22 100644
--- a/Tests/Dependency/Two/TwoSrc.c
+++ b/Tests/Dependency/Two/TwoSrc.c
@@ -1,6 +1,6 @@
 #include <two-test.h>
 
-void TwoFunction()
+void TwoFunction(void)
 {
   static int count = 0;
   if (count == 0) {
diff --git a/Tests/DoubleProject/silly.c b/Tests/DoubleProject/silly.c
index f8b643a..8488f4e 100644
--- a/Tests/DoubleProject/silly.c
+++ b/Tests/DoubleProject/silly.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt
index 67f2fcb..56b8aaa 100644
--- a/Tests/ExportImport/Export/CMakeLists.txt
+++ b/Tests/ExportImport/Export/CMakeLists.txt
@@ -4,6 +4,8 @@
 endif()
 project(Export C CXX)
 
+find_package(Foo REQUIRED CONFIG NO_DEFAULT_PATH)
+
 # Pretend that RelWithDebInfo should link to debug libraries to test
 # the DEBUG_CONFIGURATIONS property.
 set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug RelWithDebInfo)
@@ -110,6 +112,7 @@
 target_compile_definitions(testLib9ObjIface INTERFACE testLib9ObjIface_USED)
 add_library(testLib9 STATIC testLib9.c)
 target_link_libraries(testLib9 INTERFACE testLib9ObjIface PUBLIC testLib9ObjPub PRIVATE testLib9ObjPriv)
+target_link_libraries(testLib9 PUBLIC Foo::Foo)
 cmake_policy(POP)
 
 # Test using the target_link_libraries command to set the
@@ -624,7 +627,7 @@
   LIBRARY DESTINATION lib
   ARCHIVE DESTINATION lib
   )
-install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp)
+install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp EXPORT_PACKAGE_DEPENDENCIES)
 
 # Install testLib5.dll outside the export.
 if(WIN32)
diff --git a/Tests/ExportImport/Export/testExe2.c b/Tests/ExportImport/Export/testExe2.c
index 958e4d2..8e6ee44 100644
--- a/Tests/ExportImport/Export/testExe2.c
+++ b/Tests/ExportImport/Export/testExe2.c
@@ -9,7 +9,7 @@
   return 123;
 }
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/ExportImport/External/FooConfig.cmake b/Tests/ExportImport/External/FooConfig.cmake
new file mode 100644
index 0000000..48b6289
--- /dev/null
+++ b/Tests/ExportImport/External/FooConfig.cmake
@@ -0,0 +1,3 @@
+if(NOT TARGET Foo::Foo)
+  add_library(Foo::Foo INTERFACE IMPORTED)
+endif()
diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt
index 2c5662d..2a57633 100644
--- a/Tests/ExportImport/Import/A/CMakeLists.txt
+++ b/Tests/ExportImport/Import/A/CMakeLists.txt
@@ -500,7 +500,7 @@
       OUTPUT_VARIABLE OUTPUT
       )
     if(NOT BLD_ERROR_VARIABLE)
-      message(SEND_ERROR "BLD_ERROR_VARIABLE try_compile failed, but it was expected to succeed.")
+      message(SEND_ERROR "BLD_ERROR_VARIABLE try_compile failed, but it was expected to succeed. ${OUTPUT}")
     endif()
 
     if(NOT CMAKE_CROSSCOMPILING)
@@ -518,6 +518,91 @@
       endif()
     endif()
   endif()
+
+  # Testing try_compile with ALIAS targets.
+  # These assume that previous test were successful, or at least the failures will be at the linking stage
+  # with symbol not found errors
+
+  # First make sure that if the test run without appropriate alias targets, they should error out
+  try_compile(FAILING_LIBRARY_ALIAS_ERROR_VARIABLE
+          "${CMAKE_CURRENT_BINARY_DIR}/test_failing_library_alias"
+          "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
+          LINK_LIBRARIES not_existing_library
+          OUTPUT_VARIABLE OUTPUT
+          NO_CACHE
+  )
+  if(FAILING_LIBRARY_ALIAS_ERROR_VARIABLE)
+    message(SEND_ERROR "FAILING_LIBRARY_ALIAS_ERROR_VARIABLE try_compile succeeded, but it was expected to fail ${OUTPUT}.")
+  endif()
+
+  # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable
+#  set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+#  try_compile(FAILING_EXE_ALIAS_ERROR_VARIABLE
+#          "${CMAKE_CURRENT_BINARY_DIR}/test_failing_exe_alias"
+#          "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c"
+#          LINK_LIBRARIES not_existing_executable
+#          OUTPUT_VARIABLE OUTPUT
+#          NO_CACHE
+#  )
+#  unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
+#  if(FAILING_EXE_ALIAS_ERROR_VARIABLE)
+#    message(SEND_ERROR "FAILING_EXE_ALIAS_ERROR_VARIABLE try_compile succeeded, but it was expected to fail ${OUTPUT}.")
+#  endif()
+
+  # Do the actual try_compile tests for ALIAS targets
+  add_library(exp_systemlib_alias ALIAS exp_systemlib)
+  try_compile(EXP_LIBRARY_ALIAS_ERROR_VARIABLE
+          "${CMAKE_CURRENT_BINARY_DIR}/test_library_alias"
+          "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
+          LINK_LIBRARIES exp_systemlib_alias
+          OUTPUT_VARIABLE OUTPUT
+          NO_CACHE
+  )
+  if(NOT EXP_LIBRARY_ALIAS_ERROR_VARIABLE)
+    message(SEND_ERROR "EXP_LIBRARY_ALIAS_ERROR_VARIABLE try_compile failed with library aliased target, but it was expected to succeed ${OUTPUT}.")
+  endif()
+
+  # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable
+#  set(CMAKE_TRY_COMPILE_TARGET_TYPE MODULE)
+#  add_executable(exp_exe_alias ALIAS exp_testExe2)
+#  try_compile(EXP_EXE_ALIAS_ERROR_VARIABLE
+#          "${CMAKE_CURRENT_BINARY_DIR}/test_exe_alias"
+#          "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c"
+#          LINK_LIBRARIES exp_exe_alias
+#          OUTPUT_VARIABLE OUTPUT
+#          NO_CACHE
+#  )
+#  unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
+#  if(NOT EXP_EXE_ALIAS_ERROR_VARIABLE)
+#    message(SEND_ERROR "EXP_EXE_ALIAS_ERROR_VARIABLE try_compile failed with executable aliased target, but it was expected to succeed ${OUTPUT}.")
+#  endif()
+
+  add_library(bld_systemlib_alias ALIAS bld_systemlib)
+  try_compile(BLD_LIBRARY_ALIAS_ERROR_VARIABLE
+          "${CMAKE_CURRENT_BINARY_DIR}/test_library_alias"
+          "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
+          LINK_LIBRARIES bld_systemlib_alias
+          OUTPUT_VARIABLE OUTPUT
+          NO_CACHE
+  )
+  if(NOT BLD_LIBRARY_ALIAS_ERROR_VARIABLE)
+    message(SEND_ERROR "BLD_LIBRARY_ALIAS_ERROR_VARIABLE try_compile failed with library aliased target, but it was expected to succeed. ${OUTPUT}")
+  endif()
+
+  # FIXME: CMAKE_TRY_COMPILE_TARGET_TYPE=MODULE is needed to properly link and test targets linked to an executable
+#  set(CMAKE_TRY_COMPILE_TARGET_TYPE MODULE)
+#  add_executable(bld_exe_alias ALIAS bld_testExe2)
+#  try_compile(BLD_EXE_ALIAS_ERROR_VARIABLE
+#          "${CMAKE_CURRENT_BINARY_DIR}/test_exe_alias"
+#          "${CMAKE_CURRENT_SOURCE_DIR}/imp_mod1.c"
+#          LINK_LIBRARIES bld_exe_alias
+#          OUTPUT_VARIABLE OUTPUT
+#          NO_CACHE
+#  )
+#  unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
+#  if(NOT BLD_EXE_ALIAS_ERROR_VARIABLE)
+#    message(SEND_ERROR "BLD_EXE_ALIAS_ERROR_VARIABLE try_compile failed with executable aliased target, but it was expected to succeed. ${OUTPUT}")
+#  endif()
 endif()
 
 #---------------------------------------------------------------------------------
diff --git a/Tests/ExportImport/Import/A/deps_iface.c b/Tests/ExportImport/Import/A/deps_iface.c
index afb1af0..fd2c47f 100644
--- a/Tests/ExportImport/Import/A/deps_iface.c
+++ b/Tests/ExportImport/Import/A/deps_iface.c
@@ -26,7 +26,7 @@
 
 extern int testLibDepends(void);
 
-int main()
+int main(void)
 {
   return testLibDepends();
 }
diff --git a/Tests/ExportImport/Import/A/imp_mod1.c b/Tests/ExportImport/Import/A/imp_mod1.c
index 9385d55..138962e 100644
--- a/Tests/ExportImport/Import/A/imp_mod1.c
+++ b/Tests/ExportImport/Import/A/imp_mod1.c
@@ -7,7 +7,7 @@
 testExe2_IMPORT int testExe2Func(void);
 testExe2_IMPORT int testExe2lib(void);
 
-int imp_mod1()
+int imp_mod1(void)
 {
   return testExe2Func() + testExe2lib();
 }
diff --git a/Tests/ExportImport/Import/A/imp_testExe1.c b/Tests/ExportImport/Import/A/imp_testExe1.c
index d3b0e9e..e409b1c 100644
--- a/Tests/ExportImport/Import/A/imp_testExe1.c
+++ b/Tests/ExportImport/Import/A/imp_testExe1.c
@@ -21,7 +21,7 @@
 #endif
 extern int testLib4libcfg(void);
 
-int main()
+int main(void)
 {
   return (testLib2() + generated_by_testExe1() + testLib3() + testLib4() +
           testLib5() + testLib6() + testLib7() + testLibCycleA1() +
diff --git a/Tests/ExportImport/Import/A/imp_testExeAbs1.c b/Tests/ExportImport/Import/A/imp_testExeAbs1.c
index fd05242..07d33a5 100644
--- a/Tests/ExportImport/Import/A/imp_testExeAbs1.c
+++ b/Tests/ExportImport/Import/A/imp_testExeAbs1.c
@@ -7,7 +7,7 @@
 #ifndef testLibAbs1b
 #  error "testLibAbs1b not defined"
 #endif
-int main()
+int main(void)
 {
   return 0 + testLibAbs1();
 }
diff --git a/Tests/ExportImport/Import/A/imp_testLib8.c b/Tests/ExportImport/Import/A/imp_testLib8.c
index 2749b17..ef97dbe 100644
--- a/Tests/ExportImport/Import/A/imp_testLib8.c
+++ b/Tests/ExportImport/Import/A/imp_testLib8.c
@@ -2,7 +2,7 @@
 int testLib8A(void);
 int testLib8B(void);
 
-int main()
+int main(void)
 {
   return (testLib8A() + testLib8B());
 }
diff --git a/Tests/ExportImport/Import/A/imp_testLib9.c b/Tests/ExportImport/Import/A/imp_testLib9.c
index e014857..2a8d8d5 100644
--- a/Tests/ExportImport/Import/A/imp_testLib9.c
+++ b/Tests/ExportImport/Import/A/imp_testLib9.c
@@ -10,7 +10,7 @@
 
 int testLib9(void);
 
-int main()
+int main(void)
 {
   return testLib9();
 }
diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt
index e6dcd65..83c87a8 100644
--- a/Tests/ExportImport/Import/CMakeLists.txt
+++ b/Tests/ExportImport/Import/CMakeLists.txt
@@ -1,5 +1,6 @@
 cmake_minimum_required (VERSION 2.7.20090711)
 cmake_policy(SET CMP0025 NEW)
+cmake_policy(SET CMP0028 NEW)
 if(POLICY CMP0129)
   cmake_policy(SET CMP0129 NEW)
 endif()
diff --git a/Tests/ExportImport/Import/imp_testTransExe1.c b/Tests/ExportImport/Import/imp_testTransExe1.c
index 360a112..579c992 100644
--- a/Tests/ExportImport/Import/imp_testTransExe1.c
+++ b/Tests/ExportImport/Import/imp_testTransExe1.c
@@ -1,6 +1,6 @@
 extern int imp_lib1(void);
 
-int main()
+int main(void)
 {
   return imp_lib1();
 }
diff --git a/Tests/ExportImport/InitialCache.cmake.in b/Tests/ExportImport/InitialCache.cmake.in
index 44cd179..55aef86 100644
--- a/Tests/ExportImport/InitialCache.cmake.in
+++ b/Tests/ExportImport/InitialCache.cmake.in
@@ -15,3 +15,4 @@
 set(CMAKE_SKIP_RPATH ON CACHE BOOL "No RPATH")
 set(CMAKE_GNUtoMS "@ExportImport_GNUtoMS@" CACHE BOOL "CMAKE_GNUtoMS")
 set(CMake_TEST_CUDA "@CMake_TEST_CUDA@" CACHE BOOL "CMake_TEST_CUDA")
+set(Foo_DIR "@CMAKE_CURRENT_SOURCE_DIR@/External" CACHE PATH "Foo cmake package directory")
diff --git a/Tests/ExportImport/main.c b/Tests/ExportImport/main.c
index f8b643a..8488f4e 100644
--- a/Tests/ExportImport/main.c
+++ b/Tests/ExportImport/main.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/FindALSA/Test/main.c b/Tests/FindALSA/Test/main.c
index d3303d0..53831a9 100644
--- a/Tests/FindALSA/Test/main.c
+++ b/Tests/FindALSA/Test/main.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   printf("Found ALSA version %s, expected version %s\n",
          snd_asoundlib_version(), CMAKE_EXPECTED_ALSA_VERSION);
diff --git a/Tests/FindBLAS/Test/main.c b/Tests/FindBLAS/Test/main.c
index 4fc9fe4..4ce1efb 100644
--- a/Tests/FindBLAS/Test/main.c
+++ b/Tests/FindBLAS/Test/main.c
@@ -13,7 +13,7 @@
 // declare what parts of the blas C-API we need
 void dswap_(blas_int* N, double* X, blas_int* incX, double* Y, blas_int* incY);
 
-int main()
+int main(void)
 {
   double x[4] = { 1, 2, 3, 4 };
   double y[4] = { 8, 7, 7, 6 };
diff --git a/Tests/FindBZip2/Test/main.c b/Tests/FindBZip2/Test/main.c
index 8e24c94..b3cf34b 100644
--- a/Tests/FindBZip2/Test/main.c
+++ b/Tests/FindBZip2/Test/main.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-int main()
+int main(void)
 {
   int chunksize = 1024;
   FILE* file = fopen("test.bzip2", "wb");
diff --git a/Tests/FindCURL/Test/main.c b/Tests/FindCURL/Test/main.c
index 263775f..82075f1 100644
--- a/Tests/FindCURL/Test/main.c
+++ b/Tests/FindCURL/Test/main.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-int main()
+int main(void)
 {
   struct curl_slist* slist;
 
diff --git a/Tests/FindCups/Test/main.c b/Tests/FindCups/Test/main.c
index b69d621..86952db 100644
--- a/Tests/FindCups/Test/main.c
+++ b/Tests/FindCups/Test/main.c
@@ -1,6 +1,6 @@
 #include <cups/cups.h>
 
-int main()
+int main(void)
 {
   int num_options = 0;
   cups_option_t* options = NULL;
diff --git a/Tests/FindDevIL/Test/main.c b/Tests/FindDevIL/Test/main.c
index 4a07087..dfb2f63 100644
--- a/Tests/FindDevIL/Test/main.c
+++ b/Tests/FindDevIL/Test/main.c
@@ -1,6 +1,6 @@
 #include <IL/il.h>
 
-int main()
+int main(void)
 {
   // Test 1 requires to link to the library.
   ilInit();
diff --git a/Tests/FindDevIL/Test/main_ilu.c b/Tests/FindDevIL/Test/main_ilu.c
index a9e7819..ac7237a 100644
--- a/Tests/FindDevIL/Test/main_ilu.c
+++ b/Tests/FindDevIL/Test/main_ilu.c
@@ -1,6 +1,6 @@
 #include <IL/ilu.h>
 
-int main()
+int main(void)
 {
   // IL Utilities requires only initialization.
   // Unlike main DevIL there are no shutdown function.
diff --git a/Tests/FindEXPAT/Test/main.c b/Tests/FindEXPAT/Test/main.c
index 94ee3ef..703aaf9 100644
--- a/Tests/FindEXPAT/Test/main.c
+++ b/Tests/FindEXPAT/Test/main.c
@@ -3,7 +3,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   XML_Expat_Version expat_version;
   char expat_version_string[16];
diff --git a/Tests/FindFontconfig/Test/main.c b/Tests/FindFontconfig/Test/main.c
index c5b5963..96b0af1 100644
--- a/Tests/FindFontconfig/Test/main.c
+++ b/Tests/FindFontconfig/Test/main.c
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   FcInit();
   printf("Found Fontconfig.\n");
diff --git a/Tests/FindFreetype/Test/main.c b/Tests/FindFreetype/Test/main.c
index bb838a5..315a6cb 100644
--- a/Tests/FindFreetype/Test/main.c
+++ b/Tests/FindFreetype/Test/main.c
@@ -3,7 +3,7 @@
 #include FT_FREETYPE_H
 #include <string.h>
 
-int main()
+int main(void)
 {
   FT_Library library;
   FT_Error error;
diff --git a/Tests/FindGDAL/Test/main.c b/Tests/FindGDAL/Test/main.c
index 7b31a13..2e19843 100644
--- a/Tests/FindGDAL/Test/main.c
+++ b/Tests/FindGDAL/Test/main.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   printf("Found GDAL version %s, expected version %s\n", GDAL_RELEASE_NAME,
          CMAKE_EXPECTED_GDAL_VERSION);
diff --git a/Tests/FindGIF/Test/main.c b/Tests/FindGIF/Test/main.c
index 656a99c..fa2c224 100644
--- a/Tests/FindGIF/Test/main.c
+++ b/Tests/FindGIF/Test/main.c
@@ -8,7 +8,7 @@
 #  define GIFLIB_MAJOR 4
 #endif
 
-int main()
+int main(void)
 {
   // because of the API changes we have to test different functions depending
   // on the version of GIFLIB
diff --git a/Tests/FindGLUT/Test/main.c b/Tests/FindGLUT/Test/main.c
index 1c8569c..02ac34f 100644
--- a/Tests/FindGLUT/Test/main.c
+++ b/Tests/FindGLUT/Test/main.c
@@ -1,7 +1,7 @@
 #include <GL/glut.h>
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   /* The following should call exit(1) and print
       freeglut  ERROR:  Function <glutCreateWindow> called
diff --git a/Tests/FindGnuTLS/Test/main.c b/Tests/FindGnuTLS/Test/main.c
index 1105358..c379cc2 100644
--- a/Tests/FindGnuTLS/Test/main.c
+++ b/Tests/FindGnuTLS/Test/main.c
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   // test the linker
   gnutls_session_t session;
diff --git a/Tests/FindImageMagick/Test/main_magick_wand.c b/Tests/FindImageMagick/Test/main_magick_wand.c
index fa6d170..52d179d 100644
--- a/Tests/FindImageMagick/Test/main_magick_wand.c
+++ b/Tests/FindImageMagick/Test/main_magick_wand.c
@@ -1,6 +1,6 @@
 #include <wand/MagickWand.h>
 
-int main()
+int main(void)
 {
   MagickWand* wand = NewMagickWand();
   wand = DestroyMagickWand(wand);
diff --git a/Tests/FindJPEG/Test/main.c b/Tests/FindJPEG/Test/main.c
index 5a67faa..0116cb6 100644
--- a/Tests/FindJPEG/Test/main.c
+++ b/Tests/FindJPEG/Test/main.c
@@ -4,7 +4,7 @@
 #include <jpeglib.h>
 // clang-format on
 
-int main()
+int main(void)
 {
   /* Without any JPEG file to open, test that the call fails as
      expected.  This tests that linking worked. */
diff --git a/Tests/FindJasper/Test/main.c b/Tests/FindJasper/Test/main.c
index 771344d..242ff7d 100644
--- a/Tests/FindJasper/Test/main.c
+++ b/Tests/FindJasper/Test/main.c
@@ -4,7 +4,7 @@
 #include <jasper/jasper.h>
 // clang-format on
 
-int main()
+int main(void)
 {
   /* Without any JPEG file to open, test that the call fails as
      expected.  This tests that linking worked. */
diff --git a/Tests/FindLAPACK/Test/main.c b/Tests/FindLAPACK/Test/main.c
index dd33fb3..3c7ad9f 100644
--- a/Tests/FindLAPACK/Test/main.c
+++ b/Tests/FindLAPACK/Test/main.c
@@ -14,7 +14,7 @@
 void dgesv_(blas_int*, blas_int*, double*, blas_int*, blas_int*, double*,
             blas_int*, blas_int*);
 
-int main()
+int main(void)
 {
   double A[8] = {
     0, 1, 2, 3, 4, 5, 6, 7,
diff --git a/Tests/FindLibLZMA/Test/main.c b/Tests/FindLibLZMA/Test/main.c
index 06e8065..0b3de31 100644
--- a/Tests/FindLibLZMA/Test/main.c
+++ b/Tests/FindLibLZMA/Test/main.c
@@ -4,7 +4,7 @@
 
 static const uint8_t test_string[9] = "123456789";
 
-int main()
+int main(void)
 {
   static const uint32_t test_vector = 0xCBF43926;
 
diff --git a/Tests/FindLibRHash/Test/main.c b/Tests/FindLibRHash/Test/main.c
index 201dced..4cc6394 100644
--- a/Tests/FindLibRHash/Test/main.c
+++ b/Tests/FindLibRHash/Test/main.c
@@ -1,6 +1,6 @@
 #include <rhash.h>
 
-int main()
+int main(void)
 {
   rhash_library_init();
   return 0;
diff --git a/Tests/FindLibUV/Test/main.c b/Tests/FindLibUV/Test/main.c
index cbd0db3..a14adbb 100644
--- a/Tests/FindLibUV/Test/main.c
+++ b/Tests/FindLibUV/Test/main.c
@@ -1,6 +1,6 @@
 #include <uv.h>
 
-int main()
+int main(void)
 {
   uv_loop_close(uv_default_loop());
   return 0;
diff --git a/Tests/FindLibXml2/Test/main.c b/Tests/FindLibXml2/Test/main.c
index 264f07d..4ded2f7 100644
--- a/Tests/FindLibXml2/Test/main.c
+++ b/Tests/FindLibXml2/Test/main.c
@@ -2,7 +2,7 @@
 #include <libxml/tree.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   xmlDoc* doc;
 
diff --git a/Tests/FindLibXslt/Test/libexslt.c b/Tests/FindLibXslt/Test/libexslt.c
index ea6eb3d..5916024 100644
--- a/Tests/FindLibXslt/Test/libexslt.c
+++ b/Tests/FindLibXslt/Test/libexslt.c
@@ -2,7 +2,7 @@
 #include <libxslt/xslt.h>
 #include <libxslt/xsltInternals.h>
 
-int main()
+int main(void)
 {
   xsltInit();
 
diff --git a/Tests/FindLibXslt/Test/libxslt.c b/Tests/FindLibXslt/Test/libxslt.c
index 5b3d766..4a149c1 100644
--- a/Tests/FindLibXslt/Test/libxslt.c
+++ b/Tests/FindLibXslt/Test/libxslt.c
@@ -4,7 +4,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   xsltInit();
 
diff --git a/Tests/FindLibinput/Test/main.c b/Tests/FindLibinput/Test/main.c
index 3919962..a6b1aa4 100644
--- a/Tests/FindLibinput/Test/main.c
+++ b/Tests/FindLibinput/Test/main.c
@@ -1,7 +1,7 @@
 #include <libinput.h>
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   struct libinput_interface interface;
   interface.open_restricted = 0;
diff --git a/Tests/FindODBC/Test/main.c b/Tests/FindODBC/Test/main.c
index 34f279c..6c4318b 100644
--- a/Tests/FindODBC/Test/main.c
+++ b/Tests/FindODBC/Test/main.c
@@ -3,7 +3,7 @@
 #endif
 #include <sql.h>
 
-int main()
+int main(void)
 {
   SQLHENV env;
   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
diff --git a/Tests/FindOpenACC/CTest/main.c b/Tests/FindOpenACC/CTest/main.c
index 53b6cae..6c005c1 100644
--- a/Tests/FindOpenACC/CTest/main.c
+++ b/Tests/FindOpenACC/CTest/main.c
@@ -8,7 +8,7 @@
     r[i] = a[i] + b[i];
 }
 
-int main()
+int main(void)
 {
   int n = 100000; /* vector length */
   float* a;       /* input vector 1 */
diff --git a/Tests/FindOpenCL/Test/main.c b/Tests/FindOpenCL/Test/main.c
index 2fe949b..d3d0cfb 100644
--- a/Tests/FindOpenCL/Test/main.c
+++ b/Tests/FindOpenCL/Test/main.c
@@ -4,7 +4,7 @@
 #  include <CL/cl.h>
 #endif
 
-int main()
+int main(void)
 {
   cl_uint platformIdCount;
 
diff --git a/Tests/FindOpenGL/Test/main.c b/Tests/FindOpenGL/Test/main.c
index e1f25c6..88a4a8a 100644
--- a/Tests/FindOpenGL/Test/main.c
+++ b/Tests/FindOpenGL/Test/main.c
@@ -9,7 +9,7 @@
 
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   /* Reference a GL symbol without requiring a context at runtime.  */
   printf("&glGetString = %p\n", &glGetString);
diff --git a/Tests/FindOpenGL/Test/main_gles2.c b/Tests/FindOpenGL/Test/main_gles2.c
index 52f5936..355795b 100644
--- a/Tests/FindOpenGL/Test/main_gles2.c
+++ b/Tests/FindOpenGL/Test/main_gles2.c
@@ -9,7 +9,7 @@
 
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   /* Reference a GL symbol without requiring a context at runtime.  */
   printf("&glGetString = %p\n", &glGetString);
diff --git a/Tests/FindOpenGL/Test/main_gles3.c b/Tests/FindOpenGL/Test/main_gles3.c
index 875f73c..383954f7 100644
--- a/Tests/FindOpenGL/Test/main_gles3.c
+++ b/Tests/FindOpenGL/Test/main_gles3.c
@@ -9,7 +9,7 @@
 
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   /* Reference a GL symbol without requiring a context at runtime.  */
   printf("&glGetString = %p\n", &glGetString);
diff --git a/Tests/FindOpenMP/Test/main.c b/Tests/FindOpenMP/Test/main.c
index 4f0e874..9fb67e4 100644
--- a/Tests/FindOpenMP/Test/main.c
+++ b/Tests/FindOpenMP/Test/main.c
@@ -1,5 +1,5 @@
 #include <omp.h>
-int main()
+int main(void)
 {
 #ifndef _OPENMP
   breaks_on_purpose
diff --git a/Tests/FindOpenMP/Test/scaltest.c b/Tests/FindOpenMP/Test/scaltest.c
index 4678b87..be48827 100644
--- a/Tests/FindOpenMP/Test/scaltest.c
+++ b/Tests/FindOpenMP/Test/scaltest.c
@@ -6,7 +6,7 @@
 #endif
 int scalprod(int n, double* x, double* y, double* res);
 
-int main()
+int main(void)
 {
   double a[5] = { 1., 2., 3., 4., 5. };
   double b[5] = { 2., 3., 4., 5., 6. };
diff --git a/Tests/FindPNG/Test/main.c b/Tests/FindPNG/Test/main.c
index b33b28e..05b55c0 100644
--- a/Tests/FindPNG/Test/main.c
+++ b/Tests/FindPNG/Test/main.c
@@ -2,7 +2,7 @@
 #include <png.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   png_uint_32 png_version;
   char png_version_string[16];
diff --git a/Tests/FindPackageTest/Exporter/dummy.c b/Tests/FindPackageTest/Exporter/dummy.c
index f8b643a..8488f4e 100644
--- a/Tests/FindPackageTest/Exporter/dummy.c
+++ b/Tests/FindPackageTest/Exporter/dummy.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/FindPostgreSQL/Test/main.c b/Tests/FindPostgreSQL/Test/main.c
index a63377a..b5fcf64 100644
--- a/Tests/FindPostgreSQL/Test/main.c
+++ b/Tests/FindPostgreSQL/Test/main.c
@@ -2,7 +2,7 @@
 #include <stdio.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   int version = PQlibVersion();
   char version_string[100];
diff --git a/Tests/FindPython/display_time.c b/Tests/FindPython/display_time.c
index 0e78434..568d510 100644
--- a/Tests/FindPython/display_time.c
+++ b/Tests/FindPython/display_time.c
@@ -6,7 +6,7 @@
 
 #include "display_time.h"
 
-void display_time()
+void display_time(void)
 {
 #if defined(PYTHON3)
   wchar_t* program = Py_DecodeLocale("display_time", NULL);
diff --git a/Tests/FindPython/main.c b/Tests/FindPython/main.c
index 0acba29..0119ce0 100644
--- a/Tests/FindPython/main.c
+++ b/Tests/FindPython/main.c
@@ -1,7 +1,7 @@
 
 #include "display_time.h"
 
-int main()
+int main(void)
 {
   display_time();
 }
diff --git a/Tests/FindSDL/Test/main.c b/Tests/FindSDL/Test/main.c
index 057289c..3b774f5 100644
--- a/Tests/FindSDL/Test/main.c
+++ b/Tests/FindSDL/Test/main.c
@@ -1,6 +1,6 @@
 #include <SDL.h>
 
-int main()
+int main(void)
 {
   // Test 1 requires headers only.
   SDL_version compiled;
diff --git a/Tests/FindSQLite3/Test/main.c b/Tests/FindSQLite3/Test/main.c
index fb17c67..f812034 100644
--- a/Tests/FindSQLite3/Test/main.c
+++ b/Tests/FindSQLite3/Test/main.c
@@ -1,7 +1,7 @@
 #include <sqlite3.h>
 #include <string.h>
 
-int main()
+int main(void)
 {
   char sqlite3_version[] = SQLITE_VERSION;
 
diff --git a/Tests/FindTIFF/Test/main.c b/Tests/FindTIFF/Test/main.c
index 9182652..bce4a3e 100644
--- a/Tests/FindTIFF/Test/main.c
+++ b/Tests/FindTIFF/Test/main.c
@@ -1,7 +1,7 @@
 #include <assert.h>
 #include <tiffio.h>
 
-int main()
+int main(void)
 {
   /* Without any TIFF file to open, test that the call fails as
      expected.  This tests that linking worked. */
diff --git a/Tests/FindVulkan/CMakeLists.txt b/Tests/FindVulkan/CMakeLists.txt
index 46ce1c6..d7c99ce 100644
--- a/Tests/FindVulkan/CMakeLists.txt
+++ b/Tests/FindVulkan/CMakeLists.txt
@@ -5,6 +5,7 @@
   "${CMake_BINARY_DIR}/Tests/FindVulkan/Test"
   ${build_generator_args}
   --build-project TestFindVulkan
-  --build-options ${build_options}
+  # Use --fresh to make testing multiple SDK versions on the same computer easier
+  --build-options ${build_options} --fresh
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
diff --git a/Tests/FindVulkan/Test/Run-glslangValidator.cmake b/Tests/FindVulkan/Test/Run-glslangValidator.cmake
index 27fd950..fd7867f 100644
--- a/Tests/FindVulkan/Test/Run-glslangValidator.cmake
+++ b/Tests/FindVulkan/Test/Run-glslangValidator.cmake
@@ -11,8 +11,10 @@
     message(SEND_ERROR "Result of ${exe_display} --help is ${result}, should be 1")
   endif()
 
-  if(NOT output MATCHES "^Usage: glslangValidator")
-    message(SEND_ERROR "Output of ${exe_display} --help is \"${output}\", should begin with \"Usage: glslangValidator\"")
+  # NOTE: Newer version prefer just "glslang" since it's no longer really just a validator.
+  # This approach is still compatible with older version that output glslangValidator
+  if(NOT output MATCHES "^Usage: glslang")
+    message(SEND_ERROR "Output of ${exe_display} --help is \"${output}\", should begin with \"Usage: glslang\"")
   endif()
 endfunction()
 
diff --git a/Tests/FindVulkan/Test/main-SPIRV-Tools.c b/Tests/FindVulkan/Test/main-SPIRV-Tools.c
index 097198d..c38f38d 100644
--- a/Tests/FindVulkan/Test/main-SPIRV-Tools.c
+++ b/Tests/FindVulkan/Test/main-SPIRV-Tools.c
@@ -2,7 +2,7 @@
 #include <spirv-tools/libspirv.h>
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   const char* spv_version = spvSoftwareVersionString();
   const char* spv_details = spvSoftwareVersionDetailsString();
diff --git a/Tests/FindVulkan/Test/main.c b/Tests/FindVulkan/Test/main.c
index 1bff651..78eaa4d 100644
--- a/Tests/FindVulkan/Test/main.c
+++ b/Tests/FindVulkan/Test/main.c
@@ -1,6 +1,6 @@
 #include <vulkan/vulkan.h>
 
-int main()
+int main(void)
 {
   VkInstanceCreateInfo instanceCreateInfo = { 0 };
   instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
diff --git a/Tests/Fortran/mainc.c b/Tests/Fortran/mainc.c
index 9efafc5..607b9fd 100644
--- a/Tests/Fortran/mainc.c
+++ b/Tests/Fortran/mainc.c
@@ -1,5 +1,5 @@
 extern int myc(void);
-int main()
+int main(void)
 {
   return myc();
 }
diff --git a/Tests/Fortran/maincxx.c b/Tests/Fortran/maincxx.c
index d35ea7e..3056d96 100644
--- a/Tests/Fortran/maincxx.c
+++ b/Tests/Fortran/maincxx.c
@@ -1,6 +1,6 @@
 extern int myc(void);
 extern int mycxx(void);
-int main()
+int main(void)
 {
   return myc() + mycxx();
 }
diff --git a/Tests/FortranModules/Issue25252-iface-sources/lib.c b/Tests/FortranModules/Issue25252-iface-sources/lib.c
index 6ccdb8d..894540c 100644
--- a/Tests/FortranModules/Issue25252-iface-sources/lib.c
+++ b/Tests/FortranModules/Issue25252-iface-sources/lib.c
@@ -1,4 +1,4 @@
-int f()
+int f(void)
 {
   return 0;
 }
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt
index df7cda0..be750e1 100644
--- a/Tests/GeneratorExpression/CMakeLists.txt
+++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -70,6 +70,7 @@
     -Dtest_strequal_comma=$<STREQUAL:$<COMMA>,$<COMMA>>
     -Dtest_strequal_semicolon=$<STREQUAL:$<SEMICOLON>,$<SEMICOLON>>
     -Dtest_strequal_angle_r_comma=$<STREQUAL:$<ANGLE-R>,$<COMMA>>
+    -Dtest_strequal_quote=$<STREQUAL:$<QUOTE>,$<QUOTE>>
     -Dtest_strequal_both_empty=$<STREQUAL:,>
     -Dtest_strequal_one_empty=$<STREQUAL:something,>
     -Dtest_inlist_true=$<IN_LIST:a,a$<SEMICOLON>b>
diff --git a/Tests/GeneratorExpression/check-part1.cmake b/Tests/GeneratorExpression/check-part1.cmake
index 41bcd6d..ddf2507 100644
--- a/Tests/GeneratorExpression/check-part1.cmake
+++ b/Tests/GeneratorExpression/check-part1.cmake
@@ -47,6 +47,7 @@
 check(test_strequal_comma "1")
 check(test_strequal_semicolon "1")
 check(test_strequal_angle_r_comma "0")
+check(test_strequal_quote "1")
 check(test_strequal_both_empty "1")
 check(test_strequal_one_empty "0")
 check(test_inlist_true "1")
diff --git a/Tests/GeneratorExpression/objlib1.c b/Tests/GeneratorExpression/objlib1.c
index 98a95a4..b33aa48 100644
--- a/Tests/GeneratorExpression/objlib1.c
+++ b/Tests/GeneratorExpression/objlib1.c
@@ -1,4 +1,4 @@
 
-void objlib1()
+void objlib1(void)
 {
 }
diff --git a/Tests/GeneratorExpression/objlib2.c b/Tests/GeneratorExpression/objlib2.c
index b2c1050..5543f75 100644
--- a/Tests/GeneratorExpression/objlib2.c
+++ b/Tests/GeneratorExpression/objlib2.c
@@ -1,4 +1,4 @@
 
-void objlib2()
+void objlib2(void)
 {
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c
index 5d857dd..ef922a3 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/test.c
@@ -1,4 +1,4 @@
-int test_b()
+int test_b(void)
 {
   return 2;
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c
index 66ee6f3..1df9613 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder/testcase.c
@@ -1,4 +1,4 @@
-int test_f()
+int test_f(void)
 {
   return 1;
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c
index 83589ba..8bcb13a 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test.c
@@ -1,4 +1,4 @@
-int test_c()
+int test_c(void)
 {
   return 1;
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c
index 82f9a52..24e2d75 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/subfolder_test_0.c
@@ -1,4 +1,4 @@
-int test_d()
+int test_d(void)
 {
   return 1;
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c
index feba80e..5dd1214 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/test.c
@@ -1,4 +1,4 @@
-int test_a()
+int test_a(void)
 {
   return 1;
 }
diff --git a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c
index 943c19d..658f5ab 100644
--- a/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c
+++ b/Tests/GhsMulti/GhsMultiDuplicateSourceFilenames/testCase.c
@@ -1,4 +1,4 @@
-int test_e()
+int test_e(void)
 {
   return 1;
 }
diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c
index d1bce33..9282163 100644
--- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c
+++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/kernel.c
@@ -1,7 +1,7 @@
 #include "INTEGRITY.h"
 #include "boottable.h"
 
-void main()
+void main(void)
 {
   Exit(0);
 }
diff --git a/Tests/IncludeDirectories/StandardIncludeDirectories/main.c b/Tests/IncludeDirectories/StandardIncludeDirectories/main.c
index edfe9ce..29a2ee1 100644
--- a/Tests/IncludeDirectories/StandardIncludeDirectories/main.c
+++ b/Tests/IncludeDirectories/StandardIncludeDirectories/main.c
@@ -1,5 +1,5 @@
 #include "StdIncDir.h"
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c
index f8b643a..8488f4e 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c
+++ b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/main.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/JavaExportImport/main.c b/Tests/JavaExportImport/main.c
index f8b643a..8488f4e 100644
--- a/Tests/JavaExportImport/main.c
+++ b/Tests/JavaExportImport/main.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/LibName/bar.c b/Tests/LibName/bar.c
index c6c1e66..b1c5cf2 100644
--- a/Tests/LibName/bar.c
+++ b/Tests/LibName/bar.c
@@ -2,6 +2,6 @@
 __declspec(dllexport)
 #endif
 
-  extern void foo()
+  extern void foo(void)
 {
 }
diff --git a/Tests/LibName/foo.c b/Tests/LibName/foo.c
index 52e8d89..1bdb58b 100644
--- a/Tests/LibName/foo.c
+++ b/Tests/LibName/foo.c
@@ -1,11 +1,11 @@
 #ifdef _WIN32
 __declspec(dllimport)
 #endif
-  extern void foo();
+  extern void foo(void);
 #ifdef _WIN32
 __declspec(dllexport)
 #endif
-  void bar()
+  void bar(void)
 {
   foo();
 }
diff --git a/Tests/LibName/foobar.c b/Tests/LibName/foobar.c
index 2f28d30..fa7d3e9 100644
--- a/Tests/LibName/foobar.c
+++ b/Tests/LibName/foobar.c
@@ -3,7 +3,7 @@
 #endif
   extern void bar();
 
-int main()
+int main(void)
 {
   bar();
   return 0;
diff --git a/Tests/LinkLanguage/LinkLanguage.c b/Tests/LinkLanguage/LinkLanguage.c
index 37946c7..18ddb78 100644
--- a/Tests/LinkLanguage/LinkLanguage.c
+++ b/Tests/LinkLanguage/LinkLanguage.c
@@ -1,5 +1,5 @@
 extern int foo(void);
-int main()
+int main(void)
 {
   return foo();
 }
diff --git a/Tests/LinkLine/Exec.c b/Tests/LinkLine/Exec.c
index 807a7a8..11b5650 100644
--- a/Tests/LinkLine/Exec.c
+++ b/Tests/LinkLine/Exec.c
@@ -1,7 +1,7 @@
 void OneFunc();
 void TwoFunc();
 
-int main()
+int main(void)
 {
   OneFunc();
   TwoFunc();
diff --git a/Tests/LinkLine/One.c b/Tests/LinkLine/One.c
index 856d0d1..4f3ad63 100644
--- a/Tests/LinkLine/One.c
+++ b/Tests/LinkLine/One.c
@@ -1,6 +1,6 @@
-void TwoFunc();
+void TwoFunc(void);
 
-void OneFunc()
+void OneFunc(void)
 {
   static int i = 0;
   ++i;
diff --git a/Tests/LinkLine/Two.c b/Tests/LinkLine/Two.c
index 5fc212e..ac84367 100644
--- a/Tests/LinkLine/Two.c
+++ b/Tests/LinkLine/Two.c
@@ -1,6 +1,6 @@
-void OneFunc();
+void OneFunc(void);
 
-void TwoFunc()
+void TwoFunc(void)
 {
   static int i = 0;
   ++i;
diff --git a/Tests/LinkLineOrder/Exec1.c b/Tests/LinkLineOrder/Exec1.c
index 9bbf0f6..e47841d 100644
--- a/Tests/LinkLineOrder/Exec1.c
+++ b/Tests/LinkLineOrder/Exec1.c
@@ -1,7 +1,7 @@
 /* Directly depends on One */
 void OneFunc();
 
-int main()
+int main(void)
 {
   OneFunc();
   return 0;
diff --git a/Tests/LinkLineOrder/Exec2.c b/Tests/LinkLineOrder/Exec2.c
index 91b8575..d60c94e 100644
--- a/Tests/LinkLineOrder/Exec2.c
+++ b/Tests/LinkLineOrder/Exec2.c
@@ -1,7 +1,7 @@
 /* Directly depends on Two */
 void TwoFunc();
 
-int main()
+int main(void)
 {
   TwoFunc();
   return 0;
diff --git a/Tests/LinkLineOrder/NoDepA.c b/Tests/LinkLineOrder/NoDepA.c
index 76f97bc..72333bc 100644
--- a/Tests/LinkLineOrder/NoDepA.c
+++ b/Tests/LinkLineOrder/NoDepA.c
@@ -1,7 +1,7 @@
 /* depends on NoDepB */
-void NoDepB_func();
+void NoDepB_func(void);
 
-void NoDepA_func()
+void NoDepA_func(void)
 {
   NoDepB_func();
 }
diff --git a/Tests/LinkLineOrder/NoDepB.c b/Tests/LinkLineOrder/NoDepB.c
index fa89ae9..a5c30e5 100644
--- a/Tests/LinkLineOrder/NoDepB.c
+++ b/Tests/LinkLineOrder/NoDepB.c
@@ -1,4 +1,4 @@
 /* No dependencies */
-void NoDepB_func()
+void NoDepB_func(void)
 {
 }
diff --git a/Tests/LinkLineOrder/NoDepC.c b/Tests/LinkLineOrder/NoDepC.c
index f05d962..cafb3fd 100644
--- a/Tests/LinkLineOrder/NoDepC.c
+++ b/Tests/LinkLineOrder/NoDepC.c
@@ -1,7 +1,7 @@
 /* depends on NoDepA */
-void NoDepA_func();
+void NoDepA_func(void);
 
-void NoDepC_func()
+void NoDepC_func(void)
 {
   NoDepA_func();
 }
diff --git a/Tests/LinkLineOrder/NoDepE.c b/Tests/LinkLineOrder/NoDepE.c
index c47bb85..6e33c7b 100644
--- a/Tests/LinkLineOrder/NoDepE.c
+++ b/Tests/LinkLineOrder/NoDepE.c
@@ -1,7 +1,7 @@
 /* depends on NoDepF */
-void NoDepF_func();
+void NoDepF_func(void);
 
-void NoDepE_func()
+void NoDepE_func(void)
 {
   static int firstcall = 1;
   if (firstcall) {
diff --git a/Tests/LinkLineOrder/NoDepF.c b/Tests/LinkLineOrder/NoDepF.c
index a814310..f4afcb5 100644
--- a/Tests/LinkLineOrder/NoDepF.c
+++ b/Tests/LinkLineOrder/NoDepF.c
@@ -1,7 +1,7 @@
 /* depends on NoDepE */
-void NoDepE_func();
+void NoDepE_func(void);
 
-void NoDepF_func()
+void NoDepF_func(void)
 {
   static int firstcall = 1;
   if (firstcall) {
diff --git a/Tests/LinkLineOrder/NoDepX.c b/Tests/LinkLineOrder/NoDepX.c
index c895dd1..c8de222 100644
--- a/Tests/LinkLineOrder/NoDepX.c
+++ b/Tests/LinkLineOrder/NoDepX.c
@@ -1,7 +1,7 @@
 /* depends on NoDepY*/
-void NoDepY_func();
+void NoDepY_func(void);
 
-void NoDepX_func()
+void NoDepX_func(void)
 {
   NoDepY_func();
 }
diff --git a/Tests/LinkLineOrder/NoDepY.c b/Tests/LinkLineOrder/NoDepY.c
index 1e6a4ae..dc492d5 100644
--- a/Tests/LinkLineOrder/NoDepY.c
+++ b/Tests/LinkLineOrder/NoDepY.c
@@ -1,4 +1,4 @@
 /* No dependencies */
-void NoDepY_func()
+void NoDepY_func(void)
 {
 }
diff --git a/Tests/LinkLineOrder/NoDepZ.c b/Tests/LinkLineOrder/NoDepZ.c
index 045e570..c866667 100644
--- a/Tests/LinkLineOrder/NoDepZ.c
+++ b/Tests/LinkLineOrder/NoDepZ.c
@@ -1,7 +1,7 @@
 /* depends on NoDepX */
-void NoDepX_func();
+void NoDepX_func(void);
 
-void NoDepZ_func()
+void NoDepZ_func(void)
 {
   NoDepX_func();
 }
diff --git a/Tests/LinkLineOrder/One.c b/Tests/LinkLineOrder/One.c
index b23b1ec..b95fa6d 100644
--- a/Tests/LinkLineOrder/One.c
+++ b/Tests/LinkLineOrder/One.c
@@ -1,9 +1,9 @@
 /* depends on NoDepC and NoDepE (and hence on NoDepA, NoDepB and */
 /*  NoDepF) */
-void NoDepC_func();
-void NoDepE_func();
+void NoDepC_func(void);
+void NoDepE_func(void);
 
-void OneFunc()
+void OneFunc(void)
 {
   NoDepC_func();
   NoDepE_func();
diff --git a/Tests/LinkLineOrder/Two.c b/Tests/LinkLineOrder/Two.c
index 6bffaa8..9955d67 100644
--- a/Tests/LinkLineOrder/Two.c
+++ b/Tests/LinkLineOrder/Two.c
@@ -1,7 +1,7 @@
-void OneFunc();
-void NoDepZ_func();
+void OneFunc(void);
+void NoDepZ_func(void);
 
-void TwoFunc()
+void TwoFunc(void)
 {
   OneFunc();
   NoDepZ_func();
diff --git a/Tests/MSVCDebugInformationFormat/override-CUDA.cmake b/Tests/MSVCDebugInformationFormat/override-CUDA.cmake
index f870775..eaa7bff 100644
--- a/Tests/MSVCDebugInformationFormat/override-CUDA.cmake
+++ b/Tests/MSVCDebugInformationFormat/override-CUDA.cmake
@@ -1,5 +1,6 @@
 set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded")
 string(REPLACE "-Z7" "-Z7;-DTEST_Z7" "${var}" "${${var}}")
+string(REPLACE "-gcodeview" "-gcodeview;-DTEST_Z7" "${var}" "${${var}}")
 set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase")
 string(REPLACE "-Zi" "-Zi;-DTEST_Zi" "${var}" "${${var}}")
 set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue")
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
index 2a8a152..4cd200a 100644
--- a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
+++ b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
@@ -21,14 +21,16 @@
     endforeach()
   endforeach()
 endforeach()
-if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang")
-  # LLVMFlang does not actually define these, so inject them
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 18.0)
+  # LLVMFlang < 18.0 does not define these, so inject them.
   set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "-D_MT")
   set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "-D_MT;-D_DLL")
   set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "-D_MT;-D_DEBUG")
   set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "-D_MT;-D_DEBUG;-D_DLL")
 endif()
-string(APPEND CMAKE_Fortran_FLAGS " -w")
+if(NOT CMAKE_Fortran_SIMULATE_ID STREQUAL "MSVC")
+  string(APPEND CMAKE_Fortran_FLAGS " -w")
+endif()
 
 function(verify_combinations threads lang src)
   set(verify_tc_config_ Release)
diff --git a/Tests/Module/CheckIPOSupported-C/bar.c b/Tests/Module/CheckIPOSupported-C/bar.c
index 680f213..728d8fb 100644
--- a/Tests/Module/CheckIPOSupported-C/bar.c
+++ b/Tests/Module/CheckIPOSupported-C/bar.c
@@ -1,4 +1,4 @@
-int bar()
+int bar(void)
 {
   return 0x42;
 }
diff --git a/Tests/Module/CheckIPOSupported-C/foo.c b/Tests/Module/CheckIPOSupported-C/foo.c
index 1e56597..6a64a99 100644
--- a/Tests/Module/CheckIPOSupported-C/foo.c
+++ b/Tests/Module/CheckIPOSupported-C/foo.c
@@ -1,4 +1,4 @@
-int foo()
+int foo(void)
 {
   return 0x42;
 }
diff --git a/Tests/Module/CheckIPOSupported-C/main.c b/Tests/Module/CheckIPOSupported-C/main.c
index 28ab26f..7d8ad9a 100644
--- a/Tests/Module/CheckIPOSupported-C/main.c
+++ b/Tests/Module/CheckIPOSupported-C/main.c
@@ -1,7 +1,7 @@
 int foo();
 int bar();
 
-int main()
+int main(void)
 {
   if (foo() != bar()) {
     return 1;
diff --git a/Tests/Module/CheckTypeSize/CheckTypeSize.c b/Tests/Module/CheckTypeSize/CheckTypeSize.c
index adfd2fc..eb1aa94 100644
--- a/Tests/Module/CheckTypeSize/CheckTypeSize.c
+++ b/Tests/Module/CheckTypeSize/CheckTypeSize.c
@@ -28,7 +28,7 @@
     result = 1;                                                               \
   } while (0)
 
-int main()
+int main(void)
 {
   int result = 0;
   struct somestruct x;
diff --git a/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c b/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c
index 487e66d..34c373c 100644
--- a/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c
+++ b/Tests/Module/WriteCompilerDetectionHeader/c_undefined.c
@@ -1,7 +1,7 @@
 
 #include "test_compiler_detection.h"
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/Module/WriteCompilerDetectionHeader/main.c b/Tests/Module/WriteCompilerDetectionHeader/main.c
index 3420c67..1253563 100644
--- a/Tests/Module/WriteCompilerDetectionHeader/main.c
+++ b/Tests/Module/WriteCompilerDetectionHeader/main.c
@@ -24,7 +24,7 @@
 #  error Expect no CXX features defined
 #endif
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/Module/WriteCompilerDetectionHeader/main_multi.c b/Tests/Module/WriteCompilerDetectionHeader/main_multi.c
index 28f9dae..3853671 100644
--- a/Tests/Module/WriteCompilerDetectionHeader/main_multi.c
+++ b/Tests/Module/WriteCompilerDetectionHeader/main_multi.c
@@ -24,7 +24,7 @@
 #  error Expect no CXX features defined
 #endif
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/OutOfBinary/outlib.c b/Tests/OutOfBinary/outlib.c
index d309ebe..3eaedea 100644
--- a/Tests/OutOfBinary/outlib.c
+++ b/Tests/OutOfBinary/outlib.c
@@ -1,4 +1,4 @@
-int outlib()
+int outlib(void)
 {
   return 456;
 }
diff --git a/Tests/PDBDirectoryAndName/myexe.c b/Tests/PDBDirectoryAndName/myexe.c
index fdb8b09..3d66794 100644
--- a/Tests/PDBDirectoryAndName/myexe.c
+++ b/Tests/PDBDirectoryAndName/myexe.c
@@ -1,8 +1,8 @@
-extern int mylibA();
-extern int mylibB();
-extern int mylibC();
-extern int mylibD();
-int main()
+extern int mylibA(void);
+extern int mylibB(void);
+extern int mylibC(void);
+extern int mylibD(void);
+int main(void)
 {
   return mylibA() + mylibB() + mylibC() + mylibD();
 }
diff --git a/Tests/PDBDirectoryAndName/myexe2.c b/Tests/PDBDirectoryAndName/myexe2.c
index 250d651..b32e0b1 100644
--- a/Tests/PDBDirectoryAndName/myexe2.c
+++ b/Tests/PDBDirectoryAndName/myexe2.c
@@ -1,6 +1,6 @@
-extern int mylibA();
-extern int mylibD();
-int main()
+extern int mylibA(void);
+extern int mylibD(void);
+int main(void)
 {
   return mylibA() + mylibD();
 }
diff --git a/Tests/PDBDirectoryAndName/mylibA.c b/Tests/PDBDirectoryAndName/mylibA.c
index 5bc279b..58ff123 100644
--- a/Tests/PDBDirectoryAndName/mylibA.c
+++ b/Tests/PDBDirectoryAndName/mylibA.c
@@ -1,4 +1,4 @@
-__declspec(dllexport) int mylibA()
+__declspec(dllexport) int mylibA(void)
 {
   return 1;
 }
diff --git a/Tests/PDBDirectoryAndName/mylibB.c b/Tests/PDBDirectoryAndName/mylibB.c
index 3a95845..8be2aa0 100644
--- a/Tests/PDBDirectoryAndName/mylibB.c
+++ b/Tests/PDBDirectoryAndName/mylibB.c
@@ -1,4 +1,4 @@
-int mylibB()
+int mylibB(void)
 {
   return -1;
 }
diff --git a/Tests/PDBDirectoryAndName/mylibC.c b/Tests/PDBDirectoryAndName/mylibC.c
index 8982849..64dde72 100644
--- a/Tests/PDBDirectoryAndName/mylibC.c
+++ b/Tests/PDBDirectoryAndName/mylibC.c
@@ -1,4 +1,4 @@
-__declspec(dllexport) int mylibC()
+__declspec(dllexport) int mylibC(void)
 {
   return 1;
 }
diff --git a/Tests/PDBDirectoryAndName/mylibD.c b/Tests/PDBDirectoryAndName/mylibD.c
index a53b7a2..c9c04d8 100644
--- a/Tests/PDBDirectoryAndName/mylibD.c
+++ b/Tests/PDBDirectoryAndName/mylibD.c
@@ -1,4 +1,4 @@
-int mylibD()
+int mylibD(void)
 {
   return -1;
 }
diff --git a/Tests/PerConfig/perconfig.c b/Tests/PerConfig/perconfig.c
index d942d45..5162ae0 100644
--- a/Tests/PerConfig/perconfig.c
+++ b/Tests/PerConfig/perconfig.c
@@ -1,6 +1,6 @@
 #include "pcShared.h"
 extern const char* pcStatic(void);
-int main()
+int main(void)
 {
   pcStatic();
   pcShared();
diff --git a/Tests/PolicyScope/main.c b/Tests/PolicyScope/main.c
index f8b643a..8488f4e 100644
--- a/Tests/PolicyScope/main.c
+++ b/Tests/PolicyScope/main.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/PrecompiledHeader/foo1.c b/Tests/PrecompiledHeader/foo1.c
index fef2586..e743f8b 100644
--- a/Tests/PrecompiledHeader/foo1.c
+++ b/Tests/PrecompiledHeader/foo1.c
@@ -2,7 +2,7 @@
 #  error "Precompiled header foo_precompiled.h has not been loaded."
 #endif
 
-int main()
+int main(void)
 {
   return foo();
 }
diff --git a/Tests/PrecompiledHeader/foo2.c b/Tests/PrecompiledHeader/foo2.c
index 3ed04ed..5211f3e 100644
--- a/Tests/PrecompiledHeader/foo2.c
+++ b/Tests/PrecompiledHeader/foo2.c
@@ -3,7 +3,7 @@
 #  error "Precompiled header foo_precompiled.h has not been loaded."
 #endif
 
-int foo()
+int foo(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/AppleTextStubs/foo.c b/Tests/RunCMake/AppleTextStubs/foo.c
index 7f39d71..f669327 100644
--- a/Tests/RunCMake/AppleTextStubs/foo.c
+++ b/Tests/RunCMake/AppleTextStubs/foo.c
@@ -1,5 +1,5 @@
 
-int foo()
+int foo(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/AppleTextStubs/main.c b/Tests/RunCMake/AppleTextStubs/main.c
index dc5ce3d..390f7d1 100644
--- a/Tests/RunCMake/AppleTextStubs/main.c
+++ b/Tests/RunCMake/AppleTextStubs/main.c
@@ -1,7 +1,7 @@
 
 extern int foo(void);
 
-int main()
+int main(void)
 {
   return foo();
 }
diff --git a/Tests/RunCMake/AutoExportDll/foo.c b/Tests/RunCMake/AutoExportDll/foo.c
index d13bc3e..b4f3ae0 100644
--- a/Tests/RunCMake/AutoExportDll/foo.c
+++ b/Tests/RunCMake/AutoExportDll/foo.c
@@ -4,12 +4,12 @@
 #  define WINAPI
 #endif
 
-int WINAPI foo()
+int WINAPI foo(void)
 {
   return 10;
 }
 
-int bar()
+int bar(void)
 {
   return 5;
 }
diff --git a/Tests/RunCMake/AutoExportDll/hello2.c b/Tests/RunCMake/AutoExportDll/hello2.c
index d4d6b72..66e7caf 100644
--- a/Tests/RunCMake/AutoExportDll/hello2.c
+++ b/Tests/RunCMake/AutoExportDll/hello2.c
@@ -2,7 +2,7 @@
 
 extern int own_auto_export_function(int i);
 
-void hello2()
+void hello2(void)
 {
   printf("hello exec:%i", own_auto_export_function(41));
 }
diff --git a/Tests/RunCMake/AutoExportDll/objlib.c b/Tests/RunCMake/AutoExportDll/objlib.c
index 54a9658..4e580c3 100644
--- a/Tests/RunCMake/AutoExportDll/objlib.c
+++ b/Tests/RunCMake/AutoExportDll/objlib.c
@@ -1,4 +1,4 @@
-int objlib()
+int objlib(void)
 {
   return 7;
 }
diff --git a/Tests/RunCMake/Autogen/RccExample.cmake b/Tests/RunCMake/Autogen/RccExample.cmake
index 4554eb0..ade0fef 100644
--- a/Tests/RunCMake/Autogen/RccExample.cmake
+++ b/Tests/RunCMake/Autogen/RccExample.cmake
@@ -9,3 +9,7 @@
                             Qt${with_qt_version}::Gui)
 
 set_target_properties(dummy PROPERTIES AUTORCC ON)
+
+if(DEFINED ZSTD_VALUE)
+  set(QT_FEATURE_zstd ${ZSTD_VALUE})
+endif()
diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
index 1a7d4f0..0036551 100644
--- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
@@ -128,65 +128,360 @@
       if(QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
         if (RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
           set(config_list Debug Release RelWithDebInfo)
+          set(use_better_graph_list ON OFF)
         else()
           set(config_list single-config)
+          set(use_better_graph_list OFF)
         endif()
-        foreach(config IN ITEMS ${config_list})
-          block()
-            if (config STREQUAL "single-config")
-              set(config_suffix "")
-            else()
-              set(config_suffix "_${config}")
-            endif()
-            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_suffix}-build)
-            run_cmake(QtAutoMocDeps)
-            set(RunCMake_TEST_NO_CLEAN 1)
-            # Build the project.
-            if (config STREQUAL "single-config")
-              set(config_param "")
-            else()
-              set(config_param "--config ${config}")
-            endif()
-            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
-            # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
-            # for app_with_qt target.
-            file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
 
-            # Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2.
-            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
-            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
-            unset(RunCMake_TEST_NOT_EXPECT_stdout)
-
-            macro(check_file_exists file)
-              if (EXISTS "${file}")
-                set(check_result "PASSED")
-                set(message_type "STATUS")
+        foreach(use_better_graph IN ITEMS ${use_better_graph_list})
+          foreach(config IN ITEMS ${config_list})
+            block()
+              if (config STREQUAL "single-config")
+                set(config_suffix "")
               else()
-                set(check_result "FAILED")
-                set(message_type "FATAL_ERROR")
+                set(config_path "_${config}")
+                if (use_better_graph)
+                  set(config_suffix "_${config}")
+                endif()
               endif()
 
-              message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}")
-            endmacro()
+              set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_path}-build)
+              run_cmake_with_options(QtAutoMocDeps ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=${use_better_graph})
+              set(RunCMake_TEST_NO_CLEAN 1)
+              # Build the project.
+              if (config STREQUAL "single-config")
+                set(config_param "")
+              else()
+                set(config_param "--config ${config}")
+              endif()
+              run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
+              # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
+              # for app_with_qt target.
+              file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
+              set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target app_with_qt|\
+Automatic MOC for target sub_exe_1|\
+Automatic MOC for target sub_exe_2")
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for 'app_with_qt', 'sub_exe_1' and 'sub_exe_2'")
+              # Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2.
+              run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
+              unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+              unset(RunCMake_TEST_NOT_EXPECT_stdout)
 
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps")
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps")
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps")
+              macro(check_file_exists file)
+                if (EXISTS "${file}")
+                  set(check_result "PASSED")
+                  set(message_type "STATUS")
+                else()
+                  set(check_result "FAILED")
+                  set(message_type "FATAL_ERROR")
+                endif()
 
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp")
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp")
-            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp")
+                message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}")
+              endmacro()
 
-            # Touch a header file to make sure an automoc dependency cycle is not introduced.
-            file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
-            set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle")
-            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
-            # Need to run a second time to hit the dependency cycle.
-            set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle")
-            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
-          endblock()
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps${config_suffix}")
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps${config_suffix}")
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps${config_suffix}")
+
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp${config_suffix}")
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp${config_suffix}")
+              check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp${config_suffix}")
+
+              # Touch a header file to make sure an automoc dependency cycle is not introduced.
+              file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle")
+              run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
+              # Need to run a second time to hit the dependency cycle.
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle")
+              run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
+            endblock()
+          endforeach()
         endforeach()
       endif()
     endblock()
   endif()
+
+  function(run_make_program dir)
+    execute_process(
+      COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+      WORKING_DIRECTORY "${dir}"
+      OUTPUT_VARIABLE make_program_stdout
+      ERROR_VARIABLE make_program_stderr
+      RESULT_VARIABLE make_program_result
+      )
+      if (NOT DEFINED RunMakeProgram_expected_result)
+        set(RunMakeProgram_expected_result 0)
+      endif()
+      if(NOT "${make_program_result}" MATCHES "${RunMakeProgram_expected_result}")
+        message(STATUS "
+============ beginning of ${RunCMake_MAKE_PROGRAM}'s stdout ============
+${make_program_stdout}
+=============== end of ${RunCMake_MAKE_PROGRAM}'s stdout ===============
+")
+        message(STATUS "
+============ beginning of ${RunCMake_MAKE_PROGRAM}'s stderr ============
+${make_program_stderr}
+=============== end of ${RunCMake_MAKE_PROGRAM}'s stderr ===============
+")
+        message(FATAL_ERROR
+                "top ${RunCMake_MAKE_PROGRAM} build failed exited with status ${make_program_result}")
+    endif()
+    set(make_program_stdout "${make_program_stdout}" PARENT_SCOPE)
+  endfunction(run_make_program)
+
+  function(count_substring STRING SUBSTRING COUNT_VAR)
+      string(LENGTH "${STRING}" STRING_LENGTH)
+      string(LENGTH "${SUBSTRING}" SUBSTRING_LENGTH)
+      if (SUBSTRING_LENGTH EQUAL 0)
+          message(FATAL_ERROR "SUBSTRING_LENGTH is 0")
+      endif()
+
+      if (STRING_LENGTH EQUAL 0)
+          message(FATAL_ERROR "STRING_LENGTH is 0")
+      endif()
+
+      if (STRING_LENGTH LESS SUBSTRING_LENGTH)
+          message(FATAL_ERROR "STRING_LENGTH is less than SUBSTRING_LENGTH")
+      endif()
+
+      set(COUNT 0)
+      string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
+      while(SUBSTRING_START GREATER_EQUAL 0)
+          math(EXPR COUNT "${COUNT} + 1")
+          math(EXPR SUBSTRING_START "${SUBSTRING_START} + ${SUBSTRING_LENGTH}")
+          string(SUBSTRING "${STRING}" ${SUBSTRING_START} -1 STRING)
+          string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
+      endwhile()
+
+      set(${COUNT_VAR} ${COUNT} PARENT_SCOPE)
+  endfunction()
+
+  function(expect_only_once make_program_stdout expected_output test_name)
+    count_substring("${make_program_stdout}" "${expected_output}" count)
+    if(NOT count EQUAL 1)
+      message(STATUS "${test_name}-expect_only_once - FAILED")
+      message(FATAL_ERROR "Expected to find ${expected_output} exactly once in ${make_program_stdout} but found ${count} occurrences of ${expected_output}")
+    else()
+      message(STATUS "${test_name}-expect_only_once - PASSED")
+    endif()
+  endfunction()
+
+  function(expect_n_times string_to_check expected_output expected_count test_name)
+    count_substring("${string_to_check}" "${expected_output}" count)
+    if(NOT count EQUAL ${expected_count})
+      message(STATUS "${test_name}-expect_${expected_count}_times - FAILED")
+      message(FATAL_ERROR "Expected to find ${expected_output} exactly ${expected_count} times in ${string_to_check} but found ${count} occurrences of ${expected_output}")
+    else()
+      message(STATUS "${test_name}-expect_${expected_count}_times - PASSED")
+    endif()
+  endfunction()
+
+  function(not_expect make_program_stdout unexpected_output test_name)
+    count_substring("${make_program_stdout}" "${unexpected_output}" count)
+    if(NOT count EQUAL 0)
+      message(STATUS "${test_name}-not_expect - FAILED")
+      message(FATAL_ERROR "Expected to find ${unexpected_output} exactly 0 times in ${make_program_stdout} but found ${count} occurrences of ${unexpected_output}")
+    else()
+      message(STATUS "${test_name}-not_expect - PASSED")
+    endif()
+  endfunction()
+
+  if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
+    foreach(exe IN ITEMS Moc Uic Rcc)
+      if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
+        block()
+          set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+          set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-multi-config-build)
+          run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
+          unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+          set(RunCMake_TEST_NO_CLEAN 1)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_running_exe_${config}")
+              run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
+            endblock()
+          endforeach()
+          set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do")
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_no_work_to_do")
+              run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
+            endblock()
+          endforeach()
+        endblock()
+        block()
+          set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
+          run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${config}.ninja)
+
+              set(expected_output "running_exe_${config}")
+              expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
+
+              foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
+                if(NOT sub_config STREQUAL config)
+                  set(unexpected_output "running_exe_${sub_config}")
+                  not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig-${config}-${unexpected_output}")
+                endif()
+              endforeach()
+
+              if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                set(expected_output "cmake_autogen")
+              else()
+                set(expected_output "cmake_autorcc")
+              endif()
+              expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
+            endblock()
+          endforeach()
+        endblock()
+        block()
+          foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+            foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                set(TEST_SUFFIX "-CrossConfig-${ninja_config}-${target_config}")
+                set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
+                set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
+                run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=${ninja_config} -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
+                unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+
+                run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja dummy:${target_config})
+
+                set(expected_output "running_exe_${ninja_config}")
+                expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
+
+                foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
+                  if(NOT sub_config STREQUAL ninja_config)
+                    set(unexpected_output "running_exe_${sub_config}")
+                    not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${unexpected_output}")
+                  endif()
+                endforeach()
+
+                if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                  set(expected_output "cmake_autogen")
+                else()
+                  set(expected_output "cmake_autorcc")
+                endif()
+                expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
+              endblock()
+            endforeach()
+          endforeach()
+        endblock()
+        block()
+          foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+            set(TEST_SUFFIX "-CrossConfig-${ninja_config}-all-all")
+            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
+            set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
+            run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=ON)
+            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+            run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja all:all)
+          endforeach()
+        endblock()
+      elseif (RunCMake_GENERATOR MATCHES "Ninja|Make")
+        block()
+          set(RunCMake_TEST_BINARY_DIR  ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}")
+              run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_BUILD_TYPE=${config} -DCMAKE_AUTOGEN_VERBOSE=ON)
+              unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+              set(RunCMake_TEST_NO_CLEAN 1)
+              set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
+              run_cmake_command(Auto${exe}ExecutableConfig-${config}-build ${CMAKE_COMMAND} --build .)
+            endblock()
+          endforeach()
+        endblock()
+      endif()
+    endforeach()
+  endif()
+
+  # Visual Studio specific dependency tests
+  if (RunCMake_GENERATOR MATCHES "Visual Studio")
+      foreach(exe IN ITEMS Moc Uic Rcc)
+          block()
+            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+            run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+            set(RunCMake_TEST_NO_CLEAN 1)
+            foreach(config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-first-build")
+                run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
+              endblock()
+            endforeach()
+            foreach(config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                  set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}")
+                  set(not_expect_descripton "Auto${exe}")
+                else ()
+                  set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}")
+                  set(not_expect_descripton "Auto${exe}")
+                endif()
+                set(RunCMake_TEST_VARIANT_DESCRIPTION "-second-build-${config}_expect_no_${not_expect_descripton}")
+                run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
+              endblock()
+            endforeach()
+          endblock()
+      endforeach()
+  endif()
+
+  if (RunCMake_GENERATOR MATCHES "Xcode")
+    foreach(exe IN ITEMS Moc Uic Rcc)
+      block()
+        set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
+        set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+        set(RunCMake_TEST_EXPECT_stderr ".*")
+        run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+        set(RunCMake_TEST_NO_CLEAN 1)
+        set(RunCMake_MAKE_PROGRAM ${CMAKE_COMMAND})
+        run_make_program(${RunCMake_TEST_BINARY_DIR}  --build . --config Debug)
+        if (exe STREQUAL "Moc")
+          set(expected_count 16)
+        elseif (exe STREQUAL "Uic")
+          set(expected_count 4)
+        else()
+          set(expected_count 12)
+        endif()
+        expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
+        expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
+
+        if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+          expect_n_times("${make_program_stdout}" "AutoGen:" 20 "${exe}Example-build-AutoGen:")
+        endif()
+
+        foreach(config IN ITEMS Debug Release RelWithDebInfo)
+          block()
+            run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config ${config})
+            not_expect("${make_program_stdout}" "Auto${exe}" "${exe}Example-${config}_Auto${exe}")
+            not_expect("${make_program_stdout}" "AutoGen:" "${exe}Example-${config}_AutoGen")
+          endblock()
+        endforeach()
+      endblock()
+    endforeach()
+  endif()
+
+  if (QtCore_VERSION VERSION_GREATER_EQUAL 6)
+    if (RunCMake_GENERATOR MATCHES "Make|Ninja")
+      foreach(value IN ITEMS ON OFF)
+        block()
+          set(RunCMake_TEST_BINARY_DIR
+            ${RunCMake_BINARY_DIR}/RccNoZTSD-${value}-build)
+          run_cmake_with_options(RccExample ${RunCMake_TEST_OPTIONS}
+            -DCMAKE_AUTOGEN_VERBOSE=ON -DZSTD_VALUE=${value})
+          if (value STREQUAL "OFF")
+            set(RunCMake_TEST_EXPECT_stdout "--no-zstd")
+          else()
+            set(RunCMake_TEST_NOT_EXPECT_stdout "--no-zstd")
+          endif()
+          set(RunCMake_TEST_NO_CLEAN 1)
+          run_cmake_command(RccNoZTSD-${value}-build ${CMAKE_COMMAND}
+            --build . --config Debug)
+        endblock()
+      endforeach()
+    endif()
+  endif()
 endif ()
diff --git a/Tests/RunCMake/Autogen/data.qrc b/Tests/RunCMake/Autogen/data.qrc
new file mode 100644
index 0000000..9bd068c
--- /dev/null
+++ b/Tests/RunCMake/Autogen/data.qrc
@@ -0,0 +1,4 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/res/affine">
+</qresource>
+</RCC>
diff --git a/Tests/RunCMake/Autogen/example.cpp b/Tests/RunCMake/Autogen/example.cpp
new file mode 100644
index 0000000..7f1a781
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example.cpp
@@ -0,0 +1,5 @@
+#include "example.h"
+
+Example::Example()
+{
+}
diff --git a/Tests/RunCMake/Autogen/example.h b/Tests/RunCMake/Autogen/example.h
new file mode 100644
index 0000000..e8bfa42
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example.h
@@ -0,0 +1,12 @@
+#ifndef EXAMPLE_H
+#define EXAMPLE_H
+
+#include <QObject>
+
+class Example : public QObject
+{
+  Q_OBJECT
+  Example();
+};
+
+#endif
diff --git a/Tests/RunCMake/Autogen/example_ui.cpp b/Tests/RunCMake/Autogen/example_ui.cpp
new file mode 100644
index 0000000..fb97c32
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example_ui.cpp
@@ -0,0 +1,5 @@
+#include "example_ui.h"
+
+Example::Example()
+{
+}
diff --git a/Tests/RunCMake/Autogen/example_ui.h b/Tests/RunCMake/Autogen/example_ui.h
new file mode 100644
index 0000000..d691133
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example_ui.h
@@ -0,0 +1,14 @@
+#ifndef EXAMPLE_UI_H
+#define EXAMPLE_UI_H
+
+#include <QObject>
+
+#include "ui_uiA.h"
+
+class Example : public QObject
+{
+  Q_OBJECT
+  Example();
+};
+
+#endif
diff --git a/Tests/RunCMake/Autogen/exe.cpp b/Tests/RunCMake/Autogen/exe.cpp
new file mode 100644
index 0000000..f8b643a
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/Autogen/exe_common.h b/Tests/RunCMake/Autogen/exe_common.h
new file mode 100644
index 0000000..15311c6
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_common.h
@@ -0,0 +1,48 @@
+#ifndef EXE_COMMON_H
+#define EXE_COMMON_H
+
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <vector>
+
+inline int runRealExe(const int argc, char** argv)
+{
+  std::vector<std::string> args;
+  std::string realMocPath;
+  std::string const pathArg = "EXE_PATH=";
+  std::string cmd;
+  if (argc > 1) {
+    for (int i = 1; i < argc; ++i) {
+      std::string const arg = argv[i];
+      if (arg.find(pathArg) != std::string::npos) {
+        realMocPath = arg.substr(pathArg.length());
+        // if EXE_PATH contains spaces, wrap it in quotes
+        if (realMocPath.find(" ") != std::string::npos) {
+          realMocPath = "\"" + realMocPath + "\"";
+        }
+      } else {
+        args.push_back(arg);
+      }
+    }
+  }
+#ifdef _WIN32
+  cmd += "cmd /C \"";
+#endif
+  cmd += realMocPath + " ";
+  for (auto arg : args) {
+    // if arg contains spaces, wrap it in quotes
+    if (arg.find(' ') != std::string::npos) {
+      cmd += " \"" + arg + "\"";
+    } else {
+      cmd += " " + arg;
+    }
+  }
+#ifdef _WIN32
+  cmd += "\"";
+#endif
+  std::cout << "Running real exe:" << cmd << std::endl;
+  return std::system(cmd.c_str());
+}
+
+#endif
diff --git a/Tests/RunCMake/Autogen/exe_debug.cpp b/Tests/RunCMake/Autogen/exe_debug.cpp
new file mode 100644
index 0000000..ae5185b
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_debug.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_Debug\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/exe_release.cpp b/Tests/RunCMake/Autogen/exe_release.cpp
new file mode 100644
index 0000000..384c992
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_release.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_Release\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp
new file mode 100644
index 0000000..aa6c558
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_RelWithDebInfo\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/uiA.ui b/Tests/RunCMake/Autogen/uiA.ui
new file mode 100644
index 0000000..4c5762e
--- /dev/null
+++ b/Tests/RunCMake/Autogen/uiA.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UiA</class>
+ <widget class="QWidget" name="UiA">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c b/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c
index 0795aaa..bd54d76 100644
--- a/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c
+++ b/Tests/RunCMake/BuildDepends/MakeInProjectOnly.c
@@ -1,5 +1,5 @@
 #include <MakeInProjectOnly.h>
-int main()
+int main(void)
 {
   return MakeInProjectOnly();
 }
diff --git a/Tests/RunCMake/BuiltinTargets/CMakeLists.txt b/Tests/RunCMake/BuiltinTargets/CMakeLists.txt
new file mode 100644
index 0000000..6a9ce76
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake b/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake
new file mode 100644
index 0000000..6a74f57
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/RunCMakeTest.cmake
@@ -0,0 +1,19 @@
+include(RunCMake)
+
+if(RunCMake_GENERATOR MATCHES "Make|Ninja")
+  set(test_target "test")
+else()
+  set(test_target "RUN_TESTS")
+endif()
+
+function(run_BuiltinTarget case target)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+  run_cmake(${case})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug --target ${${target}_target})
+endfunction()
+
+run_BuiltinTarget(TestDependsAll-Default test)
+run_BuiltinTarget(TestDependsAll-No test)
+run_BuiltinTarget(TestDependsAll-Yes test)
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake
new file mode 100644
index 0000000..5ef321a
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default-build-check.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/TestDependsAll-No-build-check.cmake)
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake
new file mode 100644
index 0000000..6abd56c
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Default.cmake
@@ -0,0 +1 @@
+include(TestDependsAll-common.cmake)
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake
new file mode 100644
index 0000000..f42244e
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No-build-check.cmake
@@ -0,0 +1,3 @@
+if(EXISTS ${RunCMake_TEST_BINARY_DIR}/custom-output.txt)
+  set(RunCMake_TEST_FAILED "Building 'test' target incorrectly built 'all' target.")
+endif()
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake
new file mode 100644
index 0000000..50ec3b9
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-No.cmake
@@ -0,0 +1,2 @@
+include(TestDependsAll-common.cmake)
+set(CMAKE_SKIP_TEST_ALL_DEPENDENCY ON)
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake
new file mode 100644
index 0000000..ed175d4
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes-build-check.cmake
@@ -0,0 +1,3 @@
+if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/custom-output.txt)
+  set(RunCMake_TEST_FAILED "Building 'test' target did not build 'all' target:\n ${RunCMake_TEST_BINARY_DIR}/custom-output.txt")
+endif()
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake
new file mode 100644
index 0000000..c35c98d
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-Yes.cmake
@@ -0,0 +1,2 @@
+include(TestDependsAll-common.cmake)
+set(CMAKE_SKIP_TEST_ALL_DEPENDENCY OFF)
diff --git a/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake b/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake
new file mode 100644
index 0000000..bbe7c75
--- /dev/null
+++ b/Tests/RunCMake/BuiltinTargets/TestDependsAll-common.cmake
@@ -0,0 +1,3 @@
+enable_testing()
+add_custom_target(custom ALL COMMAND ${CMAKE_COMMAND} -E touch custom-output.txt)
+add_test(NAME test COMMAND ${CMAKE_COMMAND} -E echo)
diff --git a/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt
new file mode 100644
index 0000000..7d2608b
--- /dev/null
+++ b/Tests/RunCMake/CMP0126/CMP0126-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0126 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt
new file mode 100644
index 0000000..1f2179c
--- /dev/null
+++ b/Tests/RunCMake/CMP0126/CMP0126-OLD_CL-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0126-OLD_CL\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0126 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0156/CMP0156-Common.cmake b/Tests/RunCMake/CMP0156/CMP0156-Common.cmake
new file mode 100644
index 0000000..a382c77
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-Common.cmake
@@ -0,0 +1,27 @@
+
+enable_language(C)
+
+# ensure link is successful in case of circular dependency
+add_library(lib1 STATIC lib1.c)
+add_library(lib2 STATIC lib2.c)
+
+target_link_libraries(lib1 PRIVATE lib2)
+target_link_libraries(lib2 PRIVATE lib1)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib1)
+if (APPLE_TEST)
+  target_link_options(main PRIVATE "LINKER:-fatal_warnings")
+else()
+  target_link_options(main PRIVATE "$<$<AND:$<NOT:$<TARGET_POLICY:CMP0156>>,$<C_COMPILER_ID:AppleClang>,$<VERSION_GREATER_EQUAL:$<C_COMPILER_VERSION>,15.0>>:LINKER:-no_warn_duplicate_libraries>")
+endif()
+
+
+add_library(lib3 SHARED lib3.c)
+add_library(lib4 STATIC lib4.c)
+target_link_libraries(lib4 PRIVATE lib3)
+
+# link specifying a shared library not directly used by the target
+# on Windows, with CMP0156=NEW, lib3 is specified before lib4 on link step
+add_executable(main2 main2.c)
+target_link_libraries(main2 PRIVATE lib3 lib4)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake
new file mode 100644
index 0000000..59f5ecd
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-NEW-AppleClang.cmake
@@ -0,0 +1,5 @@
+
+cmake_policy(SET CMP0156 NEW)
+set(APPLE_TEST TRUE)
+
+include (CMP0156-Common.cmake)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake b/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake
new file mode 100644
index 0000000..4387b37
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-NEW.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0156 NEW)
+
+include (CMP0156-Common.cmake)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt
new file mode 100644
index 0000000..b18168c
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-result.txt
@@ -0,0 +1 @@
+.+
diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt
new file mode 100644
index 0000000..b4afc27
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang-build-stdout.txt
@@ -0,0 +1,3 @@
+ld: warning: ignoring duplicate libraries: '.*liblib1.a', '.*liblib2.a'
+ld: fatal warning\(s\) induced error \(-fatal_warnings\)
+clang: error: linker command failed with exit code 1 \(use -v to see invocation\)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake
new file mode 100644
index 0000000..33e1287
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-OLD-AppleClang.cmake
@@ -0,0 +1,5 @@
+
+cmake_policy(SET CMP0156 OLD)
+set(APPLE_TEST TRUE)
+
+include (CMP0156-Common.cmake)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake b/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake
new file mode 100644
index 0000000..b012a1a
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-OLD.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0156 OLD)
+
+include (CMP0156-Common.cmake)
diff --git a/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt
new file mode 100644
index 0000000..f52d7d8
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-WARN-stderr.txt
@@ -0,0 +1,35 @@
+CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_library\):
+  Policy CMP0156 is not set: De-duplicate libraries on link lines based on
+  linker capabilities.  Run "cmake --help-policy CMP0156" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  Since the policy is not set, legacy libraries de-duplication strategy will
+  be applied.
+Call Stack \(most recent call first\):
+  CMP0156-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_library\):
+  Policy CMP0156 is not set: De-duplicate libraries on link lines based on
+  linker capabilities.  Run "cmake --help-policy CMP0156" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  Since the policy is not set, legacy libraries de-duplication strategy will
+  be applied.
+Call Stack \(most recent call first\):
+  CMP0156-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at CMP0156-Common.cmake:[0-9]+ \(add_executable\):
+  Policy CMP0156 is not set: De-duplicate libraries on link lines based on
+  linker capabilities.  Run "cmake --help-policy CMP0156" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  Since the policy is not set, legacy libraries de-duplication strategy will
+  be applied.
+Call Stack \(most recent call first\):
+  CMP0156-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake b/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake
new file mode 100644
index 0000000..5f469ef
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMP0156-WARN.cmake
@@ -0,0 +1,4 @@
+
+set(CMAKE_POLICY_WARNING_CMP0156 TRUE)
+
+include (CMP0156-Common.cmake)
diff --git a/Tests/RunCMake/CMP0156/CMakeLists.txt b/Tests/RunCMake/CMP0156/CMakeLists.txt
new file mode 100644
index 0000000..922aad6
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.27)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0156/RunCMakeTest.cmake b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake
new file mode 100644
index 0000000..bd51830
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/RunCMakeTest.cmake
@@ -0,0 +1,26 @@
+include(RunCMake)
+
+# CMP0156 control how libraries are specified for the link step
+# a sensible configuration is how circular dependency is handled
+
+macro(run_cmake_and_build test)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  run_cmake(${test})
+  set(RunCMake_TEST_NO_CLEAN TRUE)
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+endmacro()
+
+run_cmake(CMP0156-WARN)
+run_cmake_and_build(CMP0156-OLD)
+run_cmake_and_build(CMP0156-NEW)
+
+if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
+    AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "15.0")
+  # special case for Apple: with CMP0156=OLD, linker will warning on duplicate libraries
+  run_cmake_and_build(CMP0156-OLD-AppleClang)
+  run_cmake_and_build(CMP0156-NEW-AppleClang)
+endif()
diff --git a/Tests/RunCMake/CMP0156/lib1.c b/Tests/RunCMake/CMP0156/lib1.c
new file mode 100644
index 0000000..faad375
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/lib1.c
@@ -0,0 +1,7 @@
+
+extern void lib2(void);
+
+void lib1(void)
+{
+  lib2();
+}
diff --git a/Tests/RunCMake/CMP0156/lib2.c b/Tests/RunCMake/CMP0156/lib2.c
new file mode 100644
index 0000000..9db7914
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/lib2.c
@@ -0,0 +1,7 @@
+
+extern void lib1(void);
+
+void lib2(void)
+{
+  lib1();
+}
diff --git a/Tests/RunCMake/CMP0156/lib3.c b/Tests/RunCMake/CMP0156/lib3.c
new file mode 100644
index 0000000..e63e456
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/lib3.c
@@ -0,0 +1,7 @@
+
+#if defined(_WIN32)
+__declspec(dllexport)
+#endif
+  void lib3(void)
+{
+}
diff --git a/Tests/RunCMake/CMP0156/lib4.c b/Tests/RunCMake/CMP0156/lib4.c
new file mode 100644
index 0000000..a992168
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/lib4.c
@@ -0,0 +1,9 @@
+
+#if defined(_WIN32)
+__declspec(dllimport)
+#endif
+  void lib3(void);
+
+void lib4(void)
+{
+}
diff --git a/Tests/RunCMake/CMP0156/main.c b/Tests/RunCMake/CMP0156/main.c
new file mode 100644
index 0000000..06edfd5
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/main.c
@@ -0,0 +1,9 @@
+
+extern void lib1(void);
+
+int main(void)
+{
+  lib1();
+
+  return 0;
+}
diff --git a/Tests/RunCMake/CMP0156/main2.c b/Tests/RunCMake/CMP0156/main2.c
new file mode 100644
index 0000000..9fc2838
--- /dev/null
+++ b/Tests/RunCMake/CMP0156/main2.c
@@ -0,0 +1,9 @@
+
+extern void lib4(void);
+
+int main(void)
+{
+  lib4();
+
+  return 0;
+}
diff --git a/Tests/RunCMake/CMP0160/CMP0160-NEW-result.txt b/Tests/RunCMake/CMP0160/CMP0160-NEW-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0160/CMP0160-NEW-stderr.txt b/Tests/RunCMake/CMP0160/CMP0160-NEW-stderr.txt
new file mode 100644
index 0000000..b52b633
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-NEW-stderr.txt
@@ -0,0 +1,233 @@
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  ALIAS_GLOBAL property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  BINARY_DIR property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  CXX_MODULE_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  IMPORTED property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_CXX_MODULE_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  LOCATION property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  LOCATION_CONFIG property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  SOURCE_DIR property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  IMPORTED_GLOBAL property can't be set on non-imported
+  targets\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for
+  target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  ALIAS_GLOBAL property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  BINARY_DIR property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  CXX_MODULE_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  IMPORTED property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_CXX_MODULE_SETS property is read-only for
+  target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  LOCATION property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  LOCATION_CONFIG property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  SOURCE_DIR property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  EXPORT_NAME property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  SOURCES property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-NEW.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/CMP0160/CMP0160-NEW.cmake b/Tests/RunCMake/CMP0160/CMP0160-NEW.cmake
new file mode 100644
index 0000000..ed3a256
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-NEW.cmake
@@ -0,0 +1,4 @@
+
+project(ReadOnly LANGUAGES NONE)
+cmake_policy(SET CMP0160 NEW)
+include(${CMAKE_CURRENT_LIST_DIR}/READONLY_PROPERTIES.cmake)
diff --git a/Tests/RunCMake/CMP0160/CMP0160-OLD-result.txt b/Tests/RunCMake/CMP0160/CMP0160-OLD-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-OLD-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0160/CMP0160-OLD-stderr.txt b/Tests/RunCMake/CMP0160/CMP0160-OLD-stderr.txt
new file mode 100644
index 0000000..e57cd9d
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-OLD-stderr.txt
@@ -0,0 +1,104 @@
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  IMPORTED_GLOBAL property can't be set on non-imported
+  targets\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for
+  target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  EXPORT_NAME property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  SOURCES property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-OLD.cmake:4 \(include\)
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/CMP0160/CMP0160-OLD.cmake b/Tests/RunCMake/CMP0160/CMP0160-OLD.cmake
new file mode 100644
index 0000000..6c6f1f8
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-OLD.cmake
@@ -0,0 +1,4 @@
+
+project(ReadOnly LANGUAGES NONE)
+cmake_policy(SET CMP0160 OLD)
+include(${CMAKE_CURRENT_LIST_DIR}/READONLY_PROPERTIES.cmake)
diff --git a/Tests/RunCMake/CMP0160/CMP0160-WARN-result.txt b/Tests/RunCMake/CMP0160/CMP0160-WARN-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-WARN-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt b/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt
new file mode 100644
index 0000000..b15104e
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-WARN-stderr.txt
@@ -0,0 +1,297 @@
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  ALIAS_GLOBAL property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  BINARY_DIR property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  CXX_MODULE_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  IMPORTED property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  INTERFACE_CXX_MODULE_SETS property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  LOCATION property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  LOCATION_CONFIG property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  SOURCE_DIR property is read-only for target\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  IMPORTED_GLOBAL property can't be set on non-imported
+  targets\("ReadOnlyLib"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  INTERFACE_HEADER_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  MANUALLY_ADDED_DEPENDENCIES property is read-only for
+  target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  NAME property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  TYPE property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  ALIAS_GLOBAL property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  BINARY_DIR property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  CXX_MODULE_SETS property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  IMPORTED property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  INTERFACE_CXX_MODULE_SETS property is read-only for
+  target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  LOCATION property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  LOCATION_CONFIG property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  Policy CMP0160 is not set: More read-only target properties now error when
+  trying to set them.  Run "cmake --help-policy CMP0160" for policy details.
+  Use the cmake_policy command to set the policy and suppress this warning.
+
+  SOURCE_DIR property is read-only for target\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  EXPORT_NAME property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
+
+
+CMake Error at READONLY_PROPERTIES.cmake:44 \(set_target_properties\):
+  SOURCES property can't be set on imported targets\("ReadOnlyImport"\)
+
+Call Stack \(most recent call first\):
+  CMP0160-WARN.cmake:3 \(include\)
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/CMP0160/CMP0160-WARN.cmake b/Tests/RunCMake/CMP0160/CMP0160-WARN.cmake
new file mode 100644
index 0000000..e9e99bb
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMP0160-WARN.cmake
@@ -0,0 +1,3 @@
+
+project(ReadOnly LANGUAGES NONE)
+include(${CMAKE_CURRENT_LIST_DIR}/READONLY_PROPERTIES.cmake)
diff --git a/Tests/RunCMake/CMP0160/CMakeLists.txt b/Tests/RunCMake/CMP0160/CMakeLists.txt
new file mode 100644
index 0000000..6a9ce76
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0160/READONLY_PROPERTIES.cmake b/Tests/RunCMake/CMP0160/READONLY_PROPERTIES.cmake
new file mode 100644
index 0000000..07bbe78
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/READONLY_PROPERTIES.cmake
@@ -0,0 +1,52 @@
+
+set(read_only_properties
+  "HEADER_SETS"
+  "INTERFACE_HEADER_SETS"
+  "MANUALLY_ADDED_DEPENDENCIES"
+  "NAME"
+  "TYPE"
+  )
+set(read_only_properties_imported
+  "EXPORT_NAME"
+  "SOURCES"
+  )
+set(read_only_properties_nonimported
+  "IMPORTED_GLOBAL"
+  )
+set(read_only_properties_160
+    "ALIAS_GLOBAL"
+    "BINARY_DIR"
+    "CXX_MODULE_SETS"
+    "IMPORTED"
+    "INTERFACE_CXX_MODULE_SETS"
+    "LOCATION"
+    "LOCATION_CONFIG"
+    "SOURCE_DIR"
+    )
+
+cmake_policy(GET CMP0160 policy160)
+add_library(ReadOnlyLib )
+add_library(ReadOnlyImport IMPORTED UNKNOWN)
+
+foreach(target ReadOnlyLib ReadOnlyImport)
+  get_target_property(is_imported ${target} IMPORTED)
+  set(are_read_only ${read_only_properties})
+  if(NOT policy160 STREQUAL "OLD")
+    list(APPEND are_read_only ${read_only_properties_160})
+  endif()
+  if(is_imported)
+    list(APPEND are_read_only ${read_only_properties_imported})
+  else()
+    list(APPEND are_read_only ${read_only_properties_nonimported})
+  endif()
+
+  foreach(prop IN LISTS are_read_only)
+    set_target_properties(${target} PROPERTIES ${prop} "a_value")
+  endforeach()
+
+  if(policy160 STREQUAL "OLD")
+    foreach(prop IN LISTS read_only_properties_160)
+      set_target_properties(${target} PROPERTIES ${prop} "a_value")
+    endforeach()
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/CMP0160/RunCMakeTest.cmake b/Tests/RunCMake/CMP0160/RunCMakeTest.cmake
new file mode 100644
index 0000000..60380b5
--- /dev/null
+++ b/Tests/RunCMake/CMP0160/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0160-WARN)
+run_cmake(CMP0160-OLD)
+run_cmake(CMP0160-NEW)
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt
new file mode 100644
index 0000000..1953091
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stderr.txt
@@ -0,0 +1,9 @@
+^CMake Deprecation Warning at [^
+]*/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0127 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.$
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 57034e5..d9090d6 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -1,3 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
 # See adjacent README.rst for documentation of this test infrastructure.
 
 # Note that the _isMultiConfig variable is set in the parent directory's
@@ -87,10 +90,6 @@
 # Some tests use python for extra checks.
 find_package(Python QUIET)
 
-if(XCODE_VERSION AND "${XCODE_VERSION}" VERSION_LESS 6.1)
-  set(Swift_ARGS -DXCODE_BELOW_6_1=1)
-endif()
-
 # Test MSVC for older host CMake versions, and test
 # WIN32/CMAKE_C_COMPILER_ID to fix check on Intel for Windows.
 if(MSVC OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES "MSVC|Intel")
@@ -155,6 +154,7 @@
    "${CMAKE_CXX_COMPILER_ID}" STREQUAL "LCC" OR
    "${CMAKE_Fortran_COMPILER_ID}" STREQUAL "LCC")
   add_RunCMake_test("CMP0129")
+  set_property(TEST RunCMake.CMP0129 APPEND PROPERTY LABELS "Fortran")
 endif()
 
 add_RunCMake_test(CMP0132)
@@ -170,6 +170,9 @@
 endif()
 
 add_RunCMake_test(CMP0153)
+add_RunCMake_test(CMP0156 -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+                          -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
+add_RunCMake_test(CMP0160)
 
 # The test for Policy 65 requires the use of the
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
@@ -226,6 +229,7 @@
     endif()
   endif()
   add_RunCMake_test(Ninja)
+  set_property(TEST RunCMake.Ninja APPEND PROPERTY LABELS "Fortran")
   set(NinjaMultiConfig_ARGS
     -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}
     )
@@ -322,10 +326,19 @@
   -DCMAKE_C_LINK_DEPENDS_USE_COMPILER=${CMAKE_C_LINK_DEPENDS_USE_COMPILER}
   -DCMake_TEST_BuildDepends_GNU_AS=${CMake_TEST_BuildDepends_GNU_AS}
   )
+set_property(TEST RunCMake.BuildDepends APPEND PROPERTY LABELS "Fortran")
+add_RunCMake_test(BuiltinTargets)
 if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
   add_RunCMake_test(Byproducts)
 endif()
 add_RunCMake_test(CMakeDependentOption)
+if(APPLE # Remove these conditions when the test has non-Apple cases
+    AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+  add_RunCMake_test(CMakePackage
+    -DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION}
+    -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+    )
+endif()
 add_RunCMake_test(CMakeRoleGlobalProperty)
 add_RunCMake_test(CMakeRelease -DCMake_TEST_JQ=${CMake_TEST_JQ})
 if(CMAKE_GENERATOR MATCHES "Make|Ninja")
@@ -339,7 +352,17 @@
   list(APPEND CompilerTest_ARGS -DCMake_TEST_OBJC=${CMake_TEST_OBJC})
 endif()
 if(CMAKE_Fortran_COMPILER)
-  list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1)
+  # lfortran < 1.24 cannot handle long file names.  Fortran is not
+  # enabled here, so check the C compiler version instead.
+  if(CMAKE_C_COMPILER_ID STREQUAL "LCC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "1.24")
+    string(LENGTH "${CMAKE_CURRENT_BINARY_DIR}" _CCBD_LEN)
+    if(_CCBD_LEN LESS 35)
+      list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1)
+    endif()
+    unset(_CCBD_LEN)
+  else()
+    list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1)
+  endif()
 endif()
 foreach(lang IN ITEMS CUDA HIP ISPC)
   if(CMake_TEST_${lang})
@@ -347,7 +370,7 @@
   endif()
 endforeach()
 add_RunCMake_test(CompilerTest)
-set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC")
+set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran")
 add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
 add_RunCMake_test(DisallowedCommands)
 if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
@@ -408,7 +431,11 @@
 add_RunCMake_test(GeneratorToolset)
 add_RunCMake_test(GetPrerequisites -DSAMPLE_EXE=$<TARGET_FILE:exit_code>)
 add_RunCMake_test(GNUInstallDirs -DSYSTEM_NAME=${CMAKE_SYSTEM_NAME})
-add_RunCMake_test(GoogleTest) # Note: does not actually depend on Google Test
+add_RunCMake_test(GoogleTest # Note: does not actually depend on Google Test
+  -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+  -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
+  -DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}
+  )
 add_RunCMake_test(Graphviz)
 add_RunCMake_test(Languages)
 add_RunCMake_test(LinkItemValidation)
@@ -428,11 +455,16 @@
 add_RunCMake_test(ObjectLibrary)
 add_RunCMake_test(ParseImplicitIncludeInfo)
 add_RunCMake_test(ParseImplicitLinkInfo)
-if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  add_RunCMake_test(RuntimePath)
+if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG)
+  add_RunCMake_test(RuntimePath
+    -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+    -DCMAKE_EXECUTABLE_FORMAT=${CMAKE_EXECUTABLE_FORMAT}
+    )
 endif()
 add_RunCMake_test(ScriptMode)
-add_RunCMake_test(Swift -DCMAKE_Swift_COMPILER=${CMAKE_Swift_COMPILER} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
+add_RunCMake_test(Swift -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+                        -DCMake_TEST_Swift=${CMake_TEST_Swift}
+                        -DXCODE_VERSION=${XCODE_VERSION})
 add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(TargetObjects)
 add_RunCMake_test(TargetProperties)
@@ -468,7 +500,8 @@
 add_RunCMake_test(add_executable)
 add_RunCMake_test(add_library)
 add_RunCMake_test(add_subdirectory -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
-add_RunCMake_test(add_test)
+set_property(TEST RunCMake.add_subdirectory APPEND PROPERTY LABELS "Fortran")
+add_RunCMake_test(add_test -DPSEUDO_EMULATOR=$<TARGET_FILE:pseudo_emulator>)
 add_RunCMake_test(build_command)
 add_executable(exit_code exit_code.c)
 set(execute_process_ARGS
@@ -512,6 +545,7 @@
 add_RunCMake_test(ctest_update)
 add_RunCMake_test(ctest_upload)
 add_RunCMake_test(ctest_environment)
+add_RunCMake_test(ctest_empty_binary_directory)
 add_RunCMake_test(ctest_fixtures)
 add_RunCMake_test(define_property)
 add_RunCMake_test(file -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
@@ -526,8 +560,9 @@
 endforeach()
 add_RunCMake_test(file-DOWNLOAD)
 add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
+add_RunCMake_test(file-STRINGS)
 add_RunCMake_test(find_file -DMINGW=${MINGW})
-add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
+add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DMSVC=${MSVC})
 add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS})
 add_RunCMake_test(find_path -DMINGW=${MINGW})
 add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
@@ -536,7 +571,7 @@
 add_RunCMake_test(block)
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_property)
-add_RunCMake_test(if)
+add_RunCMake_test(if -DMSYS=${MSYS})
 add_RunCMake_test(include)
 add_RunCMake_test(include_directories)
 add_RunCMake_test(include_guard)
@@ -583,8 +618,12 @@
 endfunction()
 add_RunCMake_test_try_compile()
 
-add_RunCMake_test(try_run -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-                          -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
+if(CMAKE_Fortran_COMPILER)
+  list(APPEND try_run_ARGS -DCMake_TEST_Fortran=1)
+endif()
+add_RunCMake_test(try_run)
+set_property(TEST RunCMake.try_run APPEND PROPERTY LABELS "Fortran")
+
 add_RunCMake_test(set)
 add_RunCMake_test(variable_watch)
 add_RunCMake_test(while)
@@ -603,7 +642,9 @@
   add_RunCMake_test(CUDA_architectures)
   set_property(TEST RunCMake.CUDA_architectures APPEND PROPERTY LABELS "CUDA")
 endif()
+
 add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
+set_property(TEST RunCMake.DependencyGraph APPEND PROPERTY LABELS "Fortran")
 
 # Add C++ Module tests.
 add_RunCMake_test(CXXModules -DCMake_TEST_MODULE_COMPILATION=${CMake_TEST_MODULE_COMPILATION} -DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES})
@@ -738,6 +779,15 @@
   endif()
 endif()
 
+if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin|Windows)"
+    AND CMAKE_C_COMPILER_ID MATCHES "^(AppleClang|Clang|GNU|MSVC|NVIDIA)$"
+    AND NOT CMAKE_GENERATOR STREQUAL "Green Hills MULTI")
+  add_RunCMake_test(LinkerSelection -DCMake_TEST_CUDA=${CMake_TEST_CUDA}
+                                    -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
+                                    -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
+endif()
+
+add_RunCMake_test(LinkLibrariesProcessing)
 add_RunCMake_test(File_Archive)
 add_RunCMake_test(File_Configure)
 add_RunCMake_test(File_Generate)
@@ -804,14 +854,10 @@
 set_property(TEST RunCMake.CheckCompilerFlag
                   RunCMake.CheckSourceCompiles
                   RunCMake.CheckSourceRuns
-    APPEND PROPERTY LABELS "CUDA")
+    APPEND PROPERTY LABELS "CUDA" "HIP" "Fortran")
 set_property(TEST RunCMake.CheckSourceCompiles
                   RunCMake.CheckCompilerFlag
     APPEND PROPERTY LABELS "ISPC")
-set_property(TEST RunCMake.CheckCompilerFlag
-                  RunCMake.CheckSourceCompiles
-                  RunCMake.CheckSourceRuns
-    APPEND PROPERTY LABELS "HIP")
 add_RunCMake_test(CheckModules)
 add_RunCMake_test(CheckIPOSupported)
 if (CMAKE_SYSTEM_NAME MATCHES "(Linux|Darwin)"
@@ -820,8 +866,7 @@
                                     -DCMAKE_Fortran_COMPILER_ID=${CMAKE_Fortran_COMPILER_ID}
                                     -DCMake_TEST_CUDA=${CMake_TEST_CUDA}
                                     -DCMake_TEST_HIP=${CMake_TEST_HIP})
-  set_property(TEST RunCMake.CheckLinkerFlag APPEND PROPERTY LABELS "CUDA")
-  set_property(TEST RunCMake.CheckLinkerFlag APPEND PROPERTY LABELS "HIP")
+  set_property(TEST RunCMake.CheckLinkerFlag APPEND PROPERTY LABELS "CUDA" "HIP" "Fortran")
 endif()
 
 
@@ -868,6 +913,9 @@
   list(APPEND ExternalProject_ARGS -DDOWNLOAD_SERVER_TIMEOUT=${CMake_TEST_RunCMake_ExternalProject_DOWNLOAD_SERVER_TIMEOUT})
 endif()
 add_RunCMake_test(ExternalProject -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver>)
+if(CMake_TEST_RunCMake_ExternalProject_RUN_SERIAL)
+  set_property(TEST RunCMake.ExternalProject PROPERTY RUN_SERIAL TRUE)
+endif()
 add_RunCMake_test(FetchContent)
 add_RunCMake_test(FetchContent_find_package)
 set(CTestCommandLine_ARGS -DPython_EXECUTABLE=${Python_EXECUTABLE})
@@ -936,6 +984,7 @@
     -DPSEUDO_CPPLINT=$<TARGET_FILE:pseudo_cpplint>
     -DPSEUDO_CPPCHECK=$<TARGET_FILE:pseudo_cppcheck>
     )
+
   if(DEFINED CMake_TEST_CUDA)
     list(APPEND CompilerLauncher_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
   endif()
@@ -954,13 +1003,15 @@
   endif()
   add_RunCMake_test(CompilerLauncher)
   set_property(TEST RunCMake.CompilerLauncher APPEND
-    PROPERTY LABELS "CUDA;HIP;ISPC")
+    PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran")
+
   add_RunCMake_test(ctest_labels_for_subprojects)
   add_RunCMake_test(CompilerArgs)
   add_RunCMake_test(LinkerLauncher)
 endif()
 
 set(cpack_tests
+  DEB.AUTO_SUFFIXES
   DEB.CUSTOM_NAMES
   DEB.DEBUGINFO
   DEB.DEFAULT_PERMISSIONS
@@ -979,6 +1030,7 @@
   DEB.DEB_DESCRIPTION
   DEB.PROJECT_META
 
+  RPM.AUTO_SUFFIXES
   RPM.CUSTOM_BINARY_SPEC_FILE
   RPM.CUSTOM_NAMES
   RPM.DEBUGINFO
@@ -1012,6 +1064,35 @@
 if(APPLE)
   list(APPEND cpack_tests DragNDrop)
 endif()
+
+if(CMAKE_SYSTEM_PROCESSOR STREQUAL "e2k" AND NOT DEFINED CMake_TEST_E2K_BROKEN_LIBC)
+  # Exclude tests that fail due to a broken libc version on Elbrus.
+  find_program(DPKG_QUERY "dpkg-query" )
+  execute_process(COMMAND "${DPKG_QUERY}" "-f" "\${Version}" "-W" "glibc" OUTPUT_VARIABLE LIBC_VERSION)
+  if(LIBC_VERSION MATCHES "2.29-25.*")
+    list(REMOVE_ITEM cpack_tests
+      DEB.DEFAULT_PERMISSIONS
+      DEB.DEBUGINFO
+      DEB.MINIMAL
+      DEB.PER_COMPONENT_FIELDS
+      DEB.AUTO_SUFFIXES
+      DEB.CUSTOM_NAMES
+      DEB.DEB_PACKAGE_VERSION_BACK_COMPATIBILITY
+      DEB.PROJECT_META DEB.DEPENDENCIES
+      RPM.PARTIALLY_RELOCATABLE_WARNING
+      RPM.PER_COMPONENT_FIELDS
+      RPM.USER_FILELIST
+      RPM.DIST
+      RPM.AUTO_SUFFIXES
+      TGZ
+      ZIP
+      STGZ
+      External
+      )
+    set(CMake_TEST_E2K_BROKEN_LIBC 1)
+  endif()
+endif()
+
 add_RunCMake_test_group(CPack "${cpack_tests}")
 # add a test to make sure symbols are exported from a shared library
 # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used
@@ -1056,24 +1137,26 @@
   -DCMAKE_C_SIMULATE_ID=${CMAKE_C_SIMULATE_ID}
   -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
 
-add_RunCMake_test("UnityBuild")
+add_RunCMake_test(UnityBuild -DCMake_TEST_OBJC=${CMake_TEST_OBJC})
 add_RunCMake_test(CMakePresets
   -DPython_EXECUTABLE=${Python_EXECUTABLE}
   -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
   )
-add_RunCMake_test(CMakePresetsBuild
-  -DPython_EXECUTABLE=${Python_EXECUTABLE}
-  -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
-  -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-  )
-add_RunCMake_test(CMakePresetsTest
-  -DPython_EXECUTABLE=${Python_EXECUTABLE}
-  -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
-  )
-add_RunCMake_test(CMakePresetsPackage
-  -DPython_EXECUTABLE=${Python_EXECUTABLE}
-  -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
-  )
+if(NOT CMake_TEST_E2K_BROKEN_LIBC)
+  add_RunCMake_test(CMakePresetsBuild
+    -DPython_EXECUTABLE=${Python_EXECUTABLE}
+    -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
+    -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+    )
+  add_RunCMake_test(CMakePresetsTest
+    -DPython_EXECUTABLE=${Python_EXECUTABLE}
+    -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
+    )
+  add_RunCMake_test(CMakePresetsPackage
+    -DPython_EXECUTABLE=${Python_EXECUTABLE}
+    -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
+    )
+endif()
 add_RunCMake_test(CMakePresetsWorkflow
   -DPython_EXECUTABLE=${Python_EXECUTABLE}
   -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
@@ -1089,3 +1172,14 @@
 if(WIN32)
   add_RunCMake_test(Win32GenEx)
 endif()
+
+if(CMake_TEST_IAR_TOOLCHAINS)
+  add_RunCMake_test(IAR -DCMake_TEST_IAR_TOOLCHAINS=${CMake_TEST_IAR_TOOLCHAINS})
+  set_property(TEST RunCMake.IAR APPEND PROPERTY LABELS "IAR")
+endif()
+if(CMake_TEST_TICLANG_TOOLCHAINS)
+  # This is necessary to preserve the LIST variable contents given by user.
+  string(REPLACE ";" "$<SEMICOLON>" TOOLCHAINS "${CMake_TEST_TICLANG_TOOLCHAINS}")
+  add_RunCMake_test(TIClang "-DCMake_TEST_TICLANG_TOOLCHAINS=${TOOLCHAINS}")
+  set_property(TEST RunCMake.TIClang APPEND PROPERTY LABELS "TIClang")
+endif()
diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt
new file mode 100644
index 0000000..03be015
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/Modules/CMakePackageConfigHelpers\.cmake:[0-9]+ \(message\):
+  No INSTALL_DESTINATION given to generate_apple_platform_selection_file\(\)
+Call Stack \(most recent call first\):
+  ApplePlatformMissingDest\.cmake:[0-9]+ \(generate_apple_platform_selection_file\)
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake
new file mode 100644
index 0000000..e47b595
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/ApplePlatformMissingDest.cmake
@@ -0,0 +1,4 @@
+include(CMakePackageConfigHelpers)
+generate_apple_platform_selection_file(mylib-config-install.cmake
+  #missing: INSTALL_DESTINATION lib/cmake/mylib
+  )
diff --git a/Tests/RunCMake/CMakePackage/CMakeLists.txt b/Tests/RunCMake/CMakePackage/CMakeLists.txt
new file mode 100644
index 0000000..6a9ce76
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake
new file mode 100644
index 0000000..32fc7fb
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/RunCMakeTest.cmake
@@ -0,0 +1,109 @@
+include(RunCMake)
+
+if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Release)
+endif()
+
+function(apple_export platform system_name archs sysroot)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-export-${platform}-build)
+  string(REPLACE ";" "\\;" archs "${archs}")
+  if(select_archs)
+    string(REPLACE ";" "\\\\;" maybe_IOS_SIMULATOR_SELECT_ARCHS "-DIOS_SIMULATOR_SELECT_ARCHS=${select_archs}")
+  endif()
+  run_cmake_with_options(apple-export-${platform}
+    "-DCMAKE_SYSTEM_NAME=${system_name}"
+    "-DCMAKE_OSX_ARCHITECTURES=${archs}"
+    "-DCMAKE_OSX_SYSROOT=${sysroot}"
+    "-DCMAKE_INSTALL_PREFIX=${apple_install}"
+    ${maybe_CMAKE_BUILD_TYPE}
+    ${maybe_IOS_SIMULATOR_SELECT_ARCHS}
+    )
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(apple-export-${platform}-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(apple-export-${platform}-install ${CMAKE_COMMAND} --install . --config Release)
+  file(APPEND "${apple_install}/lib/${platform}/cmake/mylib/mylib-targets.cmake" "\n"
+    "message(STATUS \"loaded: '\${CMAKE_CURRENT_LIST_FILE}'\")\n"
+    )
+endfunction()
+
+function(apple_import platform system_name archs sysroot)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/apple-import-${platform}-build)
+  string(REPLACE ";" "\\;" archs "${archs}")
+  run_cmake_with_options(apple-import-${platform}
+    "-DCMAKE_SYSTEM_NAME=${system_name}"
+    "-DCMAKE_OSX_ARCHITECTURES=${archs}"
+    "-DCMAKE_OSX_SYSROOT=${sysroot}"
+    "-DCMAKE_PREFIX_PATH=${apple_install}"
+    ${maybe_CMAKE_BUILD_TYPE}
+    )
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(apple-import-${platform}-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+if(APPLE)
+  run_cmake(ApplePlatformMissingDest)
+endif()
+
+if(APPLE AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+  set(apple_install ${RunCMake_BINARY_DIR}/apple-install)
+  file(REMOVE_RECURSE "${apple_install}")
+
+  if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12)
+    set(macos_archs "x86_64;arm64")
+    set(watch_sim_archs "x86_64")
+    set(select_archs "arm64;x86_64")
+  else()
+    set(macos_archs "x86_64")
+    set(watch_sim_archs "i386")
+    set(select_archs "")
+  endif()
+
+  if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 9)
+    set(watch_archs "armv7k;arm64_32")
+  else()
+    set(watch_archs "armv7k")
+  endif()
+
+  if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15.2)
+    set(enable_visionos 1)
+  endif()
+
+  apple_export(macos Darwin "${macos_archs}" macosx)
+  apple_export(ios iOS "arm64" iphoneos)
+  apple_export(tvos tvOS "arm64" appletvos)
+  if(enable_visionos)
+    apple_export(visionos visionOS "arm64" xros)
+  endif()
+  apple_export(watchos watchOS "${watch_archs}" watchos)
+  apple_export(ios-simulator iOS "${macos_archs}" iphonesimulator)
+  if(select_archs)
+    foreach(arch IN LISTS macos_archs)
+      apple_export(ios-simulator-${arch} iOS "${arch}" iphonesimulator)
+    endforeach()
+  endif()
+
+  apple_export(tvos-simulator tvOS "${macos_archs}" appletvsimulator)
+  if(enable_visionos)
+    apple_export(visionos-simulator visionOS "${macos_archs}" xrsimulator)
+  endif()
+  apple_export(watchos-simulator watchOS "${watch_sim_archs}" watchsimulator)
+
+  apple_import(macos Darwin "${macos_archs}" macosx)
+  apple_import(ios iOS "arm64" iphoneos)
+  apple_import(tvos tvOS "arm64" appletvos)
+  if(enable_visionos)
+    apple_import(visionos visionOS "arm64" xros)
+  endif()
+  apple_import(watchos watchOS "${watch_archs}" watchos)
+  apple_import(ios-simulator iOS "${macos_archs}" iphonesimulator)
+  if(select_archs)
+    foreach(arch IN LISTS macos_archs)
+      apple_import(ios-simulator-${arch} iOS "${arch}" iphonesimulator)
+    endforeach()
+  endif()
+  apple_import(tvos-simulator tvOS "${macos_archs}" appletvsimulator)
+  if(enable_visionos)
+    apple_import(visionos-simulator visionOS "${macos_archs}" xrsimulator)
+  endif()
+  apple_import(watchos-simulator watchOS "${watch_sim_archs}" watchsimulator)
+endif()
diff --git a/Tests/RunCMake/CMakePackage/apple-common.cmake b/Tests/RunCMake/CMakePackage/apple-common.cmake
new file mode 100644
index 0000000..f854f34
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-common.cmake
@@ -0,0 +1,15 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME MATCHES "^(iOS)$")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "^(tvOS|watchOS|visionOS)$")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
diff --git a/Tests/RunCMake/CMakePackage/apple-export-common.cmake b/Tests/RunCMake/CMakePackage/apple-export-common.cmake
new file mode 100644
index 0000000..2c79b7b
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-common.cmake
@@ -0,0 +1,29 @@
+include(apple-common.cmake)
+
+add_library(mylib STATIC src/mylib.c)
+target_sources(mylib PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include FILES include/mylib.h)
+install(TARGETS mylib EXPORT mylib-targets FILE_SET HEADERS ARCHIVE DESTINATION lib/${platform_name})
+
+install(EXPORT mylib-targets DESTINATION lib/${platform_name}/cmake/mylib)
+
+if(IOS_SIMULATOR_SELECT_ARCHS)
+  set(IOS_SIMULATOR_INCLUDE_FILE lib/ios-simulator/cmake/mylib/mylib-select-arch.cmake)
+else()
+  set(IOS_SIMULATOR_INCLUDE_FILE lib/ios-simulator/cmake/mylib/mylib-targets.cmake)
+endif()
+
+include(CMakePackageConfigHelpers)
+generate_apple_platform_selection_file(mylib-config-install.cmake
+  INSTALL_DESTINATION lib/cmake/mylib
+  INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}
+  MACOS_INCLUDE_FILE lib/macos/cmake/mylib/mylib-targets.cmake
+  IOS_INCLUDE_FILE lib/ios/cmake/mylib/mylib-targets.cmake
+  IOS_SIMULATOR_INCLUDE_FILE ${IOS_SIMULATOR_INCLUDE_FILE}
+  TVOS_INCLUDE_FILE lib/tvos/cmake/mylib/mylib-targets.cmake
+  TVOS_SIMULATOR_INCLUDE_FILE lib/tvos-simulator/cmake/mylib/mylib-targets.cmake
+  VISIONOS_INCLUDE_FILE lib/visionos/cmake/mylib/mylib-targets.cmake
+  VISIONOS_SIMULATOR_INCLUDE_FILE lib/visionos-simulator/cmake/mylib/mylib-targets.cmake
+  WATCHOS_INCLUDE_FILE lib/watchos/cmake/mylib/mylib-targets.cmake
+  WATCHOS_SIMULATOR_INCLUDE_FILE lib/watchos-simulator/cmake/mylib/mylib-targets.cmake
+  )
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-install.cmake DESTINATION lib/cmake/mylib RENAME mylib-config.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake
new file mode 100644
index 0000000..6984df2
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-arm64.cmake
@@ -0,0 +1,2 @@
+set(platform_name ios-simulator-arm64)
+include(apple-export-ios-simulator-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake
new file mode 100644
index 0000000..9011180d
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-common.cmake
@@ -0,0 +1,13 @@
+include(apple-export-common.cmake)
+
+if(IOS_SIMULATOR_SELECT_ARCHS)
+  generate_apple_architecture_selection_file(mylib-select-arch-install.cmake
+    INSTALL_DESTINATION lib/ios-simulator/cmake/mylib
+    INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}
+    SINGLE_ARCHITECTURES "${IOS_SIMULATOR_SELECT_ARCHS}"
+    SINGLE_ARCHITECTURE_INCLUDE_FILES "lib/ios-simulator-arm64/cmake/mylib/mylib-targets.cmake;lib/ios-simulator-x86_64/cmake/mylib/mylib-targets.cmake"
+    UNIVERSAL_ARCHITECTURES "${IOS_SIMULATOR_SELECT_ARCHS}"
+    UNIVERSAL_INCLUDE_FILE "lib/ios-simulator/cmake/mylib/mylib-targets.cmake"
+    )
+  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-select-arch-install.cmake DESTINATION lib/ios-simulator/cmake/mylib RENAME mylib-select-arch.cmake)
+endif()
diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake
new file mode 100644
index 0000000..290cfa7
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator-x86_64.cmake
@@ -0,0 +1,2 @@
+set(platform_name ios-simulator-x86_64)
+include(apple-export-ios-simulator-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake
new file mode 100644
index 0000000..bc3deb3
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-ios-simulator.cmake
@@ -0,0 +1,2 @@
+set(platform_name ios-simulator)
+include(apple-export-ios-simulator-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-ios.cmake b/Tests/RunCMake/CMakePackage/apple-export-ios.cmake
new file mode 100644
index 0000000..33daa40
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-ios.cmake
@@ -0,0 +1,2 @@
+set(platform_name ios)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-macos.cmake b/Tests/RunCMake/CMakePackage/apple-export-macos.cmake
new file mode 100644
index 0000000..d845d5c
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-macos.cmake
@@ -0,0 +1,2 @@
+set(platform_name macos)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake
new file mode 100644
index 0000000..d44d663
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-tvos-simulator.cmake
@@ -0,0 +1,2 @@
+set(platform_name tvos-simulator)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake b/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake
new file mode 100644
index 0000000..c58144b
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-tvos.cmake
@@ -0,0 +1,2 @@
+set(platform_name tvos)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake
new file mode 100644
index 0000000..e783d80
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-visionos-simulator.cmake
@@ -0,0 +1,2 @@
+set(platform_name visionos-simulator)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake b/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake
new file mode 100644
index 0000000..73e1b2e
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-visionos.cmake
@@ -0,0 +1,2 @@
+set(platform_name visionos)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake
new file mode 100644
index 0000000..f4f95a6
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-watchos-simulator.cmake
@@ -0,0 +1,2 @@
+set(platform_name watchos-simulator)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake b/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake
new file mode 100644
index 0000000..59fc572
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-export-watchos.cmake
@@ -0,0 +1,2 @@
+set(platform_name watchos)
+include(apple-export-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-common.cmake b/Tests/RunCMake/CMakePackage/apple-import-common.cmake
new file mode 100644
index 0000000..ce79541
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-common.cmake
@@ -0,0 +1,7 @@
+include(apple-common.cmake)
+
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER)
+find_package(mylib CONFIG REQUIRED)
+
+add_executable(myexe src/myexe.c)
+target_link_libraries(myexe PRIVATE mylib)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt
new file mode 100644
index 0000000..7999474
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator-arm64/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-arm64.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt
new file mode 100644
index 0000000..4ca925d
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt
new file mode 100644
index 0000000..ec52735
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios-simulator-x86_64/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator-x86_64.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-simulator.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt
new file mode 100644
index 0000000..eabb96a
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/ios/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-ios.cmake b/Tests/RunCMake/CMakePackage/apple-import-ios.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-ios.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt
new file mode 100644
index 0000000..2c3d87d
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-macos-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/macos/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-macos.cmake b/Tests/RunCMake/CMakePackage/apple-import-macos.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-macos.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt
new file mode 100644
index 0000000..544f90f
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/tvos-simulator/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt
new file mode 100644
index 0000000..eccad6a
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-tvos-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/tvos/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake b/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-tvos.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt
new file mode 100644
index 0000000..c4cc068
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/visionos-simulator/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt
new file mode 100644
index 0000000..410eda2
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-visionos-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/visionos/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake b/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-visionos.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt
new file mode 100644
index 0000000..752e2be
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/watchos-simulator/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt b/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt
new file mode 100644
index 0000000..5654fe8
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-watchos-stdout.txt
@@ -0,0 +1 @@
+loaded: '[^']*/Tests/RunCMake/CMakePackage/apple-install/lib/watchos/cmake/mylib/mylib-targets.cmake'
diff --git a/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake b/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake
new file mode 100644
index 0000000..fa7ba53
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/apple-import-watchos.cmake
@@ -0,0 +1 @@
+include(apple-import-common.cmake)
diff --git a/Tests/RunCMake/CMakePackage/include/mylib.h b/Tests/RunCMake/CMakePackage/include/mylib.h
new file mode 100644
index 0000000..4955e74
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/include/mylib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+extern int mylib(void);
diff --git a/Tests/RunCMake/CMakePackage/src/myexe.c b/Tests/RunCMake/CMakePackage/src/myexe.c
new file mode 100644
index 0000000..c1182a2
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/src/myexe.c
@@ -0,0 +1,6 @@
+#include <mylib.h> /* include by angle-bracket to find installed copy */
+
+int main(void)
+{
+  return mylib();
+}
diff --git a/Tests/RunCMake/CMakePackage/src/mylib.c b/Tests/RunCMake/CMakePackage/src/mylib.c
new file mode 100644
index 0000000..f4c047e
--- /dev/null
+++ b/Tests/RunCMake/CMakePackage/src/mylib.c
@@ -0,0 +1,4 @@
+int mylib(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake
index ca02b76..258f6a6 100644
--- a/Tests/RunCMake/CPack/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake
@@ -9,6 +9,7 @@
 # run_cpack_test args: TEST_NAME "GENERATORS" RUN_CMAKE_BUILD_STEP "PACKAGING_TYPES"
 run_cpack_test(CUSTOM_BINARY_SPEC_FILE "RPM.CUSTOM_BINARY_SPEC_FILE" false "MONOLITHIC;COMPONENT")
 run_cpack_test(CUSTOM_NAMES "RPM.CUSTOM_NAMES;DEB.CUSTOM_NAMES;TGZ;DragNDrop" true "COMPONENT")
+run_cpack_test(AUTO_SUFFIXES "RPM.AUTO_SUFFIXES;DEB.AUTO_SUFFIXES" false "MONOLITHIC")
 run_cpack_test(DEBUGINFO "DEB.DEBUGINFO" true "COMPONENT")
 if(NOT "${DEBUGEDIT}" STREQUAL "DEBUGEDIT-NOTFOUND")
   run_cpack_test(DEBUGINFO "RPM.DEBUGINFO" true "COMPONENT")
@@ -69,3 +70,4 @@
 )
 run_cpack_test(PROJECT_META "RPM.PROJECT_META;DEB.PROJECT_META" false "MONOLITHIC;COMPONENT")
 run_cpack_test_package_target(PRE_POST_SCRIPTS "ZIP" false "MONOLITHIC;COMPONENT")
+run_cpack_test_subtests(DUPLICATE_FILE "success;conflict_file;conflict_symlink" "TGZ" false "COMPONENT;GROUP")
diff --git a/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake
new file mode 100644
index 0000000..1ca48d8
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/ExpectedFiles.cmake
@@ -0,0 +1,8 @@
+set(EXPECTED_FILES_COUNT "1")
+set(EXPECTED_FILES_NAME_GENERATOR_SPECIFIC_FORMAT TRUE)
+set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt")
+
+if(GENERATOR_TYPE STREQUAL "DEB" OR GENERATOR_TYPE STREQUAL "RPM")
+  string(TOLOWER "${GENERATOR_TYPE}" file_extension_)
+  set(EXPECTED_FILE_1 "autosuffixpackage.${file_extension_}")
+endif()
diff --git a/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake
new file mode 100644
index 0000000..84c9bec
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/AUTO_SUFFIXES/test.cmake
@@ -0,0 +1,5 @@
+install(FILES CMakeLists.txt DESTINATION foo COMPONENT test)
+
+# if the filename doesn't have the expected deb/rpm suffix, test that it is appended automatically
+set(CPACK_DEBIAN_FILE_NAME "autosuffixpackage")
+set(CPACK_RPM_FILE_NAME "autosuffixpackage")
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake
new file mode 100644
index 0000000..5341ecd
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/ExpectedFiles.cmake
@@ -0,0 +1,16 @@
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "success")
+    if(PACKAGING_TYPE STREQUAL "COMPONENT")
+        set(EXPECTED_FILES_COUNT "1")
+        set(EXPECTED_FILE_CONTENT_1_LIST "/files;/files/1.txt;/files/2.txt;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt;/files/symlink2")
+    elseif(PACKAGING_TYPE STREQUAL "GROUP")
+        set(EXPECTED_FILES_COUNT "3")
+        set(EXPECTED_FILE_1 "duplicate_file-0.1.1-*-g1.${cpack_archive_extension_}")
+        set(EXPECTED_FILE_CONTENT_1_LIST "/files;/files/1.txt;/files/2.txt;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/symlink2")
+        set(EXPECTED_FILE_2 "duplicate_file-0.1.1-*-g2.${cpack_archive_extension_}")
+        set(EXPECTED_FILE_CONTENT_2_LIST "/files;/files/3.txt;/files/4.txt;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt")
+        set(EXPECTED_FILE_3 "duplicate_file-0.1.1-*-c5.${cpack_archive_extension_}")
+        set(EXPECTED_FILE_CONTENT_3_LIST "/files;/files/5.txt;/files/6.txt;/files/7.txt;/files/8.txt;/files/9.txt")
+    endif ()
+else()
+    set(EXPECTED_FILES_COUNT "0")
+endif ()
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt
new file mode 100644
index 0000000..5544885
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_file-stderr.txt
@@ -0,0 +1 @@
+CPack Error: ERROR The data in files with the same filename is different.*
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt
new file mode 100644
index 0000000..5544885
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/conflict_symlink-stderr.txt
@@ -0,0 +1 @@
+CPack Error: ERROR The data in files with the same filename is different.*
diff --git a/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake
new file mode 100644
index 0000000..89d6784
--- /dev/null
+++ b/Tests/RunCMake/CPack/tests/DUPLICATE_FILE/test.cmake
@@ -0,0 +1,74 @@
+# Create files named 1 to 9
+foreach(i RANGE 1 9)
+    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${i}.txt" "This is file ${i}")
+endforeach()
+
+set(COMPONENT_NAMES c1 c2 c3 c4 c5)
+foreach(j RANGE 1 5)
+    # Select 4 file and install to the component
+    math(EXPR COMPONENT_IDX "${j} - 1")
+    list(GET COMPONENT_NAMES "${COMPONENT_IDX}" SELECTED_COMPONENT)
+    math(EXPR END_FILE "${j} + 4")
+    foreach(k RANGE ${j} ${END_FILE})
+        install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${k}.txt" DESTINATION "files" COMPONENT ${SELECTED_COMPONENT})
+    endforeach()
+endforeach()
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_file")
+    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/conflict/1.txt" "This should create a conflict.")
+    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/1.txt" DESTINATION "files" COMPONENT c2)
+endif ()
+
+# You cannot create symlink in Windows test environment. Instead mock the symlink.
+if(NOT CMAKE_HOST_WIN32)
+    file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/2.txt" "${CMAKE_CURRENT_BINARY_DIR}/symlink2" SYMBOLIC)
+else()
+    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/symlink2" "This is file 2")
+endif()
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/symlink2" DESTINATION "files" COMPONENT c1)
+
+if(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_symlink" AND NOT CMAKE_HOST_WIN32)
+    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/conflict)
+    file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/1.txt" "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" SYMBOLIC)
+    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" DESTINATION "files" COMPONENT c2)
+elseif(RunCMake_SUBTEST_SUFFIX STREQUAL "conflict_symlink" AND CMAKE_HOST_WIN32)
+    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" "This should create a conflict.")
+    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/conflict/symlink2" DESTINATION "files" COMPONENT c2)
+else()
+    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/symlink2" DESTINATION "files" COMPONENT c2)
+endif ()
+
+
+if(PACKAGING_TYPE STREQUAL "COMPONENT")
+    set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE ON)
+    set(CPACK_COMPONENTS_ALL "c1;c2;c3;c4")
+elseif(PACKAGING_TYPE STREQUAL "GROUP")
+    set(CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP ON)
+    set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
+    include(CPackComponent)
+
+    cpack_add_component_group(g1 DISPLAY_NAME "Group 1")
+    cpack_add_component_group(g2 DISPLAY_NAME "Group 2")
+    cpack_add_component(c1
+            DISPLAY_NAME "Group 1"
+            DESCRIPTION "Component for Group 1"
+            GROUP g1
+    )
+    cpack_add_component(c2
+            DISPLAY_NAME "Group 1"
+            DESCRIPTION "Component for Group 1"
+            GROUP g1
+    )
+    cpack_add_component(c3
+            DISPLAY_NAME "Group 2"
+            DESCRIPTION "Component for Group 2"
+            GROUP g2
+    )
+    cpack_add_component(c4
+            DISPLAY_NAME "Group 2"
+            DESCRIPTION "Component for Group 2"
+            GROUP g2
+    )
+
+    set(CPACK_${GENERATOR_TYPE}_PACKAGE_GROUP g1 g2)
+endif ()
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-NEW-check.cmake b/Tests/RunCMake/CPackConfig/CMP0161-NEW-check.cmake
new file mode 100644
index 0000000..586fcf9
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-NEW-check.cmake
@@ -0,0 +1,3 @@
+include(${RunCMake_SOURCE_DIR}/check.cmake)
+
+test_variable(CPACK_PRODUCTBUILD_DOMAINS "ON")
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-NEW.cmake b/Tests/RunCMake/CPackConfig/CMP0161-NEW.cmake
new file mode 100644
index 0000000..cc4d34b
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0161 NEW)
+set(CPACK_BINARY_PRODUCTBUILD ON CACHE BOOL "" FORCE)
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-OLD-check.cmake b/Tests/RunCMake/CPackConfig/CMP0161-OLD-check.cmake
new file mode 100644
index 0000000..e3cb854
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-OLD-check.cmake
@@ -0,0 +1,5 @@
+include(${RunCMake_SOURCE_DIR}/check.cmake)
+
+if(DEFINED CPACK_PRODUCTBUILD_DOMANS)
+  message(FATAL_ERROR "CPACK_PRODUCTBUILD_DOMANS was defined, but it should not have been")
+endif()
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-OLD.cmake b/Tests/RunCMake/CPackConfig/CMP0161-OLD.cmake
new file mode 100644
index 0000000..4d47576
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0161 OLD)
+set(CPACK_BINARY_PRODUCTBUILD ON CACHE BOOL "" FORCE)
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-WARN-check.cmake b/Tests/RunCMake/CPackConfig/CMP0161-WARN-check.cmake
new file mode 100644
index 0000000..e3cb854
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-WARN-check.cmake
@@ -0,0 +1,5 @@
+include(${RunCMake_SOURCE_DIR}/check.cmake)
+
+if(DEFINED CPACK_PRODUCTBUILD_DOMANS)
+  message(FATAL_ERROR "CPACK_PRODUCTBUILD_DOMANS was defined, but it should not have been")
+endif()
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt b/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt
new file mode 100644
index 0000000..5841ec7
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-WARN-stderr-darwin.txt
@@ -0,0 +1,12 @@
+^CMake Warning \(dev\) at [^
+]*/Modules/CPack\.cmake:[0-9]+ \(message\):
+  Policy CMP0161 is not set: CPACK_PRODUCTBUILD_DOMAINS defaults to true\.
+  Run "cmake --help-policy CMP0161" for policy details\.  Use the cmake_policy
+  command to set the policy and suppress this warning\.
+
+  For compatibility, CPACK_PRODUCTBUILD_DOMAINS will remain unset\.
+  Explicitly setting CPACK_PRODUCTBUILD_DOMAINS or setting policy CMP0161 to
+  NEW will prevent this warning\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CPackConfig/CMP0161-WARN.cmake b/Tests/RunCMake/CPackConfig/CMP0161-WARN.cmake
new file mode 100644
index 0000000..f387c69
--- /dev/null
+++ b/Tests/RunCMake/CPackConfig/CMP0161-WARN.cmake
@@ -0,0 +1 @@
+set(CPACK_BINARY_PRODUCTBUILD ON CACHE BOOL "" FORCE)
diff --git a/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake b/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake
index 32c7296..1de640b 100644
--- a/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CPackConfig/RunCMakeTest.cmake
@@ -2,6 +2,9 @@
 
 run_cmake(CMP0133-NEW)
 run_cmake(CMP0133-WARN)
+run_cmake(CMP0161-NEW)
+run_cmake(CMP0161-OLD)
+run_cmake(CMP0161-WARN)
 run_cmake(Simple)
 run_cmake(Default)
 run_cmake(Special)
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
index 223a61c..c09fff8 100644
--- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -207,6 +207,24 @@
 endfunction()
 run_SkipRegexFoundTest()
 
+
+function(run_TestsFromFileTest arg)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestsFromFile)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
+add_test(Test1 \"${CMAKE_COMMAND}\" -E echo \"test1\")
+add_test(Test2 \"${CMAKE_COMMAND}\" -E echo \"test2\")
+add_test(Test11 \"${CMAKE_COMMAND}\" -E echo \"test11\")
+")
+  run_cmake_command(TestsFromFile-${arg} ${CMAKE_CTEST_COMMAND} --${arg} ${RunCMake_SOURCE_DIR}/TestsFromFile-TestList.txt )
+endfunction()
+run_TestsFromFileTest(tests-from-file)
+run_TestsFromFileTest(exclude-from-file)
+
+
 function(run_SerialFailed)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SerialFailed)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -229,19 +247,22 @@
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
   file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "
   add_test(TestLoad1 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\")
+  set_tests_properties(TestLoad1 PROPERTIES PROCESSORS 2)
   add_test(TestLoad2 \"${CMAKE_COMMAND}\" -E echo \"test of --test-load\")
+  set_tests_properties(TestLoad2 PROPERTIES PROCESSORS 2)
 ")
-  run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j2 --test-load ${load})
+  run_cmake_command(${name} ${CMAKE_CTEST_COMMAND} -VV -j8 --test-load ${load})
 endfunction()
 
 # Tests for the --test-load feature of ctest
 #
 # Spoof a load average value to make these tests more reliable.
-set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5)
+set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7)
 
 # Verify that new tests are not started when the load average exceeds
 # our threshold and that they then run once the load average drops.
-run_TestLoad(test-load-wait 3)
+run_TestLoad(test-load-wait0 5)
+run_TestLoad(test-load-wait1 8)
 
 # Verify that warning message is displayed but tests still start when
 # an invalid argument is given.
@@ -249,7 +270,7 @@
 
 # Verify that new tests are started when the load average falls below
 # our threshold.
-run_TestLoad(test-load-pass 10)
+run_TestLoad(test-load-pass 12)
 
 unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING})
 
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-TestList.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-TestList.txt
new file mode 100644
index 0000000..91f6504
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-TestList.txt
@@ -0,0 +1,2 @@
+Test1
+# Test11
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-from-file-stdout.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-from-file-stdout.txt
new file mode 100644
index 0000000..96d8c3b
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-exclude-from-file-stdout.txt
@@ -0,0 +1,7 @@
+ +Start 2: Test2
+1/2 Test #2: Test2 ............................   Passed +[0-9.]+ sec
+ +Start 3: Test11
+2/2 Test #3: Test11 ...........................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 2
++
diff --git a/Tests/RunCMake/CTestCommandLine/TestsFromFile-tests-from-file-stdout.txt b/Tests/RunCMake/CTestCommandLine/TestsFromFile-tests-from-file-stdout.txt
new file mode 100644
index 0000000..653d56c
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/TestsFromFile-tests-from-file-stdout.txt
@@ -0,0 +1,5 @@
+ +Start 1: Test1
+1/1 Test #1: Test1 ............................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait-stdout.txt
deleted file mode 100644
index db7d7f3..0000000
--- a/Tests/RunCMake/CTestCommandLine/test-load-wait-stdout.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Test project [^
-]*/Tests/RunCMake/CTestCommandLine/TestLoad(
-[^*][^
-]*)*
-\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 3, Smallest test TestLoad[1-2] requires 1\*\*\*\*\*
-test 1
-    Start 1: TestLoad1
-+(
-[^*][^
-]*)*
-test 2
-    Start 2: TestLoad2
-+(
-[^*][^
-]*)*
-1/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec(
-[^*][^
-]*)*
-2/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec
-+
-100% tests passed, 0 tests failed out of 2
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt
new file mode 100644
index 0000000..d112fde
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/test-load-wait0-stdout.txt
@@ -0,0 +1,21 @@
+Test project [^
+]*/Tests/RunCMake/CTestCommandLine/TestLoad(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 5\*\*\*\*\*
+test 1
+    Start 1: TestLoad1
++(
+[^*][^
+]*)*
+test 2
+    Start 2: TestLoad2
++(
+[^*][^
+]*)*
+1/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec(
+[^*][^
+]*)*
+2/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 2
diff --git a/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt
new file mode 100644
index 0000000..aa91950
--- /dev/null
+++ b/Tests/RunCMake/CTestCommandLine/test-load-wait1-stdout.txt
@@ -0,0 +1,21 @@
+Test project [^
+]*/Tests/RunCMake/CTestCommandLine/TestLoad(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test TestLoad[1-2] requires 2\*\*\*\*\*
+test 1
+    Start 1: TestLoad1
++(
+[^*][^
+]*)*
+test 2
+    Start 2: TestLoad2
++(
+[^*][^
+]*)*
+1/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec(
+[^*][^
+]*)*
+2/2 Test #[1-2]: TestLoad[1-2] ........................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 2
diff --git a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
index 42e13fc..3c3e88f 100644
--- a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
@@ -167,6 +167,16 @@
   endif()
 endfunction()
 
+set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/invalid-build")
+set(RunCMake_TEST_NO_CLEAN 1)
+file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+file(WRITE "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake" "add_test(true \"${CMAKE_COMMAND}\" -E true)\n")
+run_cmake_command(invalid-nofile-ctest ${CMAKE_CTEST_COMMAND} --resource-spec-file "${RunCMake_BINARY_DIR}/noexist.json")
+run_cmake_command(invalid-not-json-ctest ${CMAKE_CTEST_COMMAND} --resource-spec-file "${RunCMake_SOURCE_DIR}/invalid.json")
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
 run_ctest_resource(lotsoftests 10 1 0)
 run_ctest_resource(checkfree1 2 0 1)
 run_ctest_resource(checkfree2 1 0 0)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
index 397ca38..0a927e7 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
@@ -1,3 +1,5 @@
 ^Error: a cycle exists in the test dependency graph for the test "GenerateSpecFile"\.
-Please fix the cycle and run ctest again.
-No tests were found!!!$
+Please fix the cycle and run ctest again\.
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
index 06ea90f..533b07c 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
index 4e4c01c..d91d6dc 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^GENERATED_RESOURCE_SPEC_FILE test property cannot be used in conjunction with ResourceSpecFile option
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
index 273cb80..def7a8b 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^Only one test may define the GENERATED_RESOURCE_SPEC_FILE property
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
index 39ee275..3301318 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
index 06ea90f..a3c4d20 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
index 39ee275..92a93bb 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
index 2c4dff8..6829ffe 100644
--- a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
@@ -1,2 +1,4 @@
 ^GENERATED_RESOURCE_SPEC_FILE must be an absolute path
-No tests were found!!!$
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-result.txt
@@ -0,0 +1 @@
+8
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt
new file mode 100644
index 0000000..df1135e
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/invalid-nofile-ctest-stderr.txt
@@ -0,0 +1,8 @@
+^Could not read/parse resource spec file [^
+]*/Tests/RunCMake/CTestResourceAllocation/noexist\.json:[ ]
+File not found: [^
+]*/Tests/RunCMake/CTestResourceAllocation/noexist.json
+Errors while running CTest
+Output from these tests are in: [^
+]*/Tests/RunCMake/CTestResourceAllocation/invalid-build/Testing/Temporary/LastTest\.log
+Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-result.txt
@@ -0,0 +1 @@
+8
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt
new file mode 100644
index 0000000..5bd9d97
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/invalid-not-json-ctest-stderr.txt
@@ -0,0 +1,15 @@
+^Could not read/parse resource spec file [^
+]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:[ ]
+JSON Parse Error: [^
+]*/Tests/RunCMake/CTestResourceAllocation/invalid\.json:
+\* Line 1, Column 1
+  Syntax error: value, object or array expected\.
+(\* Line 1, Column 2
+  Extra non-whitespace after JSON value\.
+|\* Line 1, Column 1
+  A valid JSON document must be either an array or an object value\.
+)
+Errors while running CTest
+Output from these tests are in: [^
+]*/Tests/RunCMake/CTestResourceAllocation/invalid-build/Testing/Temporary/LastTest\.log
+Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CTestResourceAllocation/invalid.json b/Tests/RunCMake/CTestResourceAllocation/invalid.json
new file mode 100644
index 0000000..4c861f8
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/invalid.json
@@ -0,0 +1 @@
+This is not a valid JSON file!
diff --git a/Tests/RunCMake/CXXModules/sources/c-anchor.c b/Tests/RunCMake/CXXModules/sources/c-anchor.c
index c782188..8adfcbb 100644
--- a/Tests/RunCMake/CXXModules/sources/c-anchor.c
+++ b/Tests/RunCMake/CXXModules/sources/c-anchor.c
@@ -1,4 +1,4 @@
-int c_anchor()
+int c_anchor(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake
index 23b3006..c85783d 100644
--- a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake
+++ b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagSwift.cmake
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0157 NEW)
 enable_language (Swift)
 include(CheckCompilerFlag)
 
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesFortran.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesFortran.cmake
index 48dc525..68c5735 100644
--- a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesFortran.cmake
+++ b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesFortran.cmake
@@ -5,6 +5,14 @@
 
 set(Fortran 1) # test that this is tolerated
 
+# lfortran < 1.24 cannot handle long file names.
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "LCC" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "1.24")
+  string(LENGTH "${CMAKE_CURRENT_BINARY_DIR}" _CCBD_LEN)
+  if(_CCBD_LEN GREATER_EQUAL 35)
+    return()
+  endif()
+endif()
+
 check_source_compiles(Fortran [=[
       PROGRAM TEST_HAVE_PRINT
         PRINT *, 'Hello'
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake
index 767fa69..8edff2c 100644
--- a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake
+++ b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesSwift.cmake
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0157 NEW)
 enable_language(Swift)
 include(CheckSourceCompiles)
 
diff --git a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsFortran.cmake b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsFortran.cmake
index 50e8ec8..fc5506a 100644
--- a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsFortran.cmake
+++ b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsFortran.cmake
@@ -5,6 +5,14 @@
 
 set(Fortran 1) # test that this is tolerated
 
+# lfortran < 1.24 cannot handle long file names.
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "LCC" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "1.24")
+  string(LENGTH "${CMAKE_CURRENT_BINARY_DIR}" _CCBD_LEN)
+  if(_CCBD_LEN GREATER_EQUAL 35)
+    return()
+  endif()
+endif()
+
 check_source_runs(Fortran [=[
       PROGRAM TEST_HAVE_PRINT
         PRINT *, 'Hello'
diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
index c01f414..2bbe1c8 100644
--- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
+++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
@@ -1 +1 @@
-^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":6}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
+^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":7}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt
new file mode 100644
index 0000000..8210d59
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_cat-stdin-stdout.txt
@@ -0,0 +1 @@
+^Hello world$
diff --git a/Tests/RunCMake/CommandLine/E_cat-stdin.cmake b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake
new file mode 100644
index 0000000..e83e619
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_cat-stdin.cmake
@@ -0,0 +1,10 @@
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" "ell")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rld.txt" "rld")
+execute_process(
+  COMMAND ${CMAKE_COMMAND} -E echo_append "H"
+  COMMAND ${CMAKE_COMMAND} -E cat -
+  )
+execute_process(
+  COMMAND ${CMAKE_COMMAND} -E echo_append "o wo"
+  COMMAND ${CMAKE_COMMAND} -E cat "${CMAKE_CURRENT_BINARY_DIR}/ell.txt" - "${CMAKE_CURRENT_BINARY_DIR}/rld.txt"
+  )
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefix-stdout.txt b/Tests/RunCMake/CommandLine/EnvInstallPrefix-stdout.txt
new file mode 100644
index 0000000..a0ca3d5
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefix-stdout.txt
@@ -0,0 +1,3 @@
+-- ENV{CMAKE_INSTALL_PREFIX}='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_via_env_var'
+-- CMAKE_INSTALL_PREFIX='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_via_env_var'
+-- CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT=''
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefix.cmake b/Tests/RunCMake/CommandLine/EnvInstallPrefix.cmake
new file mode 100644
index 0000000..24b1840
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefix.cmake
@@ -0,0 +1,3 @@
+message(STATUS "ENV{CMAKE_INSTALL_PREFIX}='$ENV{CMAKE_INSTALL_PREFIX}'")
+message(STATUS "CMAKE_INSTALL_PREFIX='${CMAKE_INSTALL_PREFIX}'")
+message(STATUS "CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT='${CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT}'")
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt-stdout.txt b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt-stdout.txt
new file mode 100644
index 0000000..b95e277
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt-stdout.txt
@@ -0,0 +1,3 @@
+-- ENV{CMAKE_INSTALL_PREFIX}='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_via_env_var'
+-- CMAKE_INSTALL_PREFIX='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_cmd_line_opt'
+-- CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT=''
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt.cmake b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt.cmake
new file mode 100644
index 0000000..6de6be6
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithCmdLineOpt.cmake
@@ -0,0 +1 @@
+include(EnvInstallPrefix.cmake)
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar-stdout.txt b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar-stdout.txt
new file mode 100644
index 0000000..3b7181d
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar-stdout.txt
@@ -0,0 +1,3 @@
+-- ENV{CMAKE_INSTALL_PREFIX}='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_via_env_var'
+-- CMAKE_INSTALL_PREFIX='[^']*/Tests/RunCMake/CommandLine/install_prefix_set_via_var'
+-- CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT=''
diff --git a/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar.cmake b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar.cmake
new file mode 100644
index 0000000..6de6be6
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvInstallPrefixOverrideWithVar.cmake
@@ -0,0 +1 @@
+include(EnvInstallPrefix.cmake)
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index b29e50a..7b34773 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -459,6 +459,15 @@
   run_EnvironmentConfigTypes()
 endif()
 
+function(run_EnvironmentInstallPrefix)
+  set(ENV{CMAKE_INSTALL_PREFIX} "${RunCMake_BINARY_DIR}/install_prefix_set_via_env_var")
+  run_cmake(EnvInstallPrefix)
+  run_cmake_with_options(EnvInstallPrefixOverrideWithVar -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/install_prefix_set_via_var)
+  run_cmake_with_options(EnvInstallPrefixOverrideWithCmdLineOpt --install-prefix ${RunCMake_BINARY_DIR}/install_prefix_set_cmd_line_opt)
+  unset(ENV{CMAKE_INSTALL_PREFIX})
+endfunction()
+run_EnvironmentInstallPrefix()
+
 function(run_EnvironmentToolchain)
   set(ENV{CMAKE_TOOLCHAIN_FILE} "${RunCMake_SOURCE_DIR}/EnvToolchain-toolchain.cmake")
   run_cmake(EnvToolchainAbsolute)
@@ -782,6 +791,8 @@
 unset(RunCMake_TEST_COMMAND_WORKING_DIRECTORY)
 unset(out)
 
+run_cmake_command(E_cat-stdin ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_cat-stdin.cmake)
+
 # Unset environment variables that are used for testing cmake -E
 unset(ENV{TEST_ENV})
 unset(ENV{TEST_ENV_EXPECTED})
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
index 4107aa4..ef08627 100644
--- a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
+++ b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
@@ -1,2 +1,3 @@
 cmake_policy(VERSION 3.24)
+cmake_policy(SET CMP0159 NEW)
 enable_language(C)
diff --git a/Tests/RunCMake/CompileDefinitions/foo.c b/Tests/RunCMake/CompileDefinitions/foo.c
index 74a86e1..7d75e37 100644
--- a/Tests/RunCMake/CompileDefinitions/foo.c
+++ b/Tests/RunCMake/CompileDefinitions/foo.c
@@ -1,4 +1,4 @@
 
-void foo()
+void foo(void)
 {
 }
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
index 320c2ba..4b3774f 100644
--- a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
@@ -1,3 +1,14 @@
+^CMake Deprecation Warning at CMP0128WarnMatch-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0128 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
 CMake Warning \(dev\) in CMakeLists\.txt:
   Policy CMP0128 is not set: Selection of language standard and extension
   flags improved\.  Run "cmake --help-policy CMP0128" for policy details\.  Use
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
index 068cba9..37a0767 100644
--- a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
@@ -1,3 +1,14 @@
+^CMake Deprecation Warning at CMP0128WarnUnset-(C|CXX)\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0128 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
 CMake Warning \(dev\) in CMakeLists\.txt:
   Policy CMP0128 is not set: Selection of language standard and extension
   flags improved\.  Run "cmake --help-policy CMP0128" for policy details\.  Use
diff --git a/Tests/RunCMake/CompileWarningAsError/warn.c b/Tests/RunCMake/CompileWarningAsError/warn.c
index cd0c2ba..4041167 100644
--- a/Tests/RunCMake/CompileWarningAsError/warn.c
+++ b/Tests/RunCMake/CompileWarningAsError/warn.c
@@ -1,4 +1,4 @@
-static void unused_function();
+static void unused_function(void);
 
 #ifdef __SUNPRO_C
 KandR(x) int x;
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt
index b7db7eb..bc5d9ea 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-JOM.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the JOM generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt
index 03c5933..80d8a21 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr-NMake.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the NMake generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt
index c98842d..96d02de 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerC-stderr.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   Tell CMake where to find the compiler by setting either the environment
   variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt
index 4b42ea6..d108787 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-JOM.txt
@@ -3,7 +3,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the JOM generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt
index 1bfcdcc..d3c0ccd 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr-NMake.txt
@@ -3,7 +3,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the NMake generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt
index 7ef4f5e..79db41d 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCXX-stderr.txt
@@ -3,7 +3,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   Tell CMake where to find the compiler by setting either the environment
   variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt
index f25a267..360a8c2 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-JOM.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the JOM generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
@@ -21,7 +22,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the JOM generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt
index ffcdce8..8438d0e 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr-NMake.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the NMake generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
@@ -21,7 +22,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   To use the NMake generator with Visual C\+\+, cmake must be run from a shell
   that can use the compiler cl from the command line.  This environment is
diff --git a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt
index eecff54..cba0db2 100644
--- a/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt
+++ b/Tests/RunCMake/CompilerNotFound/BadCompilerCandCXX-stderr.txt
@@ -3,7 +3,8 @@
 
     no-C-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   Tell CMake where to find the compiler by setting either the environment
   variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to
@@ -16,7 +17,8 @@
 
     no-CXX-compiler
 
-  is not a full path and was not found in the PATH.
+  is not a full path and was not found in the PATH.(  Perhaps the extension is
+  missing\?)?
 
   Tell CMake where to find the compiler by setting either the environment
   variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path
diff --git a/Tests/RunCMake/Configure/ReadOnly-result.txt b/Tests/RunCMake/Configure/ReadOnly-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Configure/ReadOnly-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Configure/ReadOnly-stderr.txt b/Tests/RunCMake/Configure/ReadOnly-stderr.txt
new file mode 100644
index 0000000..fb00ae9
--- /dev/null
+++ b/Tests/RunCMake/Configure/ReadOnly-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error: Unable to \(re\)create the private pkgRedirects directory:
+  [^
+]*/Tests/RunCMake/Configure/ReadOnly-build/CMakeFiles/pkgRedirects
+This may be caused by not having read/write access to the build directory.
+Try specifying a location with read/write access like:
+  cmake -B build
+If using a CMake presets file, ensure that preset parameter
+'binaryDir' expands to a writable directory.
diff --git a/Tests/RunCMake/Configure/ReadOnly.cmake b/Tests/RunCMake/Configure/ReadOnly.cmake
new file mode 100644
index 0000000..2fc38e5
--- /dev/null
+++ b/Tests/RunCMake/Configure/ReadOnly.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/Configure/RunCMakeTest.cmake b/Tests/RunCMake/Configure/RunCMakeTest.cmake
index df6849e..842a005 100644
--- a/Tests/RunCMake/Configure/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Configure/RunCMakeTest.cmake
@@ -55,3 +55,19 @@
   run_cmake(NoCMAKE_DEFAULT_BUILD_TYPE)
   run_cmake(NoCMAKE_DEFAULT_CONFIGS)
 endif()
+
+if(NOT CMAKE_HOST_WIN32)
+  block()
+    # Test a non-writable build directory.
+    # Exclude when running as root because directories are always writable.
+    get_unix_uid(uid)
+    if(NOT uid STREQUAL "0")
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ReadOnly-build)
+      file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+      file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+      file(CHMOD "${RunCMake_TEST_BINARY_DIR}" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      run_cmake(ReadOnly)
+    endif()
+  endblock()
+endif()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake
index e10b161..45c66ea 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand-build-check.cmake
@@ -1,5 +1,6 @@
-foreach(output IN ITEMS output1 output2 output3 output4)
+foreach(output IN ITEMS output1 output2 output3 output4 output5)
   if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${output}")
-    message(FATAL_ERROR "Failed to create output: ${RunCMake_TEST_BINARY_DIR}/${output}")
+    set(RunCMake_TEST_FAILED "Failed to create output:\n  ${RunCMake_TEST_BINARY_DIR}/${output}")
+    return()
   endif()
 endforeach()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake
index 9fcc5bc..58bf817 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommand.cmake
@@ -1,10 +1,13 @@
+enable_language(C)
 set(CMAKE_CROSSCOMPILING 1)
 
 # Executable: Return error code different from 0
-add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx)
+add_executable(generated_exe_emulator_expected simple_src_exiterror.c)
+get_property(emulator TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR)
+set_property(TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR "$<1:${emulator}>")
 
 # Executable: Return error code equal to 0
-add_executable(generated_exe_emulator_unexpected generated_exe_emulator_unexpected.cxx)
+add_executable(generated_exe_emulator_unexpected emulator_unexpected.c)
 # Place the executable in a predictable location.
 set_property(TARGET generated_exe_emulator_unexpected PROPERTY RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_BINARY_DIR}>)
 
@@ -46,6 +49,17 @@
   COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output4
   DEPENDS generated_exe_emulator_expected)
 
+# Use locally-built emulator.
+add_executable(local_emulator ../pseudo_emulator.c)
+add_executable(use_emulator_local simple_src_exiterror.c)
+set_property(TARGET use_emulator_local PROPERTY CROSSCOMPILING_EMULATOR "$<TARGET_FILE:local_emulator>")
+add_dependencies(use_emulator_local local_emulator)
+add_custom_command(OUTPUT output5
+  COMMAND ${CMAKE_COMMAND} -E echo use_emulator_local
+  COMMAND use_emulator_local
+  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output5
+  DEPENDS use_emulator_local)
+
 add_custom_target(ensure_build ALL
   SOURCES
     ${CMAKE_CURRENT_BINARY_DIR}/output1
@@ -53,4 +67,5 @@
     ${CMAKE_CURRENT_BINARY_DIR}/output3
     ${CMAKE_CURRENT_BINARY_DIR}/outputImp
     ${CMAKE_CURRENT_BINARY_DIR}/output4
+    ${CMAKE_CURRENT_BINARY_DIR}/output5
 )
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg-build-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg-build-check.cmake
index 9ca6106..691191d 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg-build-check.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg-build-check.cmake
@@ -1,3 +1,4 @@
 if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/output")
-  message(FATAL_ERROR "Failed to create output: ${RunCMake_TEST_BINARY_DIR}/output")
+  set(RunCMake_TEST_FAILED "Failed to create output:\n  ${RunCMake_TEST_BINARY_DIR}/output")
+  return()
 endif()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg.cmake
index d9059ca..c5efe03 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomCommandWithArg.cmake
@@ -1,7 +1,10 @@
+enable_language(C)
 set(CMAKE_CROSSCOMPILING 1)
 
 # Executable: Return error code different from 0
-add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx)
+add_executable(generated_exe_emulator_expected simple_src_exiterror.c)
+get_property(emulator TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR)
+set_property(TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR "$<1:${emulator}>")
 
 add_custom_command(OUTPUT output
   COMMAND generated_exe_emulator_expected
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake
index 5b01abc..9d7c2b1 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTarget.cmake
@@ -1,10 +1,13 @@
+enable_language(C)
 set(CMAKE_CROSSCOMPILING 1)
 
 # Executable: Return error code different from 0
-add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx)
+add_executable(generated_exe_emulator_expected simple_src_exiterror.c)
+get_property(emulator TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR)
+set_property(TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR "$<1:${emulator}>")
 
 # Executable: Return error code equal to 0
-add_executable(generated_exe_emulator_unexpected generated_exe_emulator_unexpected.cxx)
+add_executable(generated_exe_emulator_unexpected emulator_unexpected.c)
 # Place the executable in a predictable location.
 set_property(TARGET generated_exe_emulator_unexpected PROPERTY RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_BINARY_DIR}>)
 
@@ -42,3 +45,13 @@
   generated_exe_emulator_expected
   COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output4
   DEPENDS generated_exe_emulator_expected)
+
+# Use locally-built emulator.
+add_executable(local_emulator ../pseudo_emulator.c)
+add_executable(use_emulator_local simple_src_exiterror.c)
+set_property(TARGET use_emulator_local PROPERTY CROSSCOMPILING_EMULATOR "$<TARGET_FILE:local_emulator>")
+add_dependencies(use_emulator_local local_emulator)
+add_custom_target(generate_output5 ALL
+  use_emulator_local
+  COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/output5
+  DEPENDS use_emulator_local)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTargetWithArg.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTargetWithArg.cmake
index dcd80d5..c08d58d 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddCustomTargetWithArg.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddCustomTargetWithArg.cmake
@@ -1,7 +1,10 @@
+enable_language(C)
 set(CMAKE_CROSSCOMPILING 1)
 
 # Executable: Return error code different from 0
-add_executable(generated_exe_emulator_expected simple_src_exiterror.cxx)
+add_executable(generated_exe_emulator_expected simple_src_exiterror.c)
+get_property(emulator TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR)
+set_property(TARGET generated_exe_emulator_expected PROPERTY CROSSCOMPILING_EMULATOR "$<1:${emulator}>")
 
 add_custom_target(generate_output ALL
   generated_exe_emulator_expected
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake
new file mode 100644
index 0000000..e6da71f
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-check.cmake
@@ -0,0 +1,34 @@
+set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake")
+if(EXISTS "${testfile}")
+  file(READ "${testfile}" testfile_contents)
+else()
+  set(RunCMake_TEST_FAILED "Could not find expected CTestTestfile.cmake.")
+  return()
+endif()
+
+set(error_details "There is a problem with generated test file:\n  ${testfile}")
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]ShouldNotUseEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-test-stdout.txt b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-test-stdout.txt
new file mode 100644
index 0000000..a43c2f0
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-test-stdout.txt
@@ -0,0 +1,46 @@
+test 1
+    Start 1: DoesNotUseEmulator
+
+1: Test command: "?[^
+]*[/\]cmake(\.exe)?"? "-E" "echo" "Hi"
+1: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-build
+1: Test timeout computed to be: [0-9]+
+1: Hi
+1/5 Test #1: DoesNotUseEmulator [.]* +Passed +[0-9.]+ sec
+test 2
+    Start 2: ShouldNotUseEmulator
+
+2: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-NEW-build([/\]Debug)?[/\]exe(\.exe)?"?
+2: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-build
+2: Test timeout computed to be: [0-9]+
+2/5 Test #2: ShouldNotUseEmulator [.]* +Passed +[0-9.]+ sec
+test 3
+    Start 3: DoesNotUseEmulatorWithGenex
+
+3: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-NEW-build([/\]Debug)?[/\]exe(\.exe)?"?
+3: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-build
+3: Test timeout computed to be: [0-9]+
+3/5 Test #3: DoesNotUseEmulatorWithGenex [.]* +Passed +[0-9.]+ sec
+test 4
+    Start 4: ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex
+
+4: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-NEW-build[/\]AddTest([/\]Debug)?[/\]subdir_exe_no_genex(\.exe)?"?
+4: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-build
+4: Test timeout computed to be: [0-9]+
+4/5 Test #4: ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex [.]* +Passed +[0-9.]+ sec
+test 5
+    Start 5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
+
+5: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-NEW-build[/\]AddTest([/\]Debug)?[/\]subdir_exe_with_genex(\.exe)?"?
+5: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW-build
+5: Test timeout computed to be: [0-9]+
+5/5 Test #5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [.]* +Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake
new file mode 100644
index 0000000..5550b91
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-NEW.cmake
@@ -0,0 +1,27 @@
+enable_language(C)
+enable_testing()
+if(CMAKE_CROSSCOMPILING)
+  message(FATAL_ERROR "cross compiling")
+endif()
+
+cmake_policy(SET CMP0158 NEW)
+
+enable_testing()
+add_test(NAME DoesNotUseEmulator
+  COMMAND ${CMAKE_COMMAND} -E echo "Hi")
+
+add_executable(exe main.c)
+
+add_test(NAME ShouldNotUseEmulator
+  COMMAND exe)
+
+add_test(NAME DoesNotUseEmulatorWithGenex
+  COMMAND $<TARGET_FILE:exe>)
+
+add_subdirectory(AddTest)
+
+add_test(NAME ShouldNotUseEmulatorWithExecTargetFromSubdirAddedWithoutGenex
+  COMMAND subdir_exe_no_genex)
+
+add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
+  COMMAND $<TARGET_FILE:subdir_exe_with_genex>)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake
new file mode 100644
index 0000000..b92ed4e
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-check.cmake
@@ -0,0 +1,34 @@
+set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake")
+if(EXISTS "${testfile}")
+  file(READ "${testfile}" testfile_contents)
+else()
+  set(RunCMake_TEST_FAILED "Could not find expected CTestTestfile.cmake.")
+  return()
+endif()
+
+set(error_details "There is a problem with generated test file:\n  ${testfile}")
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use emulator when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use emulator when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-test-stdout.txt b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-test-stdout.txt
new file mode 100644
index 0000000..683a866
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-test-stdout.txt
@@ -0,0 +1,48 @@
+test 1
+    Start 1: DoesNotUseEmulator
+
+1: Test command: "?[^
+]*[/\]cmake(\.exe)?"? "-E" "echo" "Hi"
+1: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build
+1: Test timeout computed to be: [0-9]+
+1: Hi
+1/5 Test #1: DoesNotUseEmulator [.]* +Passed +[0-9.]+ sec
+test 2
+    Start 2: UsesEmulator
+
+2: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build(/Debug)?/exe(\.exe)?"
+2: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build
+2: Test timeout computed to be: [0-9]+
+2: Command: "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build(/Debug)?/exe(\.exe)?"
+2/5 Test #2: UsesEmulator [.]* +Passed +[0-9.]+ sec
+test 3
+    Start 3: DoesNotUseEmulatorWithGenex
+
+3: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-OLD-build([/\]Debug)?[/\]exe(\.exe)?"?
+3: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build
+3: Test timeout computed to be: [0-9]+
+3/5 Test #3: DoesNotUseEmulatorWithGenex [.]* +Passed +[0-9.]+ sec
+test 4
+    Start 4: UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex
+
+4: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build/AddTest(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build
+4: Test timeout computed to be: [0-9]+
+4: Command: "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build/AddTest(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4/5 Test #4: UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [.]* +Passed +[0-9.]+ sec
+test 5
+    Start 5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
+
+5: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-CMP0158-OLD-build[/\]AddTest([/\]Debug)?[/\]subdir_exe_with_genex(\.exe)?"?
+5: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD-build
+5: Test timeout computed to be: [0-9]+
+5/5 Test #5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [.]* +Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake
new file mode 100644
index 0000000..e85fd40
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-CMP0158-OLD.cmake
@@ -0,0 +1,26 @@
+enable_language(C)
+enable_testing()
+if(CMAKE_CROSSCOMPILING)
+  message(FATAL_ERROR "cross compiling")
+endif()
+
+cmake_policy(SET CMP0158 OLD)
+
+add_test(NAME DoesNotUseEmulator
+  COMMAND ${CMAKE_COMMAND} -E echo "Hi")
+
+add_executable(exe main.c)
+
+add_test(NAME UsesEmulator
+  COMMAND exe)
+
+add_test(NAME DoesNotUseEmulatorWithGenex
+  COMMAND $<TARGET_FILE:exe>)
+
+add_subdirectory(AddTest)
+
+add_test(NAME UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex
+  COMMAND subdir_exe_no_genex)
+
+add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
+  COMMAND $<TARGET_FILE:subdir_exe_with_genex>)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake
index 588b77b..69c2332 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-check.cmake
@@ -2,27 +2,42 @@
 if(EXISTS "${testfile}")
   file(READ "${testfile}" testfile_contents)
 else()
-  message(FATAL_ERROR "Could not find expected CTestTestfile.cmake.")
+  set(RunCMake_TEST_FAILED "Could not find expected CTestTestfile.cmake.")
+  return()
 endif()
 
-set(error_details "There is a problem with generated test file: ${testfile}")
+set(error_details "There is a problem with generated test file:\n  ${testfile}")
 
-if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^\n]+pseudo_emulator[^\n]+\n")
-  message(SEND_ERROR "Used emulator when it should not be used. ${error_details}")
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
 endif()
 
-if(NOT testfile_contents MATCHES "add_test[(]UsesEmulator [^\n]+pseudo_emulator[^\n]+\n")
-  message(SEND_ERROR "Did not use emulator when it should be used. ${error_details}")
+if(NOT testfile_contents MATCHES "add_test[(]UsesEmulator [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use emulator when it should be used. ${error_details}")
+  return()
 endif()
 
-if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^\n]+pseudo_emulator[^\n]+\n")
-  message(SEND_ERROR "Used emulator when it should not be used. ${error_details}")
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
 endif()
 
-if(NOT testfile_contents MATCHES "add_test[(]UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^\n]+pseudo_emulator[^\n]+\n")
-  message(SEND_ERROR "Did not use emulator when it should be used. ${error_details}")
+if(NOT testfile_contents MATCHES "add_test[(]UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use emulator when it should be used. ${error_details}")
+  return()
 endif()
 
-if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^\n]+pseudo_emulator[^\n]+\n")
-  message(SEND_ERROR "Used emulator when it should not be used. ${error_details}")
+if(testfile_contents MATCHES "add_test[(]DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used emulator when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncherAndEmulator[^$<>\n]+pseudo_emulator[^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use test launcher and emulator when they should be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesLocalEmulator [^$<>\n]+local_emulator[^$<>\n]+use_emulator_local[^$<>\n]+\n")
+  message(SEND_ERROR "Did not use local emulator when it should be used. ${error_details}")
 endif()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest-test-stdout.txt b/Tests/RunCMake/CrosscompilingEmulator/AddTest-test-stdout.txt
new file mode 100644
index 0000000..a343053
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest-test-stdout.txt
@@ -0,0 +1,68 @@
+test 1
+    Start 1: DoesNotUseEmulator
+
+1: Test command: "?[^
+]*[/\]cmake(\.exe)?"? "-E" "echo" "Hi"
+1: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+1: Test timeout computed to be: [0-9]+
+1: Hi
+1/7 Test #1: DoesNotUseEmulator [.]* +Passed +[0-9.]+ sec
+test 2
+    Start 2: UsesEmulator
+
+2: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build(/Debug)?/exe(\.exe)?"
+2: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+2: Test timeout computed to be: [0-9]+
+2: Command: "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build(/Debug)?/exe(\.exe)?"
+2/7 Test #2: UsesEmulator [.]* +Passed +[0-9.]+ sec
+test 3
+    Start 3: DoesNotUseEmulatorWithGenex
+
+3: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-build([/\]Debug)?[/\]exe(\.exe)?"?
+3: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+3: Test timeout computed to be: [0-9]+
+3/7 Test #3: DoesNotUseEmulatorWithGenex [.]* +Passed +[0-9.]+ sec
+test 4
+    Start 4: UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex
+
+4: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build/AddTest(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+4: Test timeout computed to be: [0-9]+
+4: Command: "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build/AddTest(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4/7 Test #4: UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex [.]* +Passed +[0-9.]+ sec
+test 5
+    Start 5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
+
+5: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-build[/\]AddTest([/\]Debug)?[/\]subdir_exe_with_genex(\.exe)?"?
+5: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+5: Test timeout computed to be: [0-9]+
+5/7 Test #5: DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex [.]* +Passed +[0-9.]+ sec
+test 6
+    Start 6: UsesTestLauncherAndEmulator
+
+6: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake(/[^/]+)?/pseudo_emulator(\.exe)?" "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build(/Debug)?/exe_test_launcher(\.exe)?"
+6: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+6: Test timeout computed to be: [0-9]+
+6: Command: "[^"]*/Tests/RunCMake(/[^/]+)?/pseudo_emulator(\.exe)?" "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build(/Debug)?/exe_test_launcher(\.exe)?"
+6/7 Test #6: UsesTestLauncherAndEmulator [.]* +Passed +[0-9.]+ sec
+test 7
+    Start 7: UsesLocalEmulator
+
+7: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]CrosscompilingEmulator[/\]AddTest-build([/\]Debug)?[/\]local_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build(/Debug)?/use_emulator_local(\.exe)?"
+7: Working Directory: [^
+]*/Tests/RunCMake/CrosscompilingEmulator/AddTest-build
+7: Test timeout computed to be: [0-9]+
+7: Command: "[^"]*/use_emulator_local[^"]*"
+7/7 Test #7: UsesLocalEmulator [.]* +Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake
index 23e2e8d..2d169dc 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest.cmake
@@ -1,20 +1,36 @@
-set(CMAKE_CROSSCOMPILING 1)
+enable_language(C)
 enable_testing()
+set(CMAKE_CROSSCOMPILING 1)
+
 add_test(NAME DoesNotUseEmulator
   COMMAND ${CMAKE_COMMAND} -E echo "Hi")
 
-add_executable(generated_exe simple_src_exiterror.cxx)
+add_executable(exe main.c)
+get_property(emulator TARGET exe PROPERTY CROSSCOMPILING_EMULATOR)
+set_property(TARGET exe PROPERTY CROSSCOMPILING_EMULATOR "$<1:${emulator}>")
 
 add_test(NAME UsesEmulator
-  COMMAND generated_exe)
+  COMMAND exe)
 
 add_test(NAME DoesNotUseEmulatorWithGenex
-  COMMAND $<TARGET_FILE:generated_exe>)
+  COMMAND $<TARGET_FILE:exe>)
 
 add_subdirectory(AddTest)
 
 add_test(NAME UsesEmulatorWithExecTargetFromSubdirAddedWithoutGenex
-  COMMAND generated_exe_in_subdir_added_to_test_without_genex)
+  COMMAND subdir_exe_no_genex)
 
 add_test(NAME DoesNotUseEmulatorWithExecTargetFromSubdirAddedWithGenex
-  COMMAND $<TARGET_FILE:generated_exe_in_subdir_added_to_test_with_genex>)
+  COMMAND $<TARGET_FILE:subdir_exe_with_genex>)
+
+set(CMAKE_TEST_LAUNCHER "$<1:${CMAKE_CROSSCOMPILING_EMULATOR}>")
+add_executable(exe_test_launcher main.c)
+unset(CMAKE_TEST_LAUNCHER)
+
+add_test(NAME UsesTestLauncherAndEmulator
+  COMMAND exe_test_launcher)
+
+add_executable(local_emulator ../pseudo_emulator.c)
+add_executable(use_emulator_local main.c)
+set_property(TARGET use_emulator_local PROPERTY CROSSCOMPILING_EMULATOR "$<TARGET_FILE:local_emulator>")
+add_test(NAME UsesLocalEmulator COMMAND use_emulator_local)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/AddTest/CMakeLists.txt b/Tests/RunCMake/CrosscompilingEmulator/AddTest/CMakeLists.txt
index 025b54c..fa20ae9 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/AddTest/CMakeLists.txt
+++ b/Tests/RunCMake/CrosscompilingEmulator/AddTest/CMakeLists.txt
@@ -1,5 +1,2 @@
-add_executable(generated_exe_in_subdir_added_to_test_without_genex
-  ${CMAKE_CURRENT_SOURCE_DIR}/../simple_src_exiterror.cxx)
-
-add_executable(generated_exe_in_subdir_added_to_test_with_genex
-  ${CMAKE_CURRENT_SOURCE_DIR}/../simple_src_exiterror.cxx)
+add_executable(subdir_exe_no_genex ../main.c)
+add_executable(subdir_exe_with_genex ../main.c)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
index 12a7fd4..93ee9df 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
+++ b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
@@ -1,3 +1,3 @@
 cmake_minimum_required(VERSION 3.5)
-project(${RunCMake_TEST} CXX)
+project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/CrosscompilingEmulatorProperty.cmake b/Tests/RunCMake/CrosscompilingEmulator/CrosscompilingEmulatorProperty.cmake
index 2fdefc4..e9258fd 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/CrosscompilingEmulatorProperty.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/CrosscompilingEmulatorProperty.cmake
@@ -1,9 +1,11 @@
 # This tests setting the CROSSCOMPILING_EMULATOR target property from the
 # CMAKE_CROSSCOMPILING_EMULATOR variable.
 
+enable_language(C)
+
 # -DCMAKE_CROSSCOMPILING_EMULATOR=/path/to/pseudo_emulator is passed to this
 # test
-add_executable(target_with_emulator simple_src_exiterror.cxx)
+add_executable(target_with_emulator main.c)
 get_property(emulator TARGET target_with_emulator
              PROPERTY CROSSCOMPILING_EMULATOR)
 if(NOT "${emulator}" MATCHES "pseudo_emulator")
@@ -20,14 +22,14 @@
 endif()
 
 unset(CMAKE_CROSSCOMPILING_EMULATOR CACHE)
-add_executable(target_without_emulator simple_src_exiterror.cxx)
+add_executable(target_without_emulator main.c)
 get_property(emulator TARGET target_without_emulator
              PROPERTY CROSSCOMPILING_EMULATOR)
 if(NOT "${emulator}" STREQUAL "")
   message(SEND_ERROR "Default CROSSCOMPILING_EMULATOR property not set to null")
 endif()
 
-add_executable(target_with_empty_emulator simple_src_exiterror.cxx)
+add_executable(target_with_empty_emulator main.c)
 set_property(TARGET target_with_empty_emulator PROPERTY CROSSCOMPILING_EMULATOR "")
 
 enable_testing()
diff --git a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
index 1ffd91c..f32099f 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
@@ -5,7 +5,24 @@
 
 run_cmake(CrosscompilingEmulatorProperty)
 run_cmake(TryRun)
-run_cmake(AddTest)
+
+function(run_AddTest case)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+
+  run_cmake(${case})
+  unset(RunCMake_TEST_OPTIONS)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+
+  run_cmake_command(${case}-test ${CMAKE_CTEST_COMMAND} -C Debug -V)
+endfunction()
+
+run_AddTest(AddTest)
+run_AddTest(AddTest-CMP0158-OLD)
+run_AddTest(AddTest-CMP0158-NEW)
 
 function(CustomCommandGenerator_run_and_build case)
   # Use a single build tree for a few tests without cleaning.
diff --git a/Tests/RunCMake/CrosscompilingEmulator/TryRun.cmake b/Tests/RunCMake/CrosscompilingEmulator/TryRun.cmake
index af3712c..448c924 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/TryRun.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/TryRun.cmake
@@ -1,8 +1,11 @@
+enable_language(C)
 set(CMAKE_CROSSCOMPILING 1)
 
+set(ENV{PSEUDO_EMULATOR_FAIL} 1)
+
 try_run(run_result compile_result
   ${CMAKE_CURRENT_BINARY_DIR}
-  ${CMAKE_CURRENT_SOURCE_DIR}/simple_src_exiterror.cxx
+  ${CMAKE_CURRENT_SOURCE_DIR}/simple_src_exiterror.c
   RUN_OUTPUT_VARIABLE run_output)
 
 message(STATUS "run_output: ${run_output}")
@@ -13,6 +16,6 @@
   "multi arg")
 try_run(run_result compile_result
   ${CMAKE_CURRENT_BINARY_DIR}
-  ${CMAKE_CURRENT_SOURCE_DIR}/simple_src_exiterror.cxx
+  ${CMAKE_CURRENT_SOURCE_DIR}/simple_src_exiterror.c
   RUN_OUTPUT_VARIABLE run_output)
 message(STATUS "Emulator with arguments run_output: ${run_output}")
diff --git a/Tests/RunCMake/CrosscompilingEmulator/emulator_unexpected.c b/Tests/RunCMake/CrosscompilingEmulator/emulator_unexpected.c
new file mode 100644
index 0000000..e6bbcff
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/emulator_unexpected.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main(int argc, const char* argv[])
+{
+  int i;
+  for (i = 1; i < argc; ++i) {
+    fprintf(stderr, "unexpected argument: '%s'\n", argv[i]);
+  }
+  return argc == 1 ? 0 : 1;
+}
diff --git a/Tests/RunCMake/CrosscompilingEmulator/generated_exe_emulator_unexpected.cxx b/Tests/RunCMake/CrosscompilingEmulator/generated_exe_emulator_unexpected.cxx
deleted file mode 100644
index 233f432..0000000
--- a/Tests/RunCMake/CrosscompilingEmulator/generated_exe_emulator_unexpected.cxx
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdio.h>
-
-int main(int argc, const char* argv[])
-{
-  for (int i = 1; i < argc; ++i) {
-    fprintf(stderr, "unexpected argument: '%s'\n", argv[i]);
-  }
-  return argc == 1 ? 0 : 1;
-}
diff --git a/Tests/RunCMake/CrosscompilingEmulator/main.c b/Tests/RunCMake/CrosscompilingEmulator/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.c b/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.c
new file mode 100644
index 0000000..a388185
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 13;
+}
diff --git a/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.cxx b/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.cxx
deleted file mode 100644
index 6ce7183..0000000
--- a/Tests/RunCMake/CrosscompilingEmulator/simple_src_exiterror.cxx
+++ /dev/null
@@ -1,4 +0,0 @@
-int main(int, char**)
-{
-  return 13;
-}
diff --git a/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt b/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt
new file mode 100644
index 0000000..53e554b
--- /dev/null
+++ b/Tests/RunCMake/DependencyProviders/ProviderFirst-stdout.txt
@@ -0,0 +1,6 @@
+-- Before cmake_language
+-- After cmake_language
+-- Intercepted FetchContent_MakeAvailable\(SomeDep\)
+-- Provider invoked for method FETCHCONTENT_MAKEAVAILABLE_SERIAL with args: SOURCE_DIR;.*/Tests/RunCMake/DependencyProviders;BINARY_DIR;.*/Tests/RunCMake/DependencyProviders/ProviderFirst-build/_deps/somedep-build;EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR;SOURCE_SUBDIR;DoesNotExist;FIND_PACKAGE_ARGS;QUIET;NO_DEFAULT_PATH;COMPONENTS;abc
+-- FetchContent_MakeAvailable\(\) succeeded
+-- Configuring done
diff --git a/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake b/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake
new file mode 100644
index 0000000..b27c841
--- /dev/null
+++ b/Tests/RunCMake/DependencyProviders/ProviderFirst.cmake
@@ -0,0 +1,8 @@
+include(FetchContent)
+FetchContent_Declare(SomeDep
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}
+  SOURCE_SUBDIR DoesNotExist
+  FIND_PACKAGE_ARGS NO_DEFAULT_PATH COMPONENTS abc
+)
+FetchContent_MakeAvailable(SomeDep)
+message(STATUS "FetchContent_MakeAvailable() succeeded")
diff --git a/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake
index 42893d2..300bbc1 100644
--- a/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake
+++ b/Tests/RunCMake/DependencyProviders/RunCMakeTest.cmake
@@ -61,6 +61,11 @@
   -D "provider_command=redirect_FetchContentSerial_provider"
   -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL"
 )
+run_cmake_with_options(ProviderFirst
+  -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake"
+  -D "provider_command=FetchContentSerial_provider"
+  -D "provider_methods=FETCHCONTENT_MAKEAVAILABLE_SERIAL"
+)
 run_cmake_with_options(Bypass
   -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=set_provider.cmake"
   -D "provider_command=forward_find_package"
diff --git a/Tests/RunCMake/ExcludeFromAll/main.c b/Tests/RunCMake/ExcludeFromAll/main.c
index f8b643a..8488f4e 100644
--- a/Tests/RunCMake/ExcludeFromAll/main.c
+++ b/Tests/RunCMake/ExcludeFromAll/main.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake b/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake
new file mode 100644
index 0000000..e6f4e16
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/CMake/FindFDE-S.cmake
@@ -0,0 +1,6 @@
+if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue")
+  message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n  SomeOtherValue\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+endif()
+
+set(fp_called TRUE)
+set(FDE-S_FOUND TRUE)
diff --git a/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake b/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake
new file mode 100644
index 0000000..ef91cc3
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/CMake/FindFDE-U.cmake
@@ -0,0 +1,6 @@
+if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME)
+  message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should not be defined")
+endif()
+
+set(fp_called TRUE)
+set(FDE-U_FOUND TRUE)
diff --git a/Tests/RunCMake/FetchContent/FindDependencyExport.cmake b/Tests/RunCMake/FetchContent/FindDependencyExport.cmake
new file mode 100644
index 0000000..c79c357
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/FindDependencyExport.cmake
@@ -0,0 +1,78 @@
+include(FetchContent)
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
+
+unset(dp_called)
+unset(fp_called)
+set(_expected_export_find_package_name_dp FDE-U)
+FetchContent_Declare(
+  FDE-U
+  FIND_PACKAGE_ARGS
+  )
+FetchContent_MakeAvailable(FDE-U)
+
+if(NOT dp_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call dependency provider")
+endif()
+if(NOT fp_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call find_package()")
+endif()
+
+if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME)
+  message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should have been unset after FetchContent_MakeAvailable().\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+endif()
+
+unset(sub_called)
+set(_expected_export_find_package_name_dp FDE-U-Sub)
+set(_expected_export_find_package_name_sub FDE-U-Sub)
+FetchContent_Declare(
+  FDE-U-Sub
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/FindDependencyExport
+  )
+FetchContent_MakeAvailable(FDE-U-Sub)
+
+if(NOT sub_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call add_subdirectory()")
+endif()
+
+if(DEFINED CMAKE_EXPORT_FIND_PACKAGE_NAME)
+  message(FATAL_ERROR "CMAKE_EXPORT_FIND_PACKAGE_NAME should have been unset after FetchContent_MakeAvailable()")
+endif()
+
+unset(dp_called)
+unset(fp_called)
+set(CMAKE_EXPORT_FIND_PACKAGE_NAME SomeOtherValue)
+set(_expected_export_find_package_name_dp FDE-S)
+FetchContent_Declare(
+  FDE-S
+  FIND_PACKAGE_ARGS
+  )
+FetchContent_MakeAvailable(FDE-S)
+
+if(NOT dp_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call dependency provider")
+endif()
+if(NOT fp_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call find_package()")
+endif()
+
+if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue")
+  message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n  SomeOtherValue\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+endif()
+
+unset(sub_called)
+set(_expected_export_find_package_name_dp FDE-S-Sub)
+set(_expected_export_find_package_name_sub FDE-S-Sub)
+FetchContent_Declare(
+  FDE-S-Sub
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/FindDependencyExport
+  )
+FetchContent_MakeAvailable(FDE-S-Sub)
+
+if(NOT sub_called)
+  message(FATAL_ERROR "FetchContent_MakeAvailable did not call add_subdirectory()")
+endif()
+
+if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL "SomeOtherValue")
+  message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n  SomeOtherValue\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+endif()
diff --git a/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt b/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt
new file mode 100644
index 0000000..8bd4036
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/FindDependencyExport/CMakeLists.txt
@@ -0,0 +1,5 @@
+if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL _expected_export_find_package_name_sub)
+  message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n  ${_expected_export_find_package_name_sub}\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+endif()
+
+set(sub_called TRUE PARENT_SCOPE)
diff --git a/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake b/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake
new file mode 100644
index 0000000..7cabe51
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/FindDependencyExportDP.cmake
@@ -0,0 +1,11 @@
+function(fde_provide_dependency method name)
+  if(NOT CMAKE_EXPORT_FIND_PACKAGE_NAME STREQUAL _expected_export_find_package_name_dp)
+    message(FATAL_ERROR "Expected value of CMAKE_EXPORT_FIND_PACKAGE_NAME:\n  ${_expected_export_find_package_name_dp}\nActual value:\n  ${CMAKE_EXPORT_FIND_PACKAGE_NAME}")
+  endif()
+
+  set(dp_called TRUE PARENT_SCOPE)
+endfunction()
+
+cmake_language(SET_DEPENDENCY_PROVIDER fde_provide_dependency
+  SUPPORTED_METHODS FETCHCONTENT_MAKEAVAILABLE_SERIAL
+  )
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
index d0790eb..0f443a7 100644
--- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -19,6 +19,10 @@
 run_cmake(MakeAvailableUndeclared)
 run_cmake(VerifyHeaderSet)
 
+run_cmake_with_options(FindDependencyExport
+  -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${CMAKE_CURRENT_LIST_DIR}/FindDependencyExportDP.cmake"
+)
+
 run_cmake_with_options(ManualSourceDirectory
   -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/WithProject"
 )
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 807d92b..5f41ad9 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -12,7 +12,7 @@
 def check_objects(o, g):
     assert is_list(o)
     assert len(o) == 1
-    check_index_object(o[0], "codemodel", 2, 6, check_object_codemodel(g))
+    check_index_object(o[0], "codemodel", 2, 7, check_object_codemodel(g))
 
 def check_backtrace(t, b, backtrace):
     btg = t["backtraceGraph"]
@@ -405,6 +405,30 @@
                              missing_exception=lambda e: "Install path: %s" % e["path"],
                              extra_exception=lambda a: "Install path: %s" % a["path"])
 
+        if "launchers" in expected:
+            if expected["launchers"] is not None:
+                expected_keys.append("launchers")
+                def check_launcher(actual, expected):
+                    assert is_dict(actual)
+                    launcher_keys = ["command", "type"]
+                    if "arguments" in expected:
+                        launcher_keys.append("arguments")
+                    assert sorted(actual.keys()) == sorted(launcher_keys)
+                    assert matches(actual["command"], expected["command"])
+                    assert matches(actual["type"], expected["type"])
+                    if "arguments" in expected:
+                        if expected["arguments"] is not None:
+                            check_list_match(lambda a, e: matches(a, e),
+                                             actual["arguments"], expected["arguments"],
+                                             missing_exception=lambda e: "argument: %s" % e,
+                                             extra_exception=lambda a: "argument: %s" % actual["arguments"])
+                check_list_match(lambda a, e: matches(a["type"], e["type"]),
+                                obj["launchers"], expected["launchers"],
+                                check=check_launcher,
+                                check_exception=lambda a, e: "launchers: %s" % a,
+                                missing_exception=lambda e: "launchers: %s" % e,
+                                extra_exception=lambda a: "launchers: %s" % a)
+
         if expected["link"] is not None:
             expected_keys.append("link")
             assert is_dict(obj["link"])
@@ -709,6 +733,7 @@
         read_codemodel_json_data("directories/alias.json"),
         read_codemodel_json_data("directories/custom.json"),
         read_codemodel_json_data("directories/cxx.json"),
+        read_codemodel_json_data("directories/cxx.cross.json"),
         read_codemodel_json_data("directories/imported.json"),
         read_codemodel_json_data("directories/interface.json"),
         read_codemodel_json_data("directories/object.json"),
@@ -782,6 +807,10 @@
         read_codemodel_json_data("targets/zero_check_cxx.json"),
         read_codemodel_json_data("targets/cxx_lib.json"),
         read_codemodel_json_data("targets/cxx_exe.json"),
+        read_codemodel_json_data("targets/cxx_exe_cross_emulator.json"),
+        read_codemodel_json_data("targets/cxx_exe_cross_emulator_args.json"),
+        read_codemodel_json_data("targets/cxx_exe_test_launcher_and_cross_emulator.json"),
+        read_codemodel_json_data("targets/cxx_exe_test_launcher.json"),
         read_codemodel_json_data("targets/cxx_standard_compile_feature_exe.json"),
         read_codemodel_json_data("targets/cxx_standard_exe.json"),
         read_codemodel_json_data("targets/cxx_shared_lib.json"),
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json
new file mode 100644
index 0000000..8f6ded5
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.cross.json
@@ -0,0 +1,16 @@
+{
+    "source": "^cxx/cross$",
+    "build": "^cxx/cross$",
+    "parentSource": "^cxx$",
+    "parentIndex": 2,
+    "childSources": null,
+    "targetIds": [
+        "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$",
+        "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$",
+        "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$"
+    ],
+    "projectName": "Cxx",
+    "minimumCMakeVersion": "3.13",
+    "hasInstallRule": null,
+    "installers": []
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
index 22dfabd..912d664 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
@@ -2,11 +2,14 @@
     "source": "^cxx$",
     "build": "^cxx$",
     "parentSource": "^\\.$",
-    "childSources": null,
+    "childSources": [
+        "^cxx/cross$"
+    ],
     "targetIds": [
         "^ALL_BUILD::@a56b12a3f5c0529fb296$",
         "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
         "^cxx_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$",
         "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
         "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
         "^cxx_lib::@a56b12a3f5c0529fb296$",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
index 363e853..c8a1a83 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/cxx.json
@@ -3,13 +3,18 @@
     "parentName": "codemodel-v2",
     "childNames": null,
     "directorySources": [
-        "^cxx$"
+        "^cxx$",
+        "^cxx/cross$"
     ],
     "targetIds": [
         "^ALL_BUILD::@a56b12a3f5c0529fb296$",
         "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
         "^cxx_lib::@a56b12a3f5c0529fb296$",
         "^cxx_exe::@a56b12a3f5c0529fb296$",
+        "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$",
+        "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$",
+        "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$",
+        "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$",
         "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
         "^cxx_standard_exe::@a56b12a3f5c0529fb296$",
         "^cxx_shared_lib::@a56b12a3f5c0529fb296$",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
index bf36bfe..3a83d29 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_cxx.json
@@ -83,6 +83,22 @@
             "backtrace": null
         },
         {
+            "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
             "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
             "backtrace": null
         },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
index 9d0007f..ec72eb8 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
@@ -123,6 +123,22 @@
             "backtrace": null
         },
         {
+            "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$",
+            "backtrace": null
+        },
+        {
+            "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        },
+        {
             "id": "^cxx_standard_compile_feature_exe::@a56b12a3f5c0529fb296$",
             "backtrace": null
         },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json
new file mode 100644
index 0000000..bbd973b
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator.json
@@ -0,0 +1,105 @@
+{
+    "name": "cxx_exe_cross_emulator",
+    "id": "^cxx_exe_cross_emulator::@ee4a268216d1f53c4e2e$",
+    "directorySource": "^cxx/cross$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "fileSets": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "fileSetName": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "frameworks": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": 3,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_exe_cross_emulator(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx/cross$",
+    "source": "^cxx/cross$",
+    "install": null,
+    "launchers" : [
+        {
+            "command": "^no-such-emulator(\\.exe)?$",
+            "type" : "emulator"
+        }
+    ],
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": [
+            {
+                "fragment" : ".*",
+                "role" : "flags",
+                "backtrace": null
+            }
+        ]
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json
new file mode 100644
index 0000000..c1a8b0c
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_cross_emulator_args.json
@@ -0,0 +1,109 @@
+{
+    "name": "cxx_exe_cross_emulator_args",
+    "id": "^cxx_exe_cross_emulator_args::@ee4a268216d1f53c4e2e$",
+    "directorySource": "^cxx/cross$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "fileSets": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "fileSetName": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": 6,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "frameworks": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": 6,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_exe_cross_emulator_args(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator_args(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_cross_emulator_args\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx/cross$",
+    "source": "^cxx/cross$",
+    "install": null,
+    "launchers" : [
+        {
+            "arguments" : [
+                    "arg1",
+                    "arg2 with space"
+            ],
+            "command": "^no-such-emulator(\\.exe)?$",
+            "type" : "emulator"
+        }
+    ],
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": [
+            {
+                "fragment" : ".*",
+                "role" : "flags",
+                "backtrace": null
+            }
+        ]
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json
new file mode 100644
index 0000000..9002368
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher.json
@@ -0,0 +1,105 @@
+{
+    "name": "cxx_exe_test_launcher",
+    "id": "^cxx_exe_test_launcher::@a56b12a3f5c0529fb296$",
+    "directorySource": "^cxx$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "fileSets": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "fileSetName": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 49,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "frameworks": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": 49,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_exe_test_launcher(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx$",
+    "source": "^cxx$",
+    "install": null,
+    "launchers" : [
+        {
+            "command": "^no-such-launcher(\\.exe)?$",
+            "type" : "test"
+        }
+    ],
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": [
+            {
+                "fragment" : ".*",
+                "role" : "flags",
+                "backtrace": null
+            }
+        ]
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json
new file mode 100644
index 0000000..06e7a7b
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe_test_launcher_and_cross_emulator.json
@@ -0,0 +1,109 @@
+{
+    "name": "cxx_exe_test_launcher_and_cross_emulator",
+    "id": "^cxx_exe_test_launcher_and_cross_emulator::@ee4a268216d1f53c4e2e$",
+    "directorySource": "^cxx/cross$",
+    "projectName": "Cxx",
+    "type": "EXECUTABLE",
+    "isGeneratorProvided": null,
+    "fileSets": null,
+    "sources": [
+        {
+            "path": "^empty\\.cxx$",
+            "isGenerated": null,
+            "fileSetName": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "CXX",
+            "backtrace": [
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": 9,
+                    "command": "add_executable",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/cross/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "CXX",
+            "sourcePaths": [
+                "^empty\\.cxx$"
+            ],
+            "includes": null,
+            "frameworks": null,
+            "defines": null,
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": 9,
+            "command": "add_executable",
+            "hasParent": true
+        },
+        {
+            "file": "^cxx/cross/CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^cxx_exe_test_launcher_and_cross_emulator(\\.exe)?$",
+    "artifacts": [
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher_and_cross_emulator(\\.exe)?$",
+            "_dllExtra": false
+        },
+        {
+            "path": "^cxx/cross/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe_test_launcher_and_cross_emulator\\.pdb$",
+            "_dllExtra": true
+        }
+    ],
+    "build": "^cxx/cross$",
+    "source": "^cxx/cross$",
+    "install": null,
+    "launchers" : [
+        {
+            "command": "^no-such-launcher(\\.exe)?$",
+            "type" : "test"
+        },
+        {
+            "command": "^no-such-emulator(\\.exe)?$",
+            "type" : "emulator"
+        }
+    ],
+    "link": {
+        "language": "CXX",
+        "lto": null,
+        "commandFragments": [
+            {
+                "fragment" : ".*",
+                "role" : "flags",
+                "backtrace": null
+            }
+        ]
+    },
+    "archive": null,
+    "dependencies": [
+        {
+            "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+            "backtrace": null
+        }
+    ]
+}
diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
index 3ae3b60..d7d2e11 100644
--- a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
+++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
@@ -45,3 +45,8 @@
     FRAMEWORK DESTINATION fw
     )
 endif()
+
+add_executable(cxx_exe_test_launcher ../empty.cxx)
+set_property(TARGET cxx_exe_test_launcher PROPERTY TEST_LAUNCHER "$<1:no-such-launcher>")
+
+add_subdirectory(cross)
diff --git a/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt
new file mode 100644
index 0000000..b5ee38f
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/cxx/cross/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Cross-compiling is normally global.  Cover it without duplicating everything.
+set(CMAKE_CROSSCOMPILING 1)
+add_executable(cxx_exe_cross_emulator ../../empty.cxx)
+set_property(TARGET cxx_exe_cross_emulator PROPERTY CROSSCOMPILING_EMULATOR no-such-emulator)
+
+add_executable(cxx_exe_cross_emulator_args ../../empty.cxx)
+set_property(TARGET cxx_exe_cross_emulator_args PROPERTY CROSSCOMPILING_EMULATOR "no-such-emulator;arg1;arg2 with space")
+
+add_executable(cxx_exe_test_launcher_and_cross_emulator ../../empty.cxx)
+set_property(TARGET cxx_exe_test_launcher_and_cross_emulator PROPERTY TEST_LAUNCHER "$<1:no-such-launcher>")
+set_property(TARGET cxx_exe_test_launcher_and_cross_emulator PROPERTY CROSSCOMPILING_EMULATOR "$<1:no-such-emulator>")
diff --git a/Tests/RunCMake/File_Generate/empty.c b/Tests/RunCMake/File_Generate/empty.c
index f097d0a..05ffe2d 100644
--- a/Tests/RunCMake/File_Generate/empty.c
+++ b/Tests/RunCMake/File_Generate/empty.c
@@ -2,7 +2,7 @@
 #ifdef _WIN32
 __declspec(dllexport)
 #endif
-  int empty_c()
+  int empty_c(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/FindOpenGL/CMP0072-common.cmake b/Tests/RunCMake/FindOpenGL/CMP0072-common.cmake
index 3fe8030..ae0ce27 100644
--- a/Tests/RunCMake/FindOpenGL/CMP0072-common.cmake
+++ b/Tests/RunCMake/FindOpenGL/CMP0072-common.cmake
@@ -2,6 +2,7 @@
 set(WIN32 0)
 set(APPLE 0)
 set(OPENGL_INCLUDE_DIR GL/include)
+set(OPENGL_GLU_INCLUDE_DIR GLU/include)
 set(OPENGL_GLX_INCLUDE_DIR GLX/include)
 set(OPENGL_gl_LIBRARY GL)
 set(OPENGL_opengl_LIBRARY OpenGL)
diff --git a/Tests/RunCMake/Framework/consumer.c b/Tests/RunCMake/Framework/consumer.c
index a578976..4208a73 100644
--- a/Tests/RunCMake/Framework/consumer.c
+++ b/Tests/RunCMake/Framework/consumer.c
@@ -1,7 +1,7 @@
 
 #include <Gui2/Gui.h>
 
-int consumer()
+int consumer(void)
 {
   foo();
 
diff --git a/Tests/RunCMake/Framework/foo.c b/Tests/RunCMake/Framework/foo.c
index b85b60d..6c1d727 100644
--- a/Tests/RunCMake/Framework/foo.c
+++ b/Tests/RunCMake/Framework/foo.c
@@ -1,4 +1,4 @@
-int foo()
+int foo(void)
 {
   return 42;
 }
diff --git a/Tests/RunCMake/Framework/foo.h b/Tests/RunCMake/Framework/foo.h
index 5d5f8f0..5a2ca62 100644
--- a/Tests/RunCMake/Framework/foo.h
+++ b/Tests/RunCMake/Framework/foo.h
@@ -1 +1 @@
-int foo();
+int foo(void);
diff --git a/Tests/RunCMake/Framework/main.c b/Tests/RunCMake/Framework/main.c
index fc09922..74ae236 100644
--- a/Tests/RunCMake/Framework/main.c
+++ b/Tests/RunCMake/Framework/main.c
@@ -1,7 +1,7 @@
 
 #include <Gui/Gui.h>
 
-int main()
+int main(void)
 {
   foo();
 
diff --git a/Tests/RunCMake/Framework/main2.c b/Tests/RunCMake/Framework/main2.c
index 11f4e4d..26908d7 100644
--- a/Tests/RunCMake/Framework/main2.c
+++ b/Tests/RunCMake/Framework/main2.c
@@ -1,7 +1,7 @@
 
 #include <Gui2/Gui.h>
 
-int main()
+int main(void)
 {
   foo();
 
diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt
new file mode 100644
index 0000000..acb13da
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran-stderr.txt
@@ -0,0 +1,11 @@
+CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    [^
+]*
+
+  given toolset
+
+    fortran=bad
+
+  but the value is not "ifx" or "ifort"\.
diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake
new file mode 100644
index 0000000..2fc38e5
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/BadToolsetFortran.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
index 71cc2d4..b86c481 100644
--- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
@@ -43,6 +43,12 @@
     set(RunCMake_GENERATOR_TOOLSET "${VsNormal_Toolset},customFlagTableDir=does_not_exist")
     run_cmake(BadToolsetCustomFlagTableDir)
   endif()
+  set(RunCMake_GENERATOR_TOOLSET "fortran=ifort")
+  run_cmake(TestToolsetFortranIFORT)
+  set(RunCMake_GENERATOR_TOOLSET "fortran=ifx")
+  run_cmake(TestToolsetFortranIFX)
+  set(RunCMake_GENERATOR_TOOLSET "fortran=bad")
+  run_cmake(BadToolsetFortran)
   if("${RunCMake_GENERATOR}" MATCHES "Visual Studio 1[24567]")
     set(RunCMake_GENERATOR_TOOLSET "Test Toolset,host=x64")
     run_cmake(TestToolsetHostArchBoth)
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt
new file mode 100644
index 0000000..9fa8c21
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT-stdout.txt
@@ -0,0 +1 @@
+-- CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='ifort'
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake
new file mode 100644
index 0000000..7f18fbc
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFORT.cmake
@@ -0,0 +1 @@
+message(STATUS "CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}'")
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt
new file mode 100644
index 0000000..56a0c30
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX-stdout.txt
@@ -0,0 +1 @@
+-- CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='ifx'
diff --git a/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake
new file mode 100644
index 0000000..7f18fbc
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/TestToolsetFortranIFX.cmake
@@ -0,0 +1 @@
+message(STATUS "CMAKE_VS_PLATFORM_TOOLSET_FORTRAN='${CMAKE_VS_PLATFORM_TOOLSET_FORTRAN}'")
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
index 3a6572c..84bbe9f 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-POST_BUILD-timeout-build-stdout.txt
@@ -2,6 +2,7 @@
 ( *|[0-9]+>)  Error running test executable.
 ?( *|[0-9]+>)
 ( *|[0-9]+>)    Path: '.*discovery_timeout_test(\.exe)?'
+( *|[0-9]+>)    Working directory: '[^']*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout'
 ( *|[0-9]+>)    Result: Process terminated due to timeout
 ( *|[0-9]+>)    Output:
 ( *|[0-9]+>)     +
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
index 75afe4a..03d73ee 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-PRE_TEST-timeout-test-stderr.txt
@@ -2,6 +2,7 @@
 [ \t]*Error running test executable.
 +
 [ \t]*Path: '.*discovery_timeout_test(\.exe)?'
+[ \t]*Working directory: '[^']*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-timeout'
 [ \t]*Result: Process terminated due to timeout
 [ \t]*Output:
 [ \t]*timeout.
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake
new file mode 100644
index 0000000..d65fae8
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped.cmake
@@ -0,0 +1,13 @@
+enable_language(CXX)
+
+# 'GoogleTest' module is NOT included here by design to validate that including
+# it in a subdirectory will still result in test discovery working correctly if
+# 'gtest_discover_tests()' is invoked from a different scope.
+
+enable_testing()
+
+include(xcode_sign_adhoc.cmake)
+
+add_subdirectory(GoogleTestDiscoveryTestListScoped)
+
+add_gtest_executable(test_list_scoped_test test_list_test.cpp)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt
new file mode 100644
index 0000000..762a537
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryTestListScoped/CMakeLists.txt
@@ -0,0 +1,13 @@
+# This file mimics one containing GoogleTest-related helper functions. It
+# includes the GoogleTest module that (in this test) is NOT included in the
+# top-level CMake file.
+
+include(GoogleTest)
+
+function(add_gtest_executable TARGET SOURCE)
+  add_executable(${TARGET} ${SOURCE})
+  xcode_sign_adhoc(${TARGET})
+  gtest_discover_tests(
+    ${TARGET}
+  )
+endfunction()
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt
new file mode 100644
index 0000000..6313151
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestLauncher-test-stdout.txt
@@ -0,0 +1,17 @@
+1: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?"? "launcherparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests"
+1: Working Directory: [^
+]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build
+1: Test timeout computed to be: [0-9]+
+1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?'
+1: test_launcher: got arg 1 'launcherparam'
+1: test_launcher: got arg 2 '--'
+1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?'
+1: launching: "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/test_launcher(\.exe)?" "emulatorparam" "--" "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests"
+1: test_launcher: got arg 0 '[^']*[/\]Tests[/\]RunCMake[/\]GoogleTest[/\]GoogleTestLauncher-build([/\]Debug)?[/\]test_launcher(\.exe)?'
+1: test_launcher: got arg 1 'emulatorparam'
+1: test_launcher: got arg 2 '--'
+1: test_launcher: got arg 3 '[^']*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?'
+1: launching: "[^"]*/Tests/RunCMake/GoogleTest/GoogleTestLauncher-build(/Debug)?/launcher_test(\.exe)?" "--gtest_filter=launcher_test\.test1" "--gtest_also_run_disabled_tests"
+1: launcher_test\.test1
+1/1 Test #1: launcher_test\.test1 [.]+ +Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake b/Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake
new file mode 100644
index 0000000..5f4e6eb
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestLauncher.cmake
@@ -0,0 +1,29 @@
+enable_language(C)
+include(GoogleTest)
+
+enable_testing()
+
+include(xcode_sign_adhoc.cmake)
+
+add_executable(test_launcher test_launcher.c)
+
+add_executable(launcher_test launcher_test.c)
+xcode_sign_adhoc(launcher_test)
+set(launcher
+  "$<TARGET_FILE:test_launcher>"
+  ""   # Verify that an empty list item will be preserved
+  "launcherparam"
+  "--"
+)
+set_property(TARGET launcher_test PROPERTY TEST_LAUNCHER "${launcher}")
+set(emulator
+  "$<TARGET_FILE:test_launcher>"
+  ""   # Verify that an empty list item will be preserved
+  "emulatorparam"
+  "--"
+)
+set_property(TARGET launcher_test PROPERTY CROSSCOMPILING_EMULATOR "${emulator}")
+
+gtest_discover_tests(
+  launcher_test
+)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index b494cef..6e064a7 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -101,6 +101,43 @@
   )
 endfunction()
 
+function(run_GoogleTestLauncher DISCOVERY_MODE)
+  if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "14.0")
+    return()
+  endif()
+  if(CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64" AND CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "19.36")
+    return()
+  endif()
+
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestLauncher-build)
+  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+
+  run_cmake_with_options(GoogleTestLauncher
+    -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE}
+  )
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+
+  # do not issue any warnings on stderr that would cause the build to fail
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(GoogleTestLauncher-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+  )
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+
+  run_cmake_command(GoogleTestLauncher-test
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -V
+    --no-label-sumary
+  )
+endfunction()
+
 function(run_GoogleTestXML DISCOVERY_MODE)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTestXML-build)
@@ -292,9 +329,36 @@
   )
 endfunction()
 
+function(run_GoogleTest_discovery_test_list_scoped DISCOVERY_MODE)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-test-list-scoped-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif()
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake_with_options(GoogleTestDiscoveryTestListScoped -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE})
+
+  run_cmake_command(GoogleTest-discovery-test-list-scoped-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Debug
+    --target test_list_scoped_test
+  )
+
+  run_cmake_command(GoogleTest-discovery-test-list-scoped-test
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    --no-label-summary
+  )
+endfunction()
+
 foreach(DISCOVERY_MODE POST_BUILD PRE_TEST)
   message("Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...")
   run_GoogleTest(${DISCOVERY_MODE})
+  run_GoogleTestLauncher(${DISCOVERY_MODE})
   run_GoogleTestXML(${DISCOVERY_MODE})
   message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...")
   run_GoogleTest_discovery_timeout(${DISCOVERY_MODE})
@@ -303,6 +367,7 @@
     run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE})
   endif()
   run_GoogleTest_discovery_test_list(${DISCOVERY_MODE})
+  run_GoogleTest_discovery_test_list_scoped(${DISCOVERY_MODE})
   run_GoogleTest_discovery_flush_script(${DISCOVERY_MODE})
 endforeach()
 
diff --git a/Tests/RunCMake/GoogleTest/launcher_test.c b/Tests/RunCMake/GoogleTest/launcher_test.c
new file mode 100644
index 0000000..ce91565
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/launcher_test.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <string.h>
+
+/* Having this as comment lets gtest_add_tests recognizes the test we fake
+   here without requiring googletest
+TEST_F( launcher_test, test1 )
+{
+}
+*/
+
+int main(int argc, char** argv)
+{
+  /* Note: GoogleTest.cmake doesn't actually depend on Google Test as such;
+   * it only requires that we produces output in the expected format when
+   * invoked with --gtest_list_tests. Thus, we fake that here. This allows us
+   * to test the module without actually needing Google Test.  */
+  if (argc > 1 && strcmp(argv[1], "--gtest_list_tests") == 0) {
+    printf("launcher_test.\n");
+    printf("  test1\n");
+  }
+
+  printf("launcher_test.test1\n");
+  return 0;
+}
diff --git a/Tests/RunCMake/GoogleTest/test_launcher.c b/Tests/RunCMake/GoogleTest/test_launcher.c
new file mode 100644
index 0000000..be5e00d
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/test_launcher.c
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#  include <stdarg.h>
+static int snprintf(char* buffer, size_t count, const char* format, ...)
+{
+  int n;
+  va_list argptr;
+  va_start(argptr, format);
+  n = _vscprintf(format, argptr);
+  vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
+  va_end(argptr);
+  return n;
+}
+#endif
+
+static int launch(int argc, const char* argv[])
+{
+  char cmd[4096];
+  size_t len = 0;
+  const char* sep = "";
+  int i;
+  int n;
+#ifdef _WIN32
+  n = snprintf(cmd + len, sizeof(cmd) - len, "cmd /C \"");
+  if (n < 0) {
+    return 1;
+  }
+  len += n;
+#endif
+  for (i = 0; i < argc; ++i) {
+    n = snprintf(cmd + len, sizeof(cmd) - len, "%s\"%s\"", sep, argv[i]);
+    if (n < 0) {
+      return 1;
+    }
+    len += n;
+    if (len >= sizeof(cmd)) {
+      fprintf(stderr, "error: command too long\n");
+      return 1;
+    }
+    sep = " ";
+  }
+#ifdef _WIN32
+  printf("launching: %s\n", cmd + 8);
+  n = snprintf(cmd + len, sizeof(cmd) - len, "\"");
+  if (n < 1) {
+    return 1;
+  }
+#else
+  printf("launching: %s\n", cmd);
+#endif
+  fflush(stdout);
+  return system(cmd);
+}
+
+int main(int argc, const char* argv[])
+{
+  int ownArgs = 1;
+  int i;
+  for (i = 0; i < argc; ++i) {
+    printf("test_launcher: got arg %d '%s'\n", i, argv[i]);
+    if (ownArgs && strcmp(argv[i], "--") == 0) {
+      ownArgs = 0;
+    } else if (!ownArgs) {
+      return launch(argc - i, argv + i);
+    }
+  }
+  return 1;
+}
diff --git a/Tests/RunCMake/Graphviz/test_project/core_library.c b/Tests/RunCMake/Graphviz/test_project/core_library.c
index e8a8844..ce390ce 100644
--- a/Tests/RunCMake/Graphviz/test_project/core_library.c
+++ b/Tests/RunCMake/Graphviz/test_project/core_library.c
@@ -1,3 +1,3 @@
-void log_something()
+void log_something(void)
 {
 }
diff --git a/Tests/RunCMake/Graphviz/test_project/graphic_library.c b/Tests/RunCMake/Graphviz/test_project/graphic_library.c
index 958c8ab..7a31ad4 100644
--- a/Tests/RunCMake/Graphviz/test_project/graphic_library.c
+++ b/Tests/RunCMake/Graphviz/test_project/graphic_library.c
@@ -1,3 +1,3 @@
-void initialize_graphics()
+void initialize_graphics(void)
 {
 }
diff --git a/Tests/RunCMake/Graphviz/test_project/module.c b/Tests/RunCMake/Graphviz/test_project/module.c
index a508b09..56e833d 100644
--- a/Tests/RunCMake/Graphviz/test_project/module.c
+++ b/Tests/RunCMake/Graphviz/test_project/module.c
@@ -1,3 +1,3 @@
-static void some_function()
+static void some_function(void)
 {
 }
diff --git a/Tests/RunCMake/Graphviz/test_project/system_library.c b/Tests/RunCMake/Graphviz/test_project/system_library.c
index 5d67079..896bca4 100644
--- a/Tests/RunCMake/Graphviz/test_project/system_library.c
+++ b/Tests/RunCMake/Graphviz/test_project/system_library.c
@@ -1,3 +1,3 @@
-void initialize_system()
+void initialize_system(void)
 {
 }
diff --git a/Tests/RunCMake/IAR/CMakeLists.txt b/Tests/RunCMake/IAR/CMakeLists.txt
new file mode 100644
index 0000000..6a9ce76
--- /dev/null
+++ b/Tests/RunCMake/IAR/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/IAR/RunCMakeTest.cmake b/Tests/RunCMake/IAR/RunCMakeTest.cmake
new file mode 100644
index 0000000..2fefa6a
--- /dev/null
+++ b/Tests/RunCMake/IAR/RunCMakeTest.cmake
@@ -0,0 +1,73 @@
+include(RunCMake)
+
+if(RunCMake_GENERATOR MATCHES "Makefile|Ninja")
+  file(GLOB _iar_toolchains
+    "${CMake_TEST_IAR_TOOLCHAINS}/bx*-*/*/bin/icc*" )
+  if(_iar_toolchains STREQUAL "")
+    message(FATAL_ERROR "Could not find any IAR toolchains at: ${CMake_TEST_IAR_TOOLCHAINS}.")
+  endif()
+endif()
+
+function(run_toolchain case)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+  run_cmake_with_options(${case} ${ARGN})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .)
+endfunction()
+
+foreach(_iar_toolchain IN LISTS _iar_toolchains)
+  message(STATUS "Found IAR toolchain: ${_iar_toolchain}")
+  cmake_path(GET _iar_toolchain PARENT_PATH BIN_DIR)
+  cmake_path(GET BIN_DIR PARENT_PATH TOOLKIT_DIR)
+  cmake_path(GET TOOLKIT_DIR FILENAME ARCH)
+
+  # Sets the minimal requirements for linking each target architecture
+  if(ARCH STREQUAL "avr")
+    string(CONCAT LINK_OPTS
+      "-I${TOOLKIT_DIR}/lib "
+      "-f ${TOOLKIT_DIR}/src/template/lnk3s.xcl "
+    )
+  elseif(ARCH STREQUAL "rl78")
+    string(CONCAT LINK_OPTS
+      "--config_def _STACK_SIZE=256 "
+      "--config_def _NEAR_HEAP_SIZE=0x400 "
+      "--config_def _FAR_HEAP_SIZE=4096 "
+      "--config_def _HUGE_HEAP_SIZE=0 "
+      "--config_def _NEAR_CONST_LOCATION_START=0x2000 "
+      "--config_def _NEAR_CONST_LOCATION_SIZE=0x6F00 "
+      "--define_symbol _NEAR_CONST_LOCATION=0 "
+      "--config ${TOOLKIT_DIR}/config/lnkrl78_s3.icf "
+    )
+  else()
+    set(LINK_OPTS "")
+  endif()
+
+  # Set IAR Assembler (ILINK || XLINK)
+  find_program(IAR_ASSEMBLER
+    NAMES iasm${ARCH} a${ARCH}
+    PATHS ${BIN_DIR}
+    REQUIRED )
+
+  run_toolchain(iar-c
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_C_COMPILER=${_iar_toolchain}
+    -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS}
+  )
+
+  run_toolchain(iar-cxx
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_CXX_COMPILER=${_iar_toolchain}
+    -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS}
+  )
+
+  run_toolchain(iar-asm
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_ASM_COMPILER=${IAR_ASSEMBLER}
+  )
+
+  run_toolchain(iar-lib
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_C_COMPILER=${_iar_toolchain}
+    -DCMAKE_EXE_LINKER_FLAGS=${LINK_OPTS}
+  )
+endforeach()
diff --git a/Tests/RunCMake/IAR/iar-asm.cmake b/Tests/RunCMake/IAR/iar-asm.cmake
new file mode 100644
index 0000000..1c19182
--- /dev/null
+++ b/Tests/RunCMake/IAR/iar-asm.cmake
@@ -0,0 +1,4 @@
+enable_language(ASM)
+
+add_executable(exec-asm module.asm)
+target_link_options(exec-asm PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-c.cmake b/Tests/RunCMake/IAR/iar-c.cmake
new file mode 100644
index 0000000..5e7e740
--- /dev/null
+++ b/Tests/RunCMake/IAR/iar-c.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+add_executable(exec-c module.c)
+target_compile_options(exec-c PRIVATE -e)
+target_link_options(exec-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-cxx.cmake b/Tests/RunCMake/IAR/iar-cxx.cmake
new file mode 100644
index 0000000..6056d32
--- /dev/null
+++ b/Tests/RunCMake/IAR/iar-cxx.cmake
@@ -0,0 +1,5 @@
+enable_language(CXX)
+
+add_executable(exec-cxx module.cxx)
+target_compile_options(exec-cxx PRIVATE -e)
+target_link_options(exec-cxx PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/iar-lib.cmake b/Tests/RunCMake/IAR/iar-lib.cmake
new file mode 100644
index 0000000..17e6c95
--- /dev/null
+++ b/Tests/RunCMake/IAR/iar-lib.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+add_library(iar-test-lib libmod.c)
+
+add_executable(exec-lib-c module.c)
+target_compile_options(exec-lib-c PRIVATE -e)
+target_compile_definitions(exec-lib-c PRIVATE __USE_LIBFUN)
+target_link_libraries(exec-lib-c PRIVATE iar-test-lib)
+target_link_options(exec-lib-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/IAR/libmod.c b/Tests/RunCMake/IAR/libmod.c
new file mode 100644
index 0000000..d6c3b73
--- /dev/null
+++ b/Tests/RunCMake/IAR/libmod.c
@@ -0,0 +1,4 @@
+int iar_libfun()
+{
+  return 42;
+}
diff --git a/Tests/RunCMake/IAR/module.asm b/Tests/RunCMake/IAR/module.asm
new file mode 100644
index 0000000..1e08236
--- /dev/null
+++ b/Tests/RunCMake/IAR/module.asm
@@ -0,0 +1,41 @@
+#if defined(__IASM8051__) || defined(__IASM430__)
+  NAME main
+#else
+  MODULE main
+#endif
+
+  PUBLIC main
+  PUBLIC __iar_program_start
+  PUBLIC __program_start
+
+#if defined(__IASMSTM8__)
+  EXTERN CSTACK$$Limit
+  SECTION `.near_func.text`:CODE:NOROOT(0)
+#elif defined(__IASMAVR__)
+  ORG  $0
+  RJMP main
+  RSEG CODE
+#elif defined(__IASM8051__)
+  ORG  0FFFEh
+  DC16 main
+  RSEG RCODE
+?cmain:
+#elif defined(__IASM430__)
+  ORG  0FFFEh
+  DC16 init
+  RSEG CSTACK
+  RSEG CODE
+init:
+  MOV #SFE(CSTACK), SP
+#else
+  EXTERN __iar_static_base$$GPREL
+  SECTION CSTACK:DATA:NOROOT(4)
+  SECTION `.cstartup`:CODE(2)
+  CODE
+#endif
+
+__program_start:
+__iar_program_start:
+main:
+  NOP
+  END
diff --git a/Tests/RunCMake/IAR/module.c b/Tests/RunCMake/IAR/module.c
new file mode 100644
index 0000000..2f72a42
--- /dev/null
+++ b/Tests/RunCMake/IAR/module.c
@@ -0,0 +1,14 @@
+#include "module.h"
+#if defined(__USE_LIBFUN)
+extern int iar_libfun();
+#endif
+__root int i;
+__root int main()
+{
+#if defined(__USE_LIBFUN)
+  i = iar_libfun();
+#else
+  i = INTERNAL;
+#endif
+  return i;
+}
diff --git a/Tests/RunCMake/IAR/module.cxx b/Tests/RunCMake/IAR/module.cxx
new file mode 100644
index 0000000..5604435
--- /dev/null
+++ b/Tests/RunCMake/IAR/module.cxx
@@ -0,0 +1,7 @@
+#include "module.h"
+__root int i;
+__root int main()
+{
+  i = INTERNAL;
+  return i;
+}
diff --git a/Tests/RunCMake/IAR/module.h b/Tests/RunCMake/IAR/module.h
new file mode 100644
index 0000000..5f9c87b
--- /dev/null
+++ b/Tests/RunCMake/IAR/module.h
@@ -0,0 +1,14 @@
+#ifndef __MODULE_H__
+#define __MODULE_H__
+
+#if defined(__cplusplus)
+#  define INTERNAL 64
+#elif !defined(__cplusplus) && defined(__IAR_SYSTEMS_ICC__)
+#  define INTERNAL 32
+#elif defined(__IAR_SYSTEMS_ASM__)
+#  define INTERNAL 16
+#else
+#  error "Unable to determine INTERNAL symbol."
+#endif /* __IAR_SYSTEMS_ICC */
+
+#endif /* __MODULE_H__ */
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt b/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt
new file mode 100644
index 0000000..5773ae3
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28...3.29)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt
new file mode 100644
index 0000000..2566d8f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at InvalidConfiguration1.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    ORDER=
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake
new file mode 100644
index 0000000..e79eb45
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration1.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER= UNICITY=ALL)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt
new file mode 100644
index 0000000..933031d
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at InvalidConfiguration2.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    ORDER
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake
new file mode 100644
index 0000000..c9da734
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration2.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING UNICITY=ALL ORDER)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt
new file mode 100644
index 0000000..eba1eb2
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at InvalidConfiguration3.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    WRONG=REVERSE
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake
new file mode 100644
index 0000000..a1311fe
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration3.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=ALL)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt
new file mode 100644
index 0000000..46d5513
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at InvalidConfiguration4.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    WRONG=REVERSE
+    UNICITY=WRONG
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake
new file mode 100644
index 0000000..9d48f4f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/InvalidConfiguration4.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING WRONG=REVERSE UNICITY=WRONG)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt
new file mode 100644
index 0000000..33a7552
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at Invalid_ORDER.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    ORDER=WRONG
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake
new file mode 100644
index 0000000..72a7e02
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_ORDER.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=WRONG UNICITY=ALL)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt
new file mode 100644
index 0000000..4d759b7
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY-stderr.txt
@@ -0,0 +1,7 @@
+CMake Error at Invalid_UNICITY.cmake:[0-9]+ \(add_executable\):
+  Erroneous option\(s\) for 'CMAKE_C_LINK_LIBRARIES_PROCESSING':
+
+    UNICITY=WRONG
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake
new file mode 100644
index 0000000..f423eef
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/Invalid_UNICITY.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+set(CMAKE_C_LINK_LIBRARIES_PROCESSING ORDER=REVERSE UNICITY=WRONG)
+
+add_library(lib STATIC lib.c)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE lib)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake
new file mode 100644
index 0000000..763f48b
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/RunCMakeTest.cmake
@@ -0,0 +1,8 @@
+include(RunCMake)
+
+run_cmake(Invalid_ORDER)
+run_cmake(Invalid_UNICITY)
+run_cmake(InvalidConfiguration1)
+run_cmake(InvalidConfiguration2)
+run_cmake(InvalidConfiguration3)
+run_cmake(InvalidConfiguration4)
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/lib.c b/Tests/RunCMake/LinkLibrariesProcessing/lib.c
new file mode 100644
index 0000000..17a4148
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/lib.c
@@ -0,0 +1,4 @@
+
+void lib(void)
+{
+}
diff --git a/Tests/RunCMake/LinkLibrariesProcessing/main.c b/Tests/RunCMake/LinkLibrariesProcessing/main.c
new file mode 100644
index 0000000..402eac3
--- /dev/null
+++ b/Tests/RunCMake/LinkLibrariesProcessing/main.c
@@ -0,0 +1,5 @@
+
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/LinkerSelection/AppleClassic.cmake b/Tests/RunCMake/LinkerSelection/AppleClassic.cmake
new file mode 100644
index 0000000..62a12ad
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/AppleClassic.cmake
@@ -0,0 +1,7 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE APPLE_CLASSIC)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE m m)
diff --git a/Tests/RunCMake/LinkerSelection/CMakeLists.txt b/Tests/RunCMake/LinkerSelection/CMakeLists.txt
new file mode 100644
index 0000000..6a9ce76
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake b/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake
new file mode 100644
index 0000000..235c38e
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/CustomLinkerType-build-check.cmake
@@ -0,0 +1,2 @@
+
+include("${CMAKE_CURRENT_LIST_DIR}/LinkerType-validation.cmake")
diff --git a/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake b/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake
new file mode 100644
index 0000000..4bf98b0
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/CustomLinkerType.cmake
@@ -0,0 +1,36 @@
+
+enable_language(C)
+
+set(CMAKE_C_USING_LINKER_FOO_C "${CMAKE_C_USING_LINKER_LLD}")
+
+add_executable(main main.c)
+set_property(TARGET main PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>")
+
+if(CMake_TEST_CUDA)
+  enable_language(CUDA)
+
+  set(CMAKE_CUDA_USING_LINKER_FOO_CUDA "${CMAKE_CUDA_USING_LINKER_LLD}")
+
+  add_executable(mainCU main.cu)
+  set_property(TARGET mainCU PROPERTY LINKER_TYPE "$<$<LINK_LANGUAGE:C>:FOO_C>$<$<LINK_LANGUAGE:CUDA>:FOO_CUDA>")
+endif()
+
+#
+# Generate file for validation
+#
+if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL")
+  cmake_path(GET CMAKE_C_USING_LINKER_FOO_C FILENAME LINKER_TYPE_OPTION)
+else()
+  set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_FOO_C}")
+endif()
+if(CMake_TEST_CUDA)
+  if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL")
+    cmake_path(GET CMAKE_CUDA_USING_LINKER_FOO_CUDA FILENAME CUDA_LINKER)
+  else()
+    set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_FOO_CUDA}")
+  endif()
+  string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}")
+endif()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake"
+  "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n")
diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt
new file mode 100644
index 0000000..11aea7a
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType-stderr.txt
@@ -0,0 +1,3 @@
+CMake Error in CMakeLists.txt:
+  LINKER_TYPE 'FOO' is unknown.  Did you forgot to define
+  'CMAKE_C_USING_LINKER_FOO' variable\?
diff --git a/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake b/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake
new file mode 100644
index 0000000..bbe398c
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/InvalidLinkerType.cmake
@@ -0,0 +1,5 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE FOO)
+add_executable(main main.c)
diff --git a/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake b/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake
new file mode 100644
index 0000000..3f82479
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/LinkerType-validation.cmake
@@ -0,0 +1,9 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/LINKER_TYPE_OPTION.cmake")
+
+# In some environment, `=` character is escaped
+string(REPLACE "=" "\\\\?=" LINKER_TYPE_OPTION "${LINKER_TYPE_OPTION}")
+
+if (NOT actual_stdout MATCHES "${LINKER_TYPE_OPTION}")
+    set (RunCMake_TEST_FAILED "Not found expected '${LINKER_TYPE_OPTION}'.")
+endif()
diff --git a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
new file mode 100644
index 0000000..cae4ca4
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
@@ -0,0 +1,44 @@
+include(RunCMake)
+
+if (RunCMake_GENERATOR MATCHES "Visual Studio 9 2008")
+  run_cmake(UnsupportedLinkerType)
+  return()
+endif()
+
+run_cmake(InvalidLinkerType)
+
+# look-up for LLVM linker
+if (WIN32)
+  set (LINKER_NAMES lld-link)
+else()
+  set(LINKER_NAMES ld.lld ld64.lld)
+endif()
+find_program(LLD_LINKER NAMES ${LINKER_NAMES})
+
+macro(run_cmake_and_build test)
+  run_cmake_with_options(${test} -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(CMake_TEST_CUDA)
+    string(APPEND "|${CMAKE_CUDA_USING_LINKER_LLD}")
+  endif()
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release --verbose ${ARGN})
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+endmacro()
+
+if(LLD_LINKER)
+  block(SCOPE_FOR VARIABLES)
+    set(CMAKE_VERBOSE_MAKEFILE TRUE)
+    set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+    set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
+
+    run_cmake_and_build(ValidLinkerType)
+    run_cmake_and_build(CustomLinkerType)
+  endblock()
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "15.0")
+  run_cmake_and_build(AppleClassic)
+endif()
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt
new file mode 100644
index 0000000..6473451
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt
@@ -0,0 +1,3 @@
+CMake Error at UnsupportedLinkerType.cmake:[0-9]+ \(add_executable\):
+  'LINKER_TYPE' property, specified on target 'main', is not supported by
+  this generator.
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake
new file mode 100644
index 0000000..1b0703c
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake
@@ -0,0 +1,5 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE LDD)
+add_executable(main main.c)
diff --git a/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake b/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake
new file mode 100644
index 0000000..235c38e
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/ValidLinkerType-build-check.cmake
@@ -0,0 +1,2 @@
+
+include("${CMAKE_CURRENT_LIST_DIR}/LinkerType-validation.cmake")
diff --git a/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake b/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake
new file mode 100644
index 0000000..a685ac1
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/ValidLinkerType.cmake
@@ -0,0 +1,32 @@
+
+enable_language(C)
+
+set(CMAKE_LINKER_TYPE LLD)
+
+add_executable(main main.c)
+
+if(CMake_TEST_CUDA)
+  enable_language(CUDA)
+
+  add_executable(mainCU main.cu)
+endif()
+
+#
+# Generate file for validation
+#
+if (CMAKE_C_USING_LINKER_MODE STREQUAL "TOOL")
+  cmake_path(GET CMAKE_C_USING_LINKER_LLD FILENAME LINKER_TYPE_OPTION)
+else()
+  set(LINKER_TYPE_OPTION "${CMAKE_C_USING_LINKER_LLD}")
+endif()
+if(CMake_TEST_CUDA)
+  if (CMAKE_CUDA_USING_LINKER_MODE STREQUAL "TOOL")
+    cmake_path(GET CMAKE_CUDA_USING_LINKER_LLD FILENAME CUDA_LINKER)
+  else()
+    set(CUDA_LINKER "${CMAKE_CUDA_USING_LINKER_LLD}")
+  endif()
+  string(APPEND LINKER_TYPE_OPTION "|${CUDA_LINKER}")
+endif()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LINKER_TYPE_OPTION.cmake"
+  "set(LINKER_TYPE_OPTION \"${LINKER_TYPE_OPTION}\")\n")
diff --git a/Tests/RunCMake/LinkerSelection/main.c b/Tests/RunCMake/LinkerSelection/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/LinkerSelection/main.cu b/Tests/RunCMake/LinkerSelection/main.cu
new file mode 100644
index 0000000..766b775
--- /dev/null
+++ b/Tests/RunCMake/LinkerSelection/main.cu
@@ -0,0 +1,5 @@
+
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt
new file mode 100644
index 0000000..579c722
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer-NoPipe-j2-stdout.txt
@@ -0,0 +1,9 @@
+Test project [^
+]*/Tests/RunCMake/Make/CTestJobServer-build
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+1/6 Test #[0-9]+: test[0-9]+ ............................   Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt
new file mode 100644
index 0000000..eafba1c
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stderr.txt
@@ -0,0 +1 @@
+No tests were found!!!
diff --git a/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt
new file mode 100644
index 0000000..0547dc7
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer-NoTests-j2-stdout.txt
@@ -0,0 +1,3 @@
+Test project [^
+]*/Tests/RunCMake/Make/CTestJobServer-build
+Connected to MAKE jobserver
diff --git a/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt
new file mode 100644
index 0000000..a700999
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer-Tests-j2-stdout.txt
@@ -0,0 +1,6 @@
+Test project [^
+]*/Tests/RunCMake/Make/CTestJobServer-build
+Connected to MAKE jobserver
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+1/6 Test #[0-9]+: test[0-9]+ ............................   Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt b/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt
new file mode 100644
index 0000000..5a76bdc
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer-Tests-j3-stdout.txt
@@ -0,0 +1,7 @@
+Test project [^
+]*/Tests/RunCMake/Make/CTestJobServer-build
+Connected to MAKE jobserver
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+    Start [0-9]+: test[0-9]+
+1/6 Test #[0-9]+: test[0-9]+ ............................   Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/Make/CTestJobServer.cmake b/Tests/RunCMake/Make/CTestJobServer.cmake
new file mode 100644
index 0000000..2ca3d54
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer.cmake
@@ -0,0 +1,4 @@
+enable_testing()
+foreach(i RANGE 1 6)
+  add_test(NAME test${i} COMMAND ${CMAKE_COMMAND} -E true)
+endforeach()
diff --git a/Tests/RunCMake/Make/CTestJobServer.make b/Tests/RunCMake/Make/CTestJobServer.make
new file mode 100644
index 0000000..7fc5e28
--- /dev/null
+++ b/Tests/RunCMake/Make/CTestJobServer.make
@@ -0,0 +1,11 @@
+NoPipe:
+	env MAKEFLAGS= $(CMAKE_CTEST_COMMAND) -j6
+.PHONY: NoPipe
+
+NoTests:
+	+$(CMAKE_CTEST_COMMAND) -j6 -R NoTests
+.PHONY: NoTests
+
+Tests:
+	+$(CMAKE_CTEST_COMMAND) -j6
+.PHONY: Tests
diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake
index 5d1ba48..cfaf759 100644
--- a/Tests/RunCMake/Make/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Make/RunCMakeTest.cmake
@@ -79,9 +79,29 @@
   run_cmake_command(DetectJobServer-present-parallel-build ${CMAKE_COMMAND} --build . -j4)
 endfunction()
 
+function(run_make_rule case rule job_count)
+  run_cmake_command(${case}-${rule}-j${job_count}
+    ${RunCMake_MAKE_PROGRAM} -f "${RunCMake_SOURCE_DIR}/${case}.make" ${rule} -j${job_count}
+    CMAKE_COMMAND="${CMAKE_COMMAND}" CMAKE_CTEST_COMMAND="${CMAKE_CTEST_COMMAND}"
+    )
+endfunction()
+
+function(run_CTestJobServer)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CTestJobServer-build)
+  run_cmake(CTestJobServer)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_make_rule(CTestJobServer NoPipe 2)
+  run_make_rule(CTestJobServer NoTests 2)
+  run_make_rule(CTestJobServer Tests 2)
+  run_make_rule(CTestJobServer Tests 3)
+endfunction()
+
 # Jobservers are currently only supported by GNU makes, except MSYS2 make
 if(MAKE_IS_GNU AND NOT RunCMake_GENERATOR MATCHES "MSYS Makefiles")
   detect_jobserver_present()
+  if(UNIX)
+    run_CTestJobServer()
+  endif()
 endif()
 
 if(MAKE_IS_GNU)
diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake
index 5a03fcb..09fd7e9 100644
--- a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake
+++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake
@@ -1,6 +1,6 @@
 # Check that the prefix sub-directory is not repeated.
 
-if(EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}")
+if(NINJA_OUTPUT_PATH_PREFIX AND EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}")
   message(FATAL_ERROR
     "no sub directory named after the CMAKE_NINJA_OUTPUT_PATH_PREFIX "
     "should be in the binary directory."
diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake
index edde0c0..3016816 100644
--- a/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake
+++ b/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake
@@ -10,3 +10,38 @@
   string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
   list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
 endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps hello.copy.c)
+if (ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of hello.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps hello.copy2.c)
+if (ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of hello.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake
new file mode 100644
index 0000000..b2a553b
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct-check.cmake
@@ -0,0 +1,47 @@
+set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build/build.ninja")
+file(READ "${log}" build_file)
+
+set(RunCMake_TEST_FAILED)
+if(NOT "${build_file}" MATCHES "depfile = test\\.d")
+  string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_d}")
+endif()
+if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d")
+  string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
+endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps hello.copy.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps hello.copy2.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake
new file mode 100644
index 0000000..cf3b35d
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsByproduct.cmake
@@ -0,0 +1,24 @@
+add_custom_command(
+  OUTPUT hello.copy.c
+  BYPRODUCTS "test.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )
+
+add_custom_command(
+  OUTPUT hello.copy2.c
+  BYPRODUCTS "test_$<CONFIG>.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy2.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test_$<CONFIG>.d"
+  )
+
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c"
+  "${CMAKE_CURRENT_BINARY_DIR}/hello.copy2.c")
+
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake
new file mode 100644
index 0000000..4d738c8
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput-check.cmake
@@ -0,0 +1,47 @@
+set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build/build.ninja")
+file(READ "${log}" build_file)
+
+set(RunCMake_TEST_FAILED)
+if(NOT "${build_file}" MATCHES "depfile = test\\.d")
+  string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_d}")
+endif()
+if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d")
+  string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
+endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps hello.copy.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps hello.copy2.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of hello.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake
new file mode 100644
index 0000000..07a12b7
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandDepfileAsOutput.cmake
@@ -0,0 +1,24 @@
+add_custom_command(
+  OUTPUT hello.copy.c
+         "test.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )
+
+add_custom_command(
+  OUTPUT hello.copy2.c
+         "test_$<CONFIG>.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy2.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test_$<CONFIG>.d"
+  )
+
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c"
+  "${CMAKE_CURRENT_BINARY_DIR}/hello.copy2.c")
+
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 2df300c..8b24c16 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -101,6 +101,8 @@
 run_CMP0058(NEW-by)
 
 run_cmake_with_options(CustomCommandDepfile -DCMAKE_BUILD_TYPE=Debug)
+run_cmake_with_options(CustomCommandDepfileAsOutput -DCMAKE_BUILD_TYPE=Debug)
+run_cmake_with_options(CustomCommandDepfileAsByproduct -DCMAKE_BUILD_TYPE=Debug)
 run_cmake(CustomCommandJobPool)
 run_cmake(JobPoolUsesTerminal)
 
diff --git a/Tests/RunCMake/Ninja/dep.c b/Tests/RunCMake/Ninja/dep.c
index 728f031..a576b6e 100644
--- a/Tests/RunCMake/Ninja/dep.c
+++ b/Tests/RunCMake/Ninja/dep.c
@@ -1,4 +1,4 @@
-int dep()
+int dep(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/Ninja/top.c b/Tests/RunCMake/Ninja/top.c
index 4a88eb2..0358244 100644
--- a/Tests/RunCMake/Ninja/top.c
+++ b/Tests/RunCMake/Ninja/top.c
@@ -1,7 +1,7 @@
 #include "command.h"
 #include "target.h"
 
-int top()
+int top(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/NinjaMultiConfig/Common.cmake b/Tests/RunCMake/NinjaMultiConfig/Common.cmake
index 6c0d82a..9343eaf 100644
--- a/Tests/RunCMake/NinjaMultiConfig/Common.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/Common.cmake
@@ -37,7 +37,7 @@
       set(exe_file " [==[$<TARGET_FILE_DIR:${tgt}>/$<TARGET_FILE_PREFIX:${tgt}>$<TARGET_FILE_BASE_NAME:${tgt}>$<TARGET_FILE_SUFFIX:${tgt}>]==]")
       set(exe_filename " [==[$<TARGET_FILE_PREFIX:${tgt}>$<TARGET_FILE_BASE_NAME:${tgt}>$<TARGET_FILE_SUFFIX:${tgt}>]==]")
 
-      if(WIN32)
+      if(WIN32 AND NOT generate_output_files_NO_EXE_LIB)
         set(exe_lib_file " [==[$<TARGET_FILE_DIR:${tgt}>/$<TARGET_FILE_PREFIX:${tgt}>$<TARGET_FILE_BASE_NAME:${tgt}>.lib]==]")
         string(APPEND content "set(TARGET_EXE_LIB_FILE_${tgt}_$<CONFIG>${exe_lib_file})\n")
       endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CudaSimple.cmake b/Tests/RunCMake/NinjaMultiConfig/CudaSimple.cmake
index 00d8a1b..b5fc5b6 100644
--- a/Tests/RunCMake/NinjaMultiConfig/CudaSimple.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/CudaSimple.cmake
@@ -16,6 +16,9 @@
 add_executable(simplecudaexe main.cu )
 target_link_libraries(simplecudaexe PRIVATE simplecudashared)
 
+if(WIN32 AND CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
+  set(generate_output_files_NO_EXE_LIB 1)
+endif()
 include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake)
 generate_output_files(simplecudaexe simplecudashared simplecudaobj)
 
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake
index 3674aba..673391c 100644
--- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfile-check.cmake
@@ -10,3 +10,38 @@
   string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
   list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
 endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps main.copy.c)
+if (ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of main.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfile-build" -t deps main.copy2.c)
+if (ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja did not track the deps of main.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake
new file mode 100644
index 0000000..ced40ab
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct-check.cmake
@@ -0,0 +1,47 @@
+set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build/CMakeFiles/impl-Debug.ninja")
+file(READ "${log}" build_file)
+
+set(RunCMake_TEST_FAILED)
+if(NOT "${build_file}" MATCHES "depfile = test\\.d")
+  string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_d}")
+endif()
+if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d")
+  string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
+endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps main.copy.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsByproduct-build" -t deps main.copy2.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake
new file mode 100644
index 0000000..e1e49cb
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsByproduct.cmake
@@ -0,0 +1,22 @@
+add_custom_command(
+  OUTPUT main.copy.c
+  BYPRODUCTS "test.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
+          main.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )
+
+add_custom_command(
+  OUTPUT main.copy2.c
+  BYPRODUCTS "test_$<CONFIG>.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
+          main.copy2.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test_$<CONFIG>.d"
+  )
+
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/main.copy.c"
+                                   "${CMAKE_CURRENT_BINARY_DIR}/main.copy2.c")
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake
new file mode 100644
index 0000000..d4a87ba
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput-check.cmake
@@ -0,0 +1,47 @@
+set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build/CMakeFiles/impl-Debug.ninja")
+file(READ "${log}" build_file)
+
+set(RunCMake_TEST_FAILED)
+if(NOT "${build_file}" MATCHES "depfile = test\\.d")
+  string(CONCAT no_test_d "Log file:\n ${log}\n" "does not have expected line: depfile = test.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_d}")
+endif()
+if(NOT "${build_file}" MATCHES "depfile = test_Debug\\.d")
+  string(CONCAT no_test_Debug_d "\nLog file:\n ${log}\n" "does not have expected line: depfile = test_Debug.d")
+  list(APPEND RunCMake_TEST_FAILED "${no_test_Debug_d}")
+endif()
+
+function(_run_ninja dir)
+  execute_process(
+    COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+    WORKING_DIRECTORY "${dir}"
+    OUTPUT_VARIABLE ninja_stdout
+    ERROR_VARIABLE ninja_stderr
+    RESULT_VARIABLE ninja_result
+    )
+  if(NOT ninja_result EQUAL 0)
+    message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+    message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+    message(FATAL_ERROR
+      "top ninja build failed exited with status ${ninja_result}")
+  endif()
+  set(ninja_stdout "${ninja_stdout}" PARENT_SCOPE)
+endfunction()
+
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build")
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps main.copy.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy.c in the database")
+endif ()
+_run_ninja("${RunCMake_BINARY_DIR}/CustomCommandDepfileAsOutput-build" -t deps main.copy2.c)
+if (NOT ninja_stdout MATCHES "deps not found")
+  list(APPEND RunCMake_TEST_FAILED "Ninja tracked the deps of main.copy2.c in the database")
+endif ()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake
new file mode 100644
index 0000000..0617970
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandDepfileAsOutput.cmake
@@ -0,0 +1,22 @@
+add_custom_command(
+  OUTPUT main.copy.c
+         "test.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
+          main.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )
+
+add_custom_command(
+  OUTPUT main.copy2.c
+         "test_$<CONFIG>.d"
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/main.c"
+          main.copy2.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test_$<CONFIG>.d"
+  )
+
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/main.copy.c"
+                                   "${CMAKE_CURRENT_BINARY_DIR}/main.copy2.c")
diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
deleted file mode 100644
index 2d8df13..0000000
--- a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-check_files("${RunCMake_TEST_BINARY_DIR}"
-  INCLUDE
-    ${AUTOGEN_FILES}
-
-    ${TARGET_FILE_exe_Debug}
-    ${TARGET_OBJECT_FILES_exe_Debug}
-  )
diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
index 130f883..4d1bf13 100644
--- a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
@@ -19,16 +19,32 @@
   set(moc_writes_depfiles 1)
 endif()
 
-set(autogen_files)
-if(moc_writes_depfiles)
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps")
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp")
-endif()
-foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${c}.cpp")
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp")
-  if(moc_writes_depfiles)
-    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d")
+foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
+  set(config_suffix "_${CONFIG}")
+  if (CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG)
+    set(autogen_files_config_suffix "${config_suffix}")
   endif()
+  set(autogen_files)
+  if(moc_writes_depfiles)
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps${autogen_files_config_suffix}")
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp${autogen_files_config_suffix}")
+  endif()
+  if (CMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG)
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation${config_suffix}.cpp")
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include${config_suffix}/moc_qt5.cpp")
+    if(moc_writes_depfiles)
+      list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include${config_suffix}/moc_qt5.cpp.d")
+    endif()
+  else()
+    foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
+      list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${c}.cpp")
+      list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp")
+      if(moc_writes_depfiles)
+        list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d")
+      endif()
+    endforeach()
+  endif()
+
+  file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES${config_suffix} [==[${autogen_files}]==])\n")
+  unset(autogen_files)
 endforeach()
-file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n")
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index 0ccf8e8..88fd1e8 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -388,6 +388,8 @@
 unset(RunCMake_TEST_BINARY_DIR)
 
 run_cmake(CustomCommandDepfile)
+run_cmake(CustomCommandDepfileAsOutput)
+run_cmake(CustomCommandDepfileAsByproduct)
 
 set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all")
 run_cmake(PerConfigSources)
@@ -486,11 +488,65 @@
     "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
     "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
   )
-  run_cmake_configure(QtX)
-  unset(RunCMake_TEST_OPTIONS)
-  include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake)
-  run_cmake_build(QtX debug-in-release-graph Release exe:Debug)
+
+  foreach(use_better_graph IN ITEMS ON OFF)
+    foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+      foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+        block()
+          set(target_config_suffix "_${target_config}")
+          if (use_better_graph)
+            set(autogen_files_config_suffix "${target_config_suffix}")
+          else()
+            set(autogen_files_config_suffix "")
+          endif()
+          set(prefix "QtX")
+          set(case "${target_config}-in-${ninja_config}-better-graph-${use_better_graph}")
+          set(test_path "${prefix}-${case}")
+          set(RunCMake_TEST_VARIANT_DESCRIPTION "-${case}-configure")
+          set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test_path}-build)
+          run_cmake_with_options(QtX ${RunCMake_TEST_OPTIONS}
+            "-DCMAKE_AUTOGEN_BETTER_GRAPH_MULTI_CONFIG=${use_better_graph}")
+          unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+          include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake)
+          run_cmake_build(${test_path} "" ${ninja_config} exe:${target_config})
+          check_files("${RunCMake_TEST_BINARY_DIR}"
+          INCLUDE
+            "${AUTOGEN_FILES${target_config_suffix}}"
+            "${TARGET_FILE_exe${target_config_suffix}}"
+            "${TARGET_OBJECT_FILES_exe${target_config_suffix}}"
+          )
+          if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "")
+            message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}")
+          else()
+            message(STATUS "${test_path}-check-files - PASSED")
+          endif()
+
+          check_file_contents("${RunCMake_TEST_BINARY_DIR}/exe_autogen/deps${autogen_files_config_suffix}"
+                              "exe_autogen/timestamp${autogen_files_config_suffix}")
+          if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "")
+            message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}")
+          endif()
+        endblock()
+      endforeach()
+    endforeach()
+  endforeach()
+
   if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
-    run_ninja(QtX automoc-check build-Debug.ninja -t query exe_autogen/timestamp)
+    foreach(use_better_graph IN ITEMS ON OFF)
+      foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+        foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+          set(prefix "QtX")
+          set(case "${target_config}-in-${ninja_config}-better-graph-${use_better_graph}")
+          set(test_path "${prefix}-${case}")
+          if (use_better_graph)
+            set(autogen_files_config_suffix "_${target_config}")
+          else()
+            set(autogen_files_config_suffix "")
+          endif()
+          set(RunCMake_TEST_VARIANT_DESCRIPTION "-automoc-check")
+          run_ninja(${test_path} "" build-${ninja_config}.ninja -t query exe_autogen/timestamp${autogen_files_config_suffix})
+        endforeach()
+      endforeach()
+    endforeach()
   endif()
 endif()
diff --git a/Tests/RunCMake/ObjectLibrary/exe.c b/Tests/RunCMake/ObjectLibrary/exe.c
index 2e08700..25d7784 100644
--- a/Tests/RunCMake/ObjectLibrary/exe.c
+++ b/Tests/RunCMake/ObjectLibrary/exe.c
@@ -5,7 +5,7 @@
 #endif
 
 extern IMPORT int b(void);
-int main()
+int main(void)
 {
   return b();
 }
diff --git a/Tests/RunCMake/ParseImplicitData/CMakeLists.txt b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt
index 7a8570b..6ba6913 100644
--- a/Tests/RunCMake/ParseImplicitData/CMakeLists.txt
+++ b/Tests/RunCMake/ParseImplicitData/CMakeLists.txt
@@ -55,18 +55,24 @@
   set(outfile "${CMAKE_PLATFORM_INFO_DIR}/test${lang}.out")
   set(CMAKE_FLAGS )
   set(COMPILE_DEFINITIONS )
+  set(LINK_OPTIONS )
   if(DEFINED CMAKE_${lang}_VERBOSE_FLAG)
-    set(CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${CMAKE_${lang}_VERBOSE_FLAG}")
+    set(LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
     set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_FLAG}")
   endif()
   if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG)
     set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}")
   endif()
+  if(DEFINED CMAKE_${lang}_VERBOSE_LINK_FLAG)
+    list(APPEND LINK_OPTIONS "${CMAKE_${lang}_VERBOSE_LINK_FLAG}")
+  endif()
   if(NOT "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
     # Avoid adding our own platform standard libraries for compilers
     # from which we might detect implicit link libraries.
     list(APPEND CMAKE_FLAGS "-DCMAKE_${lang}_STANDARD_LIBRARIES=")
   endif()
+  list(JOIN LINK_OPTIONS " " LINK_OPTIONS)
+  list(APPEND CMAKE_FLAGS "-DEXE_LINKER_FLAGS=${LINK_OPTIONS}")
 
   try_compile(rv ${CMAKE_BINARY_DIR} ${file}
     CMAKE_FLAGS ${CMAKE_FLAGS}
diff --git a/Tests/RunCMake/ParseImplicitData/aix-C-IBMClang-17.1.1.2.input b/Tests/RunCMake/ParseImplicitData/aix-C-IBMClang-17.1.1.2.input
new file mode 100644
index 0000000..0d57b2c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/aix-C-IBMClang-17.1.1.2.input
@@ -0,0 +1,43 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=IBMClang
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=17.1.1.2
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake-build/bin/cmake -E env VERBOSE=1 /opt/freeware/bin/gmake -f Makefile cmTC_b9ba2/fast
+/opt/freeware/bin/gmake  -f CMakeFiles/cmTC_b9ba2.dir/build.make CMakeFiles/cmTC_b9ba2.dir/build
+gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building C object CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o
+/opt/IBM/openxlC/17.1.1/bin/ibm-clang_r   -v -MD -MT CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o -MF CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o.d -o CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o -c /tmp/cmake/Modules/CMakeCCompilerABI.c
+IBM Open XL C/C++ for AIX 17.1.1 (5725-C72, 5765-J18), version 17.1.1.2, clang version 15.0.0 (build 3948f09)
+Target: powerpc-ibm-aix7.3.0.0
+Thread model: posix
+InstalledDir: /opt/IBM/openxlC/17.1.1/bin
+ "/opt/IBM/openxlC/17.1.1/bin/.ibm-clang.orig" -cc1 -triple powerpc-ibm-aix7.3.0.0 -S -disable-free -clear-ast-before-backend -main-file-name CMakeCCompilerABI.c -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -fno-verbose-asm -mconstructor-aliases -target-cpu pwr7 -mfloat-abi hard -mllvm -treat-scalable-fixed-error-as-warning -gstrict-dwarf -gno-column-info -debugger-tuning=dbx -fno-dwarf-directory-asm -v -fdata-sections -fcoverage-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -resource-dir /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0 -dependency-file CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o.d -MT CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o -sys-header-deps -internal-isystem /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers -internal-isystem /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include -internal-isystem /usr/include -internal-isystem /opt/IBM/xlmass/10.1.1/include -fdebug-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -ferror-limit 19 -pthread -fno-signed-char -fno-use-cxa-atexit -fgnuc-version=4.2.1 -no-opaque-pointers -fexec-charset UTF-8 -fxl-pragma-pack -o /tmp/CMakeCCompilerABI-a15e67.s -x c /tmp/cmake/Modules/CMakeCCompilerABI.c
+clang -cc1 version 15.0.0 based upon LLVM 15.0.0git default target powerpc-ibm-aix7.3.0.0
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers
+ /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include
+ /usr/include
+ /opt/IBM/xlmass/10.1.1/include
+End of search list.
+ "/usr/bin/as" -a32 -many -o CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o /tmp/CMakeCCompilerABI-a15e67.s
+Linking C executable cmTC_b9ba2
+/tmp/cmake-build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_b9ba2.dir/link.txt --verbose=1
+/opt/IBM/openxlC/17.1.1/bin/ibm-clang_r -Wl,-bnoipath  -v -Wl,-v -Wl,-bexpall CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o -o cmTC_b9ba2  -Wl,-blibpath:/opt/IBM/xlmass/10.1.1/lib:/usr/lib:/lib
+IBM Open XL C/C++ for AIX 17.1.1 (5725-C72, 5765-J18), version 17.1.1.2, clang version 15.0.0 (build 3948f09)
+Target: powerpc-ibm-aix7.3.0.0
+Thread model: posix
+InstalledDir: /opt/IBM/openxlC/17.1.1/bin
+ "/usr/bin/ld" -o cmTC_b9ba2 -b32 -bpT:0x10000000 -bpD:0x20000000 /usr/lib/crt0.o /usr/lib/crti.o -bcdtors:all:0:s -bnoipath -v -bexpall CMakeFiles/cmTC_b9ba2.dir/CMakeCCompilerABI.c.o -blibpath:/opt/IBM/xlmass/10.1.1/lib:/usr/lib:/lib -L/opt/IBM/xlmass/10.1.1/lib /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libxlopt.a /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libclang_rt.builtins-powerpc.a -lunwind -lpthreads -lc
+gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/aix-CXX-IBMClang-17.1.1.2.input b/Tests/RunCMake/ParseImplicitData/aix-CXX-IBMClang-17.1.1.2.input
new file mode 100644
index 0000000..ace5021
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/aix-CXX-IBMClang-17.1.1.2.input
@@ -0,0 +1,44 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=IBMClang
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=17.1.1.2
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake-build/bin/cmake -E env VERBOSE=1 /opt/freeware/bin/gmake -f Makefile cmTC_7f688/fast
+/opt/freeware/bin/gmake  -f CMakeFiles/cmTC_7f688.dir/build.make CMakeFiles/cmTC_7f688.dir/build
+gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building CXX object CMakeFiles/cmTC_7f688.dir/CMakeCXXCompilerABI.cpp.o
+/opt/IBM/openxlC/17.1.1/bin/ibm-clang++_r -x c++   -v -o CMakeFiles/cmTC_7f688.dir/CMakeCXXCompilerABI.cpp.o -c /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+IBM Open XL C/C++ for AIX 17.1.1 (5725-C72, 5765-J18), version 17.1.1.2, clang version 15.0.0 (build 3948f09)
+Target: powerpc-ibm-aix7.3.0.0
+Thread model: posix
+InstalledDir: /opt/IBM/openxlC/17.1.1/bin
+ "/opt/IBM/openxlC/17.1.1/bin/.ibm-clang.orig" -cc1 -triple powerpc-ibm-aix7.3.0.0 -S -disable-free -clear-ast-before-backend -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -fno-verbose-asm -mconstructor-aliases -target-cpu pwr7 -mfloat-abi hard -mllvm -treat-scalable-fixed-error-as-warning -gstrict-dwarf -gno-column-info -debugger-tuning=dbx -fno-dwarf-directory-asm -v -fdata-sections -fcoverage-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -resource-dir /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0 -internal-isystem /opt/IBM/openxlC/17.1.1/bin/../include/c++/v1 -D__LIBC_NO_CPP_MATH_OVERLOADS__ -internal-isystem /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers -internal-isystem /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include -internal-isystem /usr/include -internal-isystem /opt/IBM/xlmass/10.1.1/include -fdeprecated-macro -fdebug-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -ferror-limit 19 -pthread -fno-signed-char -fno-use-cxa-atexit -fgnuc-version=4.2.1 -no-opaque-pointers -fcxx-exceptions -fexceptions -fexec-charset UTF-8 -fxl-pragma-pack -o /tmp/CMakeCXXCompilerABI-8a2098.s -x c++ /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+clang -cc1 version 15.0.0 based upon LLVM 15.0.0git default target powerpc-ibm-aix7.3.0.0
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/IBM/openxlC/17.1.1/bin/../include/c++/v1
+ /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers
+ /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include
+ /usr/include
+ /opt/IBM/xlmass/10.1.1/include
+End of search list.
+ "/usr/bin/as" -a32 -many -o CMakeFiles/cmTC_7f688.dir/CMakeCXXCompilerABI.cpp.o /tmp/CMakeCXXCompilerABI-8a2098.s
+Linking CXX executable cmTC_7f688
+/tmp/cmake-build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_7f688.dir/link.txt --verbose=1
+/opt/IBM/openxlC/17.1.1/bin/ibm-clang++_r -Wl,-bnoipath  -v -Wl,-v -Wl,-bexpall CMakeFiles/cmTC_7f688.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_7f688  -Wl,-blibpath:/opt/IBM/xlmass/10.1.1/lib:/usr/lib:/lib
+IBM Open XL C/C++ for AIX 17.1.1 (5725-C72, 5765-J18), version 17.1.1.2, clang version 15.0.0 (build 3948f09)
+Target: powerpc-ibm-aix7.3.0.0
+Thread model: posix
+InstalledDir: /opt/IBM/openxlC/17.1.1/bin
+ "/usr/bin/ld" -o cmTC_7f688 -b32 -bpT:0x10000000 -bpD:0x20000000 /usr/lib/crt0.o /usr/lib/crti.o -bcdtors:all:0:s -bnoipath -v -bexpall CMakeFiles/cmTC_7f688.dir/CMakeCXXCompilerABI.cpp.o -blibpath:/opt/IBM/xlmass/10.1.1/lib:/usr/lib:/lib -lc++ -lc++abi -L/opt/IBM/xlmass/10.1.1/lib /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libxlopt.a /opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libclang_rt.builtins-powerpc.a -lunwind -lpthreads -lm -lc
+gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-C-GNU-12.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-C-GNU-12.2.0.input
new file mode 100644
index 0000000..7ecf081
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-C-GNU-12.2.0.input
@@ -0,0 +1,74 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/usr/bin/gcc-ar-12
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=GNU
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/usr/bin/gcc-ranlib-12
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=12.2.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_9e804
+[1/2] /usr/bin/cc   -v -o CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o -c /tmp/cmake/Modules/CMakeCCompilerABI.c
+Using built-in specs.
+COLLECT_GCC=/usr/bin/cc
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9e804.dir/'
+ /usr/lib/gcc/x86_64-linux-gnu/12/cc1 -quiet -v -imultiarch x86_64-linux-gnu /tmp/cmake/Modules/CMakeCCompilerABI.c -quiet -dumpdir CMakeFiles/cmTC_9e804.dir/ -dumpbase CMakeCCompilerABI.c.c -dumpbase-ext .c -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -o /tmp/ccS22h9H.s
+GNU C17 (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/include-fixed"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/lib/gcc/x86_64-linux-gnu/12/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+GNU C17 (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: cc72d2b9b5048fedc2be9051c917b40b
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9e804.dir/'
+ as -v --64 -o CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o /tmp/ccS22h9H.s
+GNU assembler version 2.40 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.40
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.'
+[2/2] : && /usr/bin/cc  -v -Wl,-v  -rdynamic CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o -o cmTC_9e804   && :
+Using built-in specs.
+COLLECT_GCC=/usr/bin/cc
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-rdynamic' '-o' 'cmTC_9e804' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_9e804.'
+ /usr/lib/gcc/x86_64-linux-gnu/12/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/cc4RWkJm.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_9e804 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+collect2 version 12.2.0
+/usr/bin/ld -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/cc4RWkJm.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_9e804 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_9e804.dir/CMakeCCompilerABI.c.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+GNU ld (GNU Binutils for Debian) 2.40
+COLLECT_GCC_OPTIONS='-v' '-rdynamic' '-o' 'cmTC_9e804' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_9e804.'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-C-Intel-2021.10.0.20230609.input b/Tests/RunCMake/ParseImplicitData/linux-C-Intel-2021.10.0.20230609.input
new file mode 100644
index 0000000..60f2017
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-C-Intel-2021.10.0.20230609.input
@@ -0,0 +1,41 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=Intel
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=2021.10.0.20230609
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_c7575
+[1/2] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/icc   -v -MD -MT CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -MF CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o.d -o CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -c /tmp/cmake/Modules/CMakeCCompilerABI.c
+icc: remark #10441: The Intel(R) C++ Compiler Classic (ICC) is deprecated and will be removed from product release in the second half of 2023. The Intel(R) oneAPI DPC++/C++ Compiler (ICX) is the recommended compiler moving forward. Please transition to use this compiler. Use '-diag-disable=10441' to disable this message.
+icc version 2021.10.0 (gcc version 8.3.1 compatibility)
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/mcpcom    --target_efi2 --lang=c -oCMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -_g -mP3OPT_inline_alloca -D__ICC=2021 -D__INTEL_COMPILER=2021 -D__INTEL_COMPILER_UPDATE=10 -D__PTRDIFF_TYPE__=long "-D__SIZE_TYPE__=unsigned long" -D__WCHAR_TYPE__=int "-D__WINT_TYPE__=unsigned int" "-D__INTMAX_TYPE__=long int" "-D__UINTMAX_TYPE__=long unsigned int" -D__LONG_MAX__=9223372036854775807L -D__QMSPP_ -D__OPTIMIZE__ -D__NO_MATH_INLINES -D__NO_STRING_INLINES -D__GNUC_GNU_INLINE__ -D__GNUC__=8 -D__GNUC_MINOR__=3 -D__GNUC_PATCHLEVEL__=1 -D__LP64__ -D_LP64 -D__GXX_ABI_VERSION=1010 "-D__USER_LABEL_PREFIX__= " -D__REGISTER_PREFIX__= -D__INTEL_RTTI__ -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -B -Dunix -Dlinux "-_Asystem(unix)" -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ "-_Acpu(x86_64)" "-_Amachine(x86_64)" -D__INTEL_COMPILER_BUILD_DATE=20230609 -D__INTEL_OFFLOAD -D__MMX__ -D__SSE__ -D__SSE_MATH__ -D__SSE2__ -D__SSE2_MATH__ -D__pentium4 -D__pentium4__ -D__tune_pentium4__ -_k -_8 -_l --has_new_stdarg_support -_a -_b --gnu_version=80301 -_W5 --openmp_simd --noopenmp_offload --dependency_file_name CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o.d --compile_dependencies --dependency_target_name CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o --multibyte_chars -mGLOB_diag_suppress_sys --system_preinclude /usr/include/stdc-predef.h -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include --array_section --simd --simd_func --offload_mode=1 --offload_target_names=mic,MIC --offload_unique_string=icc1008062305yGW96M -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-v -MD -MT CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -MF CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o.d -o CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -c" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/icc2jQ0anas_.s -mIPOPT_activate -mIPOPT_lite -mGLOB_uarch_tuning=0x0 -mGLOB_product_id_code=0x22006d93 -mCG_bnl_movbe=T -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mP2OPT_disam_assume_ansi_c -mP2OPT_checked_disam_ansi_alias=TRUE -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mP2OPT_il0_array_sections=TRUE -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=icc1008062305yGW96M -mP2OPT_hlo_level=2 -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_lto_object_enabled -mIPOPT_lto_object_value=1 -mIPOPT_obj_output_file_name=CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -mIPOPT_whole_archive_fixup_file_name=/tmp/iccwarchY2M6W5 -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/icctempfilegrsOlX -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_C -mP1OPT_source_file_name=/tmp/cmake/Modules/CMakeCCompilerABI.c -mP1OPT_full_source_file_name=/tmp/cmake/Modules/CMakeCCompilerABI.c /tmp/cmake/Modules/CMakeCCompilerABI.c
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-redhat-linux/8/include
+ /usr/include/
+ /usr/include
+End of search list.
+[2/2] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/icc  -v  -rdynamic CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -o cmTC_c7575   && :
+icc: remark #10441: The Intel(R) C++ Compiler Classic (ICC) is deprecated and will be removed from product release in the second half of 2023. The Intel(R) oneAPI DPC++/C++ Compiler (ICX) is the recommended compiler moving forward. Please transition to use this compiler. Use '-diag-disable=10441' to disable this message.
+icc version 2021.10.0 (gcc version 8.3.1 compatibility)
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/mcpcom    -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-v -rdynamic -o cmTC_c7575" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/iccWrdTRVas_.s -mGLOB_dashboard_use_source_name -mIPOPT_activate -mGLOB_product_id_code=0x22006d93 -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mP2OPT_disam_assume_ansi_c -mP2OPT_checked_disam_ansi_alias=TRUE -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mGLOB_opt_report_use_source_name -mP2OPT_il0_array_sections=TRUE -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=icc130092668226Q7tV -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_link -mIPOPT_ipo_activate -mIPOPT_mo_activate -mIPOPT_source_files_list=/tmp/iccsliskXOPjc -mIPOPT_mo_global_data -mIPOPT_link_script_file=/tmp/iccscriptcFJQ53 "-mIPOPT_cmdline_link="/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o" "/usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o" "-export-dynamic" "--eh-frame-hdr" "--build-id" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-m" "elf_x86_64" "-o" "cmTC_c7575" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release" "-L/opt/intel/oneapi/mpi/2021.10.0//lib" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin" "-L/usr/lib/gcc/x86_64-redhat-linux/8/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/" "-L/lib/../lib64" "-L/lib/../lib64/" "-L/usr/lib/../lib64" "-L/usr/lib/../lib64/" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../" "-L/lib64" "-L/lib/" "-L/usr/lib64" "-L/usr/lib" "CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o" "-Bdynamic" "-Bstatic" "-limf" "-lsvml" "-lirng" "-Bdynamic" "-lm" "-Bstatic" "-lipgo" "-ldecimal" "--as-needed" "-Bdynamic" "-lcilkrts" "-lstdc++" "--no-as-needed" "-lgcc" "-lgcc_s" "-Bstatic" "-lirc" "-lsvml" "-Bdynamic" "-lc" "-lgcc" "-lgcc_s" "-Bstatic" "-lirc_s" "-Bdynamic" "-ldl" "-lc" "/usr/lib/gcc/x86_64-redhat-linux/8/crtend.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o"" -mIPOPT_il_in_obj -mIPOPT_ipo_activate_warn=FALSE -mIPOPT_obj_output_file_name=/tmp/ipo_icc4yyaGZ.o -mIPOPT_whole_archive_fixup_file_name=/tmp/iccwarchKxTVrF -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/icctempfileo3zWfx -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=/tmp/ipo_icc4yyaGZ.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_NONE -mP1OPT_source_file_name=ipo_out.c CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -mIPOPT_object_files=T -mIPOPT_assembly_files=/tmp/iccalisibzYdJ -mIPOPT_generated_tempfiles=/tmp/iccelisAuAUZA -mIPOPT_embedded_object_base_name=/tmp/icceobjWlKRLs -mIPOPT_cmdline_link_new_name=/tmp/iccllisG28Pxk
+ld    /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o -export-dynamic --eh-frame-hdr --build-id -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o cmTC_c7575 -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin -L/usr/lib/gcc/x86_64-redhat-linux/8/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/ -L/lib/../lib64 -L/lib/../lib64/ -L/usr/lib/../lib64 -L/usr/lib/../lib64/ -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/ -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/release/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/ -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../ -L/lib64 -L/lib/ -L/usr/lib64 -L/usr/lib CMakeFiles/cmTC_c7575.dir/CMakeCCompilerABI.c.o -Bdynamic -Bstatic -limf -lsvml -lirng -Bdynamic -lm -Bstatic -lipgo -ldecimal --as-needed -Bdynamic -lcilkrts -lstdc++ --no-as-needed -lgcc -lgcc_s -Bstatic -lirc -lsvml -Bdynamic -lc -lgcc -lgcc_s -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-C-IntelLLVM-2023.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-C-IntelLLVM-2023.2.0.input
new file mode 100644
index 0000000..dd3b227
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-C-IntelLLVM-2023.2.0.input
@@ -0,0 +1,59 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ar
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=IntelLLVM
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ranlib
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=2023.2.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_0d8c1
+[1/2] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/icx   -v -MD -MT CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -MF CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o.d -o CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -c /tmp/cmake/Modules/CMakeCCompilerABI.c
+Intel(R) oneAPI DPC++/C++ Compiler 2023.2.0 (2023.2.0.20230721)
+Target: x86_64-unknown-linux-gnu
+Thread model: posix
+InstalledDir: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm
+Configuration file: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../bin/icx.cfg
+Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Selected multilib: .@m64
+ (in-process)
+ "/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/clang" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name CMakeCCompilerABI.c -mrelocation-model static -fveclib=SVML -mframe-pointer=none -menable-no-infs -menable-no-nans -fapprox-func -funsafe-math-optimizations -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -mllvm -x86-enable-unaligned-vector-move=true -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -resource-dir /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17 -dependency-file CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o.d -MT CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -sys-header-deps -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -internal-isystem /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../compiler/include -internal-isystem /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -ferror-limit 19 -fheinous-gnu-extensions -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -D__GCC_HAVE_DWARF2_CFI_ASM=1 -fintel-compatibility -fintel-libirc-allowed -mllvm -disable-hir-generate-mkl-call -mllvm -loopopt=1 -floopopt-pipeline=light -mllvm -intel-abi-compatible=true -o CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -x c /tmp/cmake/Modules/CMakeCCompilerABI.c
+clang -cc1 version 17.0.0 based upon LLVM 17.0.0git default target x86_64-unknown-linux-gnu
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include"
+ignoring nonexistent directory "/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../compiler/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include
+ /usr/local/include
+ /usr/include
+End of search list.
+[2/2] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/icx  -v  -rdynamic CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -o cmTC_0d8c1   && :
+Intel(R) oneAPI DPC++/C++ Compiler 2023.2.0 (2023.2.0.20230721)
+Target: x86_64-unknown-linux-gnu
+Thread model: posix
+InstalledDir: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm
+Configuration file: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../bin/icx.cfg
+Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Selected multilib: .@m64
+ "/usr/bin/ld" -export-dynamic --hash-style=gnu --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_0d8c1 /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/usr/lib/gcc/x86_64-redhat-linux/8 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../.. -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../lib -L/lib -L/usr/lib -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib CMakeFiles/cmTC_0d8c1.dir/CMakeCCompilerABI.c.o -Bstatic -lsvml -Bdynamic -Bstatic -lirng -Bdynamic -Bstatic -limf -Bdynamic -lm -lgcc --as-needed -lgcc_s --no-as-needed -Bstatic -lirc -Bdynamic -ldl -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed -Bstatic -lirc_s -Bdynamic /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CXX-GNU-12.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-GNU-12.2.0.input
new file mode 100644
index 0000000..134a8e9
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CXX-GNU-12.2.0.input
@@ -0,0 +1,78 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/usr/bin/gcc-ar-12
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=GNU
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/usr/bin/gcc-ranlib-12
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=12.2.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_705d2
+[1/2] /usr/bin/c++   -v -o CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o -c /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+Using built-in specs.
+COLLECT_GCC=/usr/bin/c++
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_705d2.dir/'
+ /usr/lib/gcc/x86_64-linux-gnu/12/cc1plus -quiet -v -imultiarch x86_64-linux-gnu -D_GNU_SOURCE /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp -quiet -dumpdir CMakeFiles/cmTC_705d2.dir/ -dumpbase CMakeCXXCompilerABI.cpp.cpp -dumpbase-ext .cpp -mtune=generic -march=x86-64 -version -fasynchronous-unwind-tables -o /tmp/ccn8sUGv.s
+GNU C++17 (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring duplicate directory "/usr/include/x86_64-linux-gnu/c++/12"
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/include-fixed"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/include/c++/12
+ /usr/include/x86_64-linux-gnu/c++/12
+ /usr/include/c++/12/backward
+ /usr/lib/gcc/x86_64-linux-gnu/12/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+GNU C++17 (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: 62b090dbbefa50644117a3c13d47369a
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_705d2.dir/'
+ as -v --64 -o CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o /tmp/ccn8sUGv.s
+GNU assembler version 2.40 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.40
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.'
+[2/2] : && /usr/bin/c++  -v -Wl,-v  -rdynamic CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_705d2   && :
+Using built-in specs.
+COLLECT_GCC=/usr/bin/c++
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-rdynamic' '-o' 'cmTC_705d2' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_705d2.'
+ /usr/lib/gcc/x86_64-linux-gnu/12/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/ccAJr9zl.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_705d2 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+collect2 version 12.2.0
+/usr/bin/ld -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/ccAJr9zl.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_705d2 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_705d2.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+GNU ld (GNU Binutils for Debian) 2.40
+COLLECT_GCC_OPTIONS='-v' '-rdynamic' '-o' 'cmTC_705d2' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_705d2.'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CXX-Intel-2021.10.0.20230609.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-Intel-2021.10.0.20230609.input
new file mode 100644
index 0000000..8a7ffda
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CXX-Intel-2021.10.0.20230609.input
@@ -0,0 +1,44 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=Intel
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=2021.10.0.20230609
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_b5439
+[1/2] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/icpc   -v -MD -MT CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -MF CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o.d -o CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -c /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+icpc: remark #10441: The Intel(R) C++ Compiler Classic (ICC) is deprecated and will be removed from product release in the second half of 2023. The Intel(R) oneAPI DPC++/C++ Compiler (ICX) is the recommended compiler moving forward. Please transition to use this compiler. Use '-diag-disable=10441' to disable this message.
+icpc version 2021.10.0 (gcc version 8.3.1 compatibility)
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/mcpcom    --target_efi2 --lang=c++ -oCMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -_g -mP3OPT_inline_alloca -D__ICC=2021 -D__INTEL_COMPILER=2021 -D__INTEL_COMPILER_UPDATE=10 -D__PTRDIFF_TYPE__=long "-D__SIZE_TYPE__=unsigned long" -D__WCHAR_TYPE__=int "-D__WINT_TYPE__=unsigned int" "-D__INTMAX_TYPE__=long int" "-D__UINTMAX_TYPE__=long unsigned int" -D__GLIBCXX_TYPE_INT_N_0=__int128 -D__GLIBCXX_BITSIZE_INT_N_0=128 -D__LONG_MAX__=9223372036854775807L -D__QMSPP_ -D__OPTIMIZE__ -D__NO_MATH_INLINES -D__NO_STRING_INLINES -D__GNUC_GNU_INLINE__ -D__GNUG__=8 -D__GNUC__=8 -D__GNUC_MINOR__=3 -D__GNUC_PATCHLEVEL__=1 -D__LP64__ -D_LP64 -D_GNU_SOURCE=1 -D__DEPRECATED=1 -D__GXX_WEAK__=1 -D__GXX_ABI_VERSION=1010 "-D__USER_LABEL_PREFIX__= " -D__REGISTER_PREFIX__= -D__INTEL_RTTI__ -D__EXCEPTIONS=1 -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -B -Dunix -Dlinux "-_Asystem(unix)" -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ "-_Acpu(x86_64)" "-_Amachine(x86_64)" -D__INTEL_COMPILER_BUILD_DATE=20230609 -D__INTEL_OFFLOAD -D__MMX__ -D__SSE__ -D__SSE_MATH__ -D__SSE2__ -D__SSE2_MATH__ -D__pentium4 -D__pentium4__ -D__tune_pentium4__ -_k -_8 -_l --has_new_stdarg_support -_a -_b --gnu_version=80301 -_W5 -p --bool -tused -x --openmp_simd --noopenmp_offload --dependency_file_name CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o.d --compile_dependencies --dependency_target_name CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o --multibyte_chars -mGLOB_diag_suppress_sys --system_preinclude /usr/include/stdc-predef.h -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include --array_section --simd --simd_func --offload_mode=1 --offload_target_names=mic,MIC --offload_unique_string=icpc1590505640fLpnC2 --bool -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-v -MD -MT CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -MF CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o.d -o CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -c" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/icpctw7qvGas_.s -mIPOPT_activate -mIPOPT_lite -mGLOB_uarch_tuning=0x0 -mGLOB_product_id_code=0x22006d90 -mCG_bnl_movbe=T -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mP2OPT_disam_assume_ansi_c -mP2OPT_checked_disam_ansi_alias=TRUE -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mP2OPT_il0_array_sections=TRUE -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=icpc1590505640fLpnC2 -mP2OPT_hlo_level=2 -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_lto_object_enabled -mIPOPT_lto_object_value=1 -mIPOPT_obj_output_file_name=CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -mIPOPT_whole_archive_fixup_file_name=/tmp/icpcwarch1PUgQr -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/icpctempfiletJgmwk -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_C_PLUS_PLUS -mP1OPT_source_file_name=/tmp/cmake/Modules/CMakeCXXCompilerABI.cpp -mP1OPT_full_source_file_name=/tmp/cmake/Modules/CMakeCXXCompilerABI.cpp -mGLOB_eh_linux /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include
+ /usr/include/c++/8
+ /usr/include/c++/8/x86_64-redhat-linux
+ /usr/include/c++/8/backward
+ /usr/local/include
+ /usr/lib/gcc/x86_64-redhat-linux/8/include
+ /usr/include/
+ /usr/include
+End of search list.
+[2/2] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/icpc  -v  -rdynamic CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_b5439   && :
+icpc: remark #10441: The Intel(R) C++ Compiler Classic (ICC) is deprecated and will be removed from product release in the second half of 2023. The Intel(R) oneAPI DPC++/C++ Compiler (ICX) is the recommended compiler moving forward. Please transition to use this compiler. Use '-diag-disable=10441' to disable this message.
+icpc version 2021.10.0 (gcc version 8.3.1 compatibility)
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/mcpcom    -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-v -rdynamic -o cmTC_b5439" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/icpcN3pftvas_.s -mGLOB_dashboard_use_source_name -mIPOPT_activate -mGLOB_product_id_code=0x22006d90 -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mP2OPT_disam_assume_ansi_c -mP2OPT_checked_disam_ansi_alias=TRUE -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mGLOB_opt_report_use_source_name -mP2OPT_il0_array_sections=TRUE -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=icpc0800981546zDEBac -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_link -mIPOPT_ipo_activate -mIPOPT_mo_activate -mIPOPT_source_files_list=/tmp/icpcslisbcBQoJ -mIPOPT_mo_global_data -mIPOPT_link_script_file=/tmp/icpcscript9dg2qC "-mIPOPT_cmdline_link="/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o" "/usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o" "-export-dynamic" "--eh-frame-hdr" "--build-id" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-m" "elf_x86_64" "-o" "cmTC_b5439" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release" "-L/opt/intel/oneapi/mpi/2021.10.0//lib" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin" "-L/usr/lib/gcc/x86_64-redhat-linux/8/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/" "-L/lib/../lib64" "-L/lib/../lib64/" "-L/usr/lib/../lib64" "-L/usr/lib/../lib64/" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../" "-L/lib64" "-L/lib/" "-L/usr/lib64" "-L/usr/lib" "CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o" "-Bdynamic" "-Bstatic" "-limf" "-lsvml" "-lirng" "-Bdynamic" "-lstdc++" "-lm" "-Bstatic" "-lipgo" "-ldecimal" "--as-needed" "-Bdynamic" "-lcilkrts" "--no-as-needed" "-lstdc++" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-Bstatic" "-lirc" "-lsvml" "-Bdynamic" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-Bstatic" "-lirc_s" "-Bdynamic" "-ldl" "-lc" "/usr/lib/gcc/x86_64-redhat-linux/8/crtend.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o"" -mIPOPT_il_in_obj -mIPOPT_ipo_activate_warn=FALSE -mIPOPT_obj_output_file_name=/tmp/ipo_icpcZow8bp.o -mIPOPT_whole_archive_fixup_file_name=/tmp/icpcwarchPRGCzh -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/icpctempfileTAaNDa -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=/tmp/ipo_icpcZow8bp.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_NONE -mP1OPT_source_file_name=ipo_out.c CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -mIPOPT_object_files=T -mIPOPT_assembly_files=/tmp/icpcalisv59hgb -mIPOPT_generated_tempfiles=/tmp/icpcelis1IXoi4 -mIPOPT_embedded_object_base_name=/tmp/icpceobj1VZwkX -mIPOPT_cmdline_link_new_name=/tmp/icpcllisFeaGmQ
+ld    /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o -export-dynamic --eh-frame-hdr --build-id -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o cmTC_b5439 -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin -L/usr/lib/gcc/x86_64-redhat-linux/8/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/ -L/lib/../lib64 -L/lib/../lib64/ -L/usr/lib/../lib64 -L/usr/lib/../lib64/ -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/ -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/release/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/ -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../ -L/lib64 -L/lib/ -L/usr/lib64 -L/usr/lib CMakeFiles/cmTC_b5439.dir/CMakeCXXCompilerABI.cpp.o -Bdynamic -Bstatic -limf -lsvml -lirng -Bdynamic -lstdc++ -lm -Bstatic -lipgo -ldecimal --as-needed -Bdynamic -lcilkrts --no-as-needed -lstdc++ -lgcc --as-needed -lgcc_s --no-as-needed -Bstatic -lirc -lsvml -Bdynamic -lc -lgcc --as-needed -lgcc_s --no-as-needed -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-CXX-IntelLLVM-2023.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-CXX-IntelLLVM-2023.2.0.input
new file mode 100644
index 0000000..8713d6d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-CXX-IntelLLVM-2023.2.0.input
@@ -0,0 +1,62 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ar
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=IntelLLVM
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ranlib
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=2023.2.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_05be3
+[1/2] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/icpx   -v -MD -MT CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -MF CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o.d -o CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -c /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+Intel(R) oneAPI DPC++/C++ Compiler 2023.2.0 (2023.2.0.20230721)
+Target: x86_64-unknown-linux-gnu
+Thread model: posix
+InstalledDir: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm
+Configuration file: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../bin/icpx.cfg
+Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Selected multilib: .@m64
+ (in-process)
+ "/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/clang" -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model static -fveclib=SVML -mframe-pointer=none -menable-no-infs -menable-no-nans -fapprox-func -funsafe-math-optimizations -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -mllvm -x86-enable-unaligned-vector-move=true -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -resource-dir /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17 -dependency-file CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o.d -MT CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -sys-header-deps -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -internal-isystem /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../compiler/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -fdeprecated-macro -fdebug-compilation-dir=/tmp/ii/CMakeFiles/CMakeTmp -ferror-limit 19 -fheinous-gnu-extensions -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -D__GCC_HAVE_DWARF2_CFI_ASM=1 -fintel-compatibility -fintel-libirc-allowed -mllvm -disable-hir-generate-mkl-call -mllvm -loopopt=1 -floopopt-pipeline=light -mllvm -intel-abi-compatible=true -o CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -x c++ /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+clang -cc1 version 17.0.0 based upon LLVM 17.0.0git default target x86_64-unknown-linux-gnu
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include"
+ignoring nonexistent directory "/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../compiler/include
+ /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8
+ /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux
+ /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include
+ /usr/local/include
+ /usr/include
+End of search list.
+[2/2] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/icpx  -v  -rdynamic CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_05be3   && :
+Intel(R) oneAPI DPC++/C++ Compiler 2023.2.0 (2023.2.0.20230721)
+Target: x86_64-unknown-linux-gnu
+Thread model: posix
+InstalledDir: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm
+Configuration file: /opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../bin/icpx.cfg
+Found candidate GCC installation: /usr/lib/gcc/i686-redhat-linux/8
+Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/8
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Selected multilib: .@m64
+ "/usr/bin/ld" -export-dynamic --hash-style=gnu --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_05be3 /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/usr/lib/gcc/x86_64-redhat-linux/8 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../.. -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/../lib -L/lib -L/usr/lib -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib CMakeFiles/cmTC_05be3.dir/CMakeCXXCompilerABI.cpp.o -Bstatic -lsvml -Bdynamic -Bstatic -lirng -Bdynamic -lstdc++ -Bstatic -limf -Bdynamic -lm -lgcc_s -lgcc -Bstatic -lirc -Bdynamic -ldl -lgcc_s -lgcc -lc -lgcc_s -lgcc -Bstatic -lirc_s -Bdynamic /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-12.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-12.2.0.input
new file mode 100644
index 0000000..ed4ddcb
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-GNU-12.2.0.input
@@ -0,0 +1,93 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=/usr/bin/gcc-ar-12
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=GNU
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=/usr/bin/gcc-ranlib-12
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=12.2.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_62ee5
+[1/4] /usr/bin/gfortran -cpp   -v -E /tmp/cmake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f && /tmp/cmake-build/bin/cmake -E cmake_ninja_depends --tdi=CMakeFiles/cmTC_62ee5.dir/FortranDependInfo.json --lang=Fortran --src=CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f --out=CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f --dep=CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f.d --obj=CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o --ddi=CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o.ddi
+Using built-in specs.
+COLLECT_GCC=/usr/bin/gfortran
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COLLECT_GCC_OPTIONS='-cpp' '-v' '-E' '-o' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_62ee5.dir/'
+ /usr/lib/gcc/x86_64-linux-gnu/12/f951 /tmp/cmake/Modules/CMakeFortranCompilerABI.F -ffixed-form -cpp=/tmp/ccJUkJqq.f90 -E -quiet -v -imultiarch x86_64-linux-gnu /tmp/cmake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f -dumpdir CMakeFiles/cmTC_62ee5.dir/ -dumpbase CMakeFortranCompilerABI.F-pp.F -dumpbase-ext .F -mtune=generic -march=x86-64 -fsyntax-only
+ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/include-fixed"
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /usr/lib/gcc/x86_64-linux-gnu/12/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-cpp' '-v' '-E' '-o' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.'
+[2/4] /tmp/cmake-build/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/cmTC_62ee5.dir/FortranDependInfo.json --lang=Fortran --dd=CMakeFiles/cmTC_62ee5.dir/Fortran.dd @CMakeFiles/cmTC_62ee5.dir/Fortran.dd.rsp
+[3/4] /usr/bin/gfortran -I/tmp/cmake/Modules -v -fpreprocessed -c CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f -o CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o
+Using built-in specs.
+COLLECT_GCC=/usr/bin/gfortran
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+COLLECT_GCC_OPTIONS='-I' '/tmp/cmake/Modules' '-v' '-fpreprocessed' '-c' '-o' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_62ee5.dir/'
+ /usr/lib/gcc/x86_64-linux-gnu/12/f951 CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F-pp.f -ffixed-form -I /tmp/cmake/Modules -quiet -dumpdir CMakeFiles/cmTC_62ee5.dir/ -dumpbase CMakeFortranCompilerABI.F.f -dumpbase-ext .f -mtune=generic -march=x86-64 -version -fpreprocessed -fintrinsic-modules-path /usr/lib/gcc/x86_64-linux-gnu/12/finclude -fpre-include=/usr/include/finclude/x86_64-linux-gnu/math-vector-fortran.h -o /tmp/ccj0kDCS.s
+GNU Fortran (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+GNU Fortran2008 (Debian 12.2.0-14) version 12.2.0 (x86_64-linux-gnu)
+	compiled by GNU C version 12.2.0, GMP version 6.2.1, MPFR version 4.1.1-p1, MPC version 1.3.1, isl version isl-0.25-GMP
+
+warning: MPFR header version 4.1.1-p1 differs from library version 4.2.0.
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+COLLECT_GCC_OPTIONS='-I' '/tmp/cmake/Modules' '-v' '-fpreprocessed' '-c' '-o' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_62ee5.dir/'
+ as -v -I /tmp/cmake/Modules --64 -o CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o /tmp/ccj0kDCS.s
+GNU assembler version 2.40 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.40
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-I' '/tmp/cmake/Modules' '-v' '-fpreprocessed' '-c' '-o' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=x86-64' '-dumpdir' 'CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.'
+[4/4] : && /usr/bin/gfortran -v -Wl,-v  CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o -o cmTC_62ee5   && :
+Driving: /usr/bin/gfortran -v -Wl,-v CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o -o cmTC_62ee5 -l gfortran -l m -shared-libgcc
+Using built-in specs.
+COLLECT_GCC=/usr/bin/gfortran
+COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper
+OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
+OFFLOAD_TARGET_DEFAULT=1
+Target: x86_64-linux-gnu
+Configured with: ../src/configure -v --with-pkgversion='Debian 12.2.0-14' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-bTRWOB/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+Thread model: posix
+Supported LTO compression algorithms: zlib zstd
+gcc version 12.2.0 (Debian 12.2.0-14)
+Reading specs from /usr/lib/gcc/x86_64-linux-gnu/12/libgfortran.spec
+rename spec lib to liborig
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_62ee5' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_62ee5-'
+COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/
+LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/12/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_62ee5' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_62ee5.'
+ /usr/lib/gcc/x86_64-linux-gnu/12/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/ccGtuZbF.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_62ee5 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc -lquadmath -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+collect2 version 12.2.0
+/usr/bin/ld -plugin /usr/lib/gcc/x86_64-linux-gnu/12/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper -plugin-opt=-fresolution=/tmp/ccGtuZbF.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -o cmTC_62ee5 /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/12 -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/12/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/12/../../.. -v CMakeFiles/cmTC_62ee5.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc -lquadmath -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/12/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/crtn.o
+GNU ld (GNU Binutils for Debian) 2.40
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_62ee5' '-shared-libgcc' '-mtune=generic' '-march=x86-64' '-dumpdir' 'cmTC_62ee5.'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-Intel-2021.10.0.20230609.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-Intel-2021.10.0.20230609.input
new file mode 100644
index 0000000..7379c84
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-Intel-2021.10.0.20230609.input
@@ -0,0 +1,63 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_Fortran_COMPILER_ABI=ELF
+CMAKE_Fortran_COMPILER_AR=
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=Intel
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=2021.10.0.20230609
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_9370c
+[1/4] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/ifort -fpp   -v -P /tmp/cmake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f && /tmp/cmake-build/bin/cmake -E cmake_ninja_depends --tdi=CMakeFiles/cmTC_9370c.dir/FortranDependInfo.json --lang=Fortran --src=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f --out=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f --dep=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f.d --obj=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o --ddi=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o.ddi
+ifort version 2021.10.0
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/fpp    -D__INTEL_COMPILER=2021 -D__INTEL_COMPILER_UPDATE=10 -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -Dunix -Dlinux -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ -D__INTEL_COMPILER_BUILD_DATE=20230609 -D__INTEL_OFFLOAD -D__MMX__ -D__SSE__ -D__SSE_MATH__ -D__SSE2__ -D__SSE2_MATH__ -D__pentium4 -D__pentium4__ -D__tune_pentium4__ -I. -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64 -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include -I/usr/local/include -I/usr/lib/gcc/x86_64-redhat-linux/8/include -I/usr/include/ -I/usr/include -fixed -4Ycpp -4Ncvf -f_com=yes /tmp/cmake/Modules/CMakeFortranCompilerABI.F CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f
+#include "..." search starts here:
+#include <...> search starts here:
+ .
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-redhat-linux/8/include
+ /usr/include/
+ /usr/include
+End of search list.
+[2/4] /tmp/cmake-build/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/cmTC_9370c.dir/FortranDependInfo.json --lang=Fortran --dd=CMakeFiles/cmTC_9370c.dir/Fortran.dd @CMakeFiles/cmTC_9370c.dir/Fortran.dd.rsp
+[3/4] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/ifort  -I/tmp/cmake/Modules -v -c CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f -o CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o
+ifort version 2021.10.0
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/fortcom    -D__INTEL_COMPILER=2021 -D__INTEL_COMPILER_UPDATE=10 -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -Dunix -Dlinux -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ -D__INTEL_COMPILER_BUILD_DATE=20230609 -D__INTEL_OFFLOAD -D__MMX__ -D__SSE__ -D__SSE_MATH__ -D__SSE2__ -D__SSE2_MATH__ -D__pentium4 -D__pentium4__ -D__tune_pentium4__ -ICMakeFiles/cmTC_9370c.dir -I. -I/tmp/cmake/Modules -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64 -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc -I/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include -I/usr/local/include -I/usr/lib/gcc/x86_64-redhat-linux/8/include -I/usr/include/ -I/usr/include -omp_simd -O2 "-reentrancy threaded" -simd -offload_host -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mGLOB_source_language=GLOB_SOURCE_LANGUAGE_F90 -mP2OPT_static_promotion -mGLOB_pack_sort_init_list -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-I/tmp/cmake/Modules -v -c -o CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/ifortlMfujias_.s -mIPOPT_activate -mIPOPT_lite -mGLOB_uarch_tuning=0x0 -mGLOB_product_id_code=0x22006d91 -mCG_bnl_movbe=T -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=ifort0602277341Xeklvz -mP2OPT_hlo_level=2 -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_lto_object_enabled -mIPOPT_lto_object_value=1 -mIPOPT_obj_output_file_name=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o -mIPOPT_whole_archive_fixup_file_name=/tmp/ifortwarchdJu6U6 -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/iforttempfile9xZze1 -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_FORTRAN -mP1OPT_source_file_name=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f -mP1OPT_full_source_file_name=/tmp/ii/CMakeFiles/CMakeTmp/CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f -mP2OPT_symtab_type_copy=true CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F-pp.f
+#include "..." search starts here:
+#include <...> search starts here:
+ CMakeFiles/cmTC_9370c.dir
+ .
+ /tmp/cmake/Modules
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include/icc
+ /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/include
+ /usr/local/include
+ /usr/lib/gcc/x86_64-redhat-linux/8/include
+ /usr/include/
+ /usr/include
+End of search list.
+[4/4] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/ifort -v  CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o -o cmTC_9370c   && :
+ifort version 2021.10.0
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/fortcom    -mGLOB_em64t=TRUE -mP1OPT_version=2021.10.0-intel64 -mGLOB_diag_file=CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.diag -mGLOB_long_size_64 -mGLOB_routine_pointer_size_64 -mGLOB_source_language=GLOB_SOURCE_LANGUAGE_F90 -mP2OPT_static_promotion -mGLOB_pack_sort_init_list -mP1OPT_print_version=FALSE -mCG_use_gas_got_workaround=F -mP2OPT_align_option_used=TRUE -mGLOB_gcc_version=831 "-mGLOB_options_string=-v -o cmTC_9370c" -mGLOB_cxx_limited_range=FALSE -mCG_extend_parms=FALSE -mGLOB_compiler_bin_directory=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64 -mGLOB_as_output_backup_file_name=/tmp/ifortdtZJJpas_.s -mGLOB_dashboard_use_source_name -mIPOPT_activate -mGLOB_product_id_code=0x22006d91 -mP3OPT_use_mspp_call_convention -mP2OPT_subs_out_of_bound=FALSE -mP2OPT_disam_type_based_disam=2 -mGLOB_ansi_alias -mPGOPTI_value_profile_use=T -mGLOB_opt_report_use_source_name -mGLOB_offload_mode=1 -mGLOB_offload_no_openmp=TRUE -mP2OPT_offload_unique_var_string=ifort1973007351zLXgMH -mP2OPT_hlo -mP2OPT_hpo_rtt_control=0 -mIPOPT_args_in_regs=0 -mP2OPT_disam_assume_nonstd_intent_in=FALSE -mGLOB_imf_mapping_library=/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/libiml_attr.so -mPGOPTI_gen_threadsafe_level=0 -mIPOPT_link -mIPOPT_ipo_activate -mIPOPT_mo_activate -mIPOPT_source_files_list=/tmp/ifortslisxGkpnA -mIPOPT_mo_global_data -mIPOPT_link_script_file=/tmp/ifortscript7sRy3u "-mIPOPT_cmdline_link="/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o" "/usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o" "--eh-frame-hdr" "--build-id" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-m" "elf_x86_64" "-plugin" "/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/../../lib/icx-lto.so" "-o" "cmTC_9370c" "/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin/for_main.o" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release" "-L/opt/intel/oneapi/mpi/2021.10.0//lib" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin" "-L/usr/lib/gcc/x86_64-redhat-linux/8/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/" "-L/lib/../lib64" "-L/lib/../lib64/" "-L/usr/lib/../lib64" "-L/usr/lib/../lib64/" "-L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/" "-L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/release/" "-L/opt/intel/oneapi/mpi/2021.10.0//lib/" "-L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/" "-L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/" "-L/usr/lib/gcc/x86_64-redhat-linux/8/../../../" "-L/lib64" "-L/lib/" "-L/usr/lib64" "-L/usr/lib" "CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o" "-Bdynamic" "-Bstatic" "-lifport" "-lifcoremt" "-limf" "-lsvml" "-Bdynamic" "-lm" "-Bstatic" "-lipgo" "-lirc" "-Bdynamic" "-lpthread" "-Bstatic" "-lsvml" "-Bdynamic" "-lc" "-lgcc" "-lgcc_s" "-Bstatic" "-lirc_s" "-Bdynamic" "-ldl" "-lc" "/usr/lib/gcc/x86_64-redhat-linux/8/crtend.o" "/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o"" -mIPOPT_il_in_obj -mIPOPT_ipo_activate_warn=FALSE -mIPOPT_obj_output_file_name=/tmp/ipo_ifort9BUTi6.o -mIPOPT_whole_archive_fixup_file_name=/tmp/ifortwarchVuR57e -mGLOB_linker_version=2.30 -mGLOB_linker=ld -mGLOB_driver_tempfile_name=/tmp/iforttempfile1X5gQ9 -mP3OPT_asm_target=P3OPT_ASM_TARGET_GAS -mGLOB_async_unwind_tables=TRUE -mGLOB_obj_output_file=/tmp/ipo_ifort9BUTi6.o -mGLOB_source_dialect=GLOB_SOURCE_DIALECT_NONE -mP1OPT_source_file_name=ipo_out.f -mP2OPT_symtab_type_copy=true CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o -mIPOPT_object_files=T -mIPOPT_assembly_files=/tmp/ifortalisTIqZEV -mIPOPT_generated_tempfiles=/tmp/ifortelisvL73kQ -mIPOPT_embedded_object_base_name=/tmp/iforteobjx0090K -mIPOPT_cmdline_link_new_name=/tmp/ifortllisH21gHF
+ld    /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o --eh-frame-hdr --build-id -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -plugin /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../bin/intel64/../../lib/icx-lto.so -o cmTC_9370c /opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin/for_main.o -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/bin/intel64/../../compiler/lib/intel64_lin -L/usr/lib/gcc/x86_64-redhat-linux/8/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/ -L/lib/../lib64 -L/lib/../lib64/ -L/usr/lib/../lib64 -L/usr/lib/../lib64/ -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/ -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/release/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/ -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../ -L/lib64 -L/lib/ -L/usr/lib64 -L/usr/lib CMakeFiles/cmTC_9370c.dir/CMakeFortranCompilerABI.F.o -Bdynamic -Bstatic -lifport -lifcoremt -limf -lsvml -Bdynamic -lm -Bstatic -lipgo -lirc -Bdynamic -lpthread -Bstatic -lsvml -Bdynamic -lc -lgcc -lgcc_s -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/linux-Fortran-IntelLLVM-2023.2.0.input b/Tests/RunCMake/ParseImplicitData/linux-Fortran-IntelLLVM-2023.2.0.input
new file mode 100644
index 0000000..53c7e53
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-Fortran-IntelLLVM-2023.2.0.input
@@ -0,0 +1,58 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_Fortran_COMPILER_ABI=ELF
+CMAKE_Fortran_COMPILER_AR=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ar
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=IntelLLVM
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/llvm-ranlib
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=2023.2.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/.gitlab/ninja -v cmTC_59e9e
+[1/4] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/ifx -fpp   -v -P /tmp/cmake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f && /tmp/cmake-build/bin/cmake -E cmake_ninja_depends --tdi=CMakeFiles/cmTC_59e9e.dir/FortranDependInfo.json --lang=Fortran --src=CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f --out=CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f --dep=CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f.d --obj=CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o --ddi=CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o.ddi
+ifx version 2023.2.0
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin/fpp    -D__INTEL_LLVM_COMPILER=20230200 -D__INTEL_LLVM_COMPILER_UPDATE=0 -D__INTEL_COMPILER=20230200 -D__INTEL_COMPILER_UPDATE=0 -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -Dunix -Dlinux -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ -D__INTEL_COMPILER_BUILD_DATE=20230721 -I. -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64 -I/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include -I/usr/local/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include -I/usr/include -fixed -4Ycpp -4Ncvf -f_com=yes /tmp/cmake/Modules/CMakeFortranCompilerABI.F CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f
+#include "..." search starts here:
+#include <...> search starts here:
+ .
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include
+ /usr/local/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include
+ /usr/include
+End of search list.
+[2/4] /tmp/cmake-build/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/cmTC_59e9e.dir/FortranDependInfo.json --lang=Fortran --dd=CMakeFiles/cmTC_59e9e.dir/Fortran.dd @CMakeFiles/cmTC_59e9e.dir/Fortran.dd.rsp
+[3/4] /opt/intel/oneapi/compiler/2023.2.1/linux/bin/ifx  -I/tmp/cmake/Modules -v -c CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f -o CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o
+ifx version 2023.2.0
+/opt/intel/oneapi/compiler/2023.2.1/linux/bin-llvm/xfortcom    -triple x86_64-unknown-linux-gnu -emit-obj "-verify nomodule" -main-file-name CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f -mllvm --relocation-model=static "-options-string -I/tmp/cmake/Modules -v -c -o CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o " -mframe-pointer=none -omp_simd -target-cpu x86-64 -target-linker-version 2.30 -fveclib=SVML -mllvm -loopopt=1 -floopopt-pipeline=light -mllvm -disable-hir-generate-mkl-call -mllvm -paropt=11 -fintel-libirc-allowed -o CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o -D__INTEL_LLVM_COMPILER=20230200 -D__INTEL_LLVM_COMPILER_UPDATE=0 -D__unix__ -D__unix -D__linux__ -D__linux -D__gnu_linux__ -Dunix -Dlinux -D__ELF__ -D__x86_64 -D__x86_64__ -D__amd64 -D__amd64__ -D__INTEL_COMPILER_BUILD_DATE=20230721 -D__INTEL_COMPILER=20230200 -D__INTEL_COMPILER_UPDATE=0 -ICMakeFiles/cmTC_59e9e.dir -I. -I/tmp/cmake/Modules -I/opt/intel/oneapi/tbb/2021.10.0/env/../include -I/opt/intel/oneapi/mpi/2021.10.0//include -I/opt/intel/oneapi/mkl/2023.2.0/include -I/opt/intel/oneapi/dpcpp-ct/2023.2.0/include -I/opt/intel/oneapi/dev-utilities/2021.10.0/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64 -I/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include -I/usr/local/include -I/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include -I/usr/include -omp_simd -O2 "-reentrancy threaded" "-unroll 2" -simd -offload_host CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F-pp.f
+#include "..." search starts here:
+#include <...> search starts here:
+ CMakeFiles/cmTC_59e9e.dir
+ .
+ /tmp/cmake/Modules
+ /opt/intel/oneapi/tbb/2021.10.0/env/../include
+ /opt/intel/oneapi/mpi/2021.10.0//include
+ /opt/intel/oneapi/mkl/2023.2.0/include
+ /opt/intel/oneapi/dpcpp-ct/2023.2.0/include
+ /opt/intel/oneapi/dev-utilities/2021.10.0/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64
+ /opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include
+ /usr/local/include
+ /opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include
+ /usr/include
+End of search list.
+[4/4] : && /opt/intel/oneapi/compiler/2023.2.1/linux/bin/ifx -v  CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o -o cmTC_59e9e   && :
+ifx version 2023.2.0
+ld    /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/8/crtbegin.o --eh-frame-hdr --build-id -dynamic-linker /lib64/ld-linux-x86-64.so.2 -m elf_x86_64 -o cmTC_59e9e /opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/for_main.o -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8 -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib -L/opt/intel/oneapi/mpi/2021.10.0//lib/release -L/opt/intel/oneapi/mpi/2021.10.0//lib -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64 -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/lib/linux -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/lib/x86_64-unknown-linux-gnu -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib -L/usr/lib/gcc/x86_64-redhat-linux/8/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/ -L/lib/../lib64 -L/lib/../lib64/ -L/usr/lib/../lib64 -L/usr/lib/../lib64/ -L/opt/intel/oneapi/tbb/2021.10.0/env/../lib/intel64/gcc4.8/ -L/opt/intel/oneapi/mpi/2021.10.0//libfabric/lib/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/release/ -L/opt/intel/oneapi/mpi/2021.10.0//lib/ -L/opt/intel/oneapi/mkl/2023.2.0/lib/intel64/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin/ -L/opt/intel/oneapi/compiler/2023.2.1/linux/lib/ -L/usr/lib/gcc/x86_64-redhat-linux/8/../../../ -L/lib64 -L/lib/ -L/usr/lib64 -L/usr/lib CMakeFiles/cmTC_59e9e.dir/CMakeFortranCompilerABI.F.o -Bdynamic -Bstatic -lifport -lifcoremt -Bdynamic -limf -Bstatic -lsvml -Bdynamic -lm -Bstatic -lipgo -lirc -Bdynamic -lpthread -Bstatic -lsvml -Bdynamic -lc -lgcc -lgcc_s -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib/gcc/x86_64-redhat-linux/8/crtend.o /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crtn.o
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-C-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-C-GNU-5.5.0.input
new file mode 100644
index 0000000..39d9f79
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-C-GNU-5.5.0.input
@@ -0,0 +1,66 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/ccs/bin/ld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=GNU
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=5.5.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/build/bin/cmake -E env VERBOSE=1 /opt/csw/bin/gmake -f Makefile cmTC_c0ced/fast
+/opt/csw/bin/gmake  -f CMakeFiles/cmTC_c0ced.dir/build.make CMakeFiles/cmTC_c0ced.dir/build
+gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building C object CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o
+/opt/csw/bin/gcc   -v -o CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o -c /tmp/cmake/Modules/CMakeCCompilerABI.c
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/gcc
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o' '-c' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/cc1 -quiet -v -D__sparcv8 /tmp/cmake/Modules/CMakeCCompilerABI.c -quiet -dumpbase CMakeCCompilerABI.c -mcpu=v9 -auxbase-strip CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o -version -o /var/tmp//ccYQRQSM.s
+GNU C11 (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU C11 (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: 02b73bce42722ebeabb3a83ac3913cb8
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o' '-c' '-mcpu=v9'
+ /usr/ccs/bin/as -V -Qy -s -xarch=v8plus -m32 -o CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o /var/tmp//ccYQRQSM.s
+/usr/ccs/bin/as: Studio 12.5 Compiler Common 12.5 SunOS_sparc Patch 05/02/2016
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o' '-c' '-mcpu=v9'
+Linking C executable cmTC_c0ced
+/tmp/cmake/build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_c0ced.dir/link.txt --verbose=1
+/opt/csw/bin/gcc  -v CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o -o cmTC_c0ced
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/gcc
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/lto-wrapper
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_c0ced' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/collect2 -V -Y P,/lib:/usr/lib -Qy -o cmTC_c0ced /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_c0ced.dir/CMakeCCompilerABI.c.o -lgcc -lgcc_eh -lc -R /opt/csw/lib -lgcc -lgcc_eh -lc -R /opt/csw/lib /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.1520
+gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-CXX-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-CXX-GNU-5.5.0.input
new file mode 100644
index 0000000..dd0799a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-CXX-GNU-5.5.0.input
@@ -0,0 +1,69 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/ccs/bin/ld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=GNU
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=5.5.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/build/bin/cmake -E env VERBOSE=1 /opt/csw/bin/gmake -f Makefile cmTC_e6422/fast
+/opt/csw/bin/gmake  -f CMakeFiles/cmTC_e6422.dir/build.make CMakeFiles/cmTC_e6422.dir/build
+gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building CXX object CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o
+/opt/csw/bin/g++   -v -o CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o -c /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/g++
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/cc1plus -quiet -v -D__sparcv8 /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp -quiet -dumpbase CMakeCXXCompilerABI.cpp -mcpu=v9 -auxbase-strip CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o -version -o /var/tmp//ccD58BdJ.s
+GNU C++ (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../include/c++/5.5.0
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../include/c++/5.5.0/sparc-sun-solaris2.10
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../include/c++/5.5.0/backward
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU C++ (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: 0fb2c410667dff80f2823486f487547c
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mcpu=v9'
+ /usr/ccs/bin/as -V -Qy -s -xarch=v8plus -m32 -o CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o /var/tmp//ccD58BdJ.s
+/usr/ccs/bin/as: Studio 12.5 Compiler Common 12.5 SunOS_sparc Patch 05/02/2016
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mcpu=v9'
+Linking CXX executable cmTC_e6422
+/tmp/cmake/build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_e6422.dir/link.txt --verbose=1
+/opt/csw/bin/g++  -v CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_e6422
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/g++
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/lto-wrapper
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_e6422' '-shared-libgcc' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/collect2 -V -M /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../libgcc-unwind.map -Y P,/lib:/usr/lib -Qy -o cmTC_e6422 /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_e6422.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lrt -lgcc_s -lgcc -lc -R /opt/csw/lib -lgcc_s -lgcc -lc -R /opt/csw/lib /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.1520
+gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-Fortran-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-Fortran-GNU-5.5.0.input
new file mode 100644
index 0000000..9d62b1a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.10_sparc32-Fortran-GNU-5.5.0.input
@@ -0,0 +1,70 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=/usr/ccs/bin/ld
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=GNU
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=5.5.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): /tmp/cmake/build/bin/cmake -E env VERBOSE=1 /opt/csw/bin/gmake -f Makefile cmTC_955e5/fast
+/opt/csw/bin/gmake  -f CMakeFiles/cmTC_955e5.dir/build.make CMakeFiles/cmTC_955e5.dir/build
+gmake[1]: Entering directory '/tmp/ii/CMakeFiles/CMakeTmp'
+Building Fortran object CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o
+/opt/csw/bin/gfortran   -v -c /tmp/cmake/Modules/CMakeFortranCompilerABI.F -o CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/gfortran
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/f951 /tmp/cmake/Modules/CMakeFortranCompilerABI.F -ffixed-form -cpp=/var/tmp//ccIEY4SI.f90 -quiet -v -D__sparcv8 /tmp/cmake/Modules/CMakeFortranCompilerABI.F -quiet -dumpbase CMakeFortranCompilerABI.F -mcpu=v9 -auxbase-strip CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o -version -fintrinsic-modules-path /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/finclude -o /var/tmp//cccnvwRh.s
+GNU Fortran (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/finclude
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU Fortran2008 (GCC) version 5.5.0 (sparc-sun-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o' '-mcpu=v9'
+ /usr/ccs/bin/as -V -Qy -s -xarch=v8plus -m32 -o CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o /var/tmp//cccnvwRh.s
+/usr/ccs/bin/as: Studio 12.5 Compiler Common 12.5 SunOS_sparc Patch 05/02/2016
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o' '-mcpu=v9'
+Linking Fortran executable cmTC_955e5
+/tmp/cmake/build/bin/cmake -E cmake_link_script CMakeFiles/cmTC_955e5.dir/link.txt --verbose=1
+/opt/csw/bin/gfortran  -v CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o -o cmTC_955e5
+Driving: /opt/csw/bin/gfortran -v CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o -o cmTC_955e5 -l gfortran -l m -shared-libgcc
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/bin/gfortran
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/lto-wrapper
+Target: sparc-sun-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-sparc/build-isa-sparcv8plus/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-as=/usr/ccs/bin/as --without-gnu-as
+Thread model: posix
+gcc version 5.5.0 (GCC)
+Reading specs from /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../libgfortran.spec
+rename spec lib to liborig
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_955e5' '-shared-libgcc' '-mcpu=v9'
+COMPILER_PATH=/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib/:/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_955e5' '-shared-libgcc' '-mcpu=v9'
+ /opt/csw/libexec/gcc/sparc-sun-solaris2.10/5.5.0/collect2 -V -M /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../libgcc-unwind.map -Y P,/lib:/usr/lib -Qy -o cmTC_955e5 /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../../../sparc-sun-solaris2.10/lib -L/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_955e5.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc -lm -lgcc_s -lgcc -lc -R /opt/csw/lib -lgcc_s -lgcc -lm -lgcc_s -lgcc -lc -R /opt/csw/lib /opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.1520
+gmake[1]: Leaving directory '/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-C-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-C-GNU-5.5.0.input
new file mode 100644
index 0000000..0fc6809
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-C-GNU-5.5.0.input
@@ -0,0 +1,66 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=GNU
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=5.5.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): "/tmp/cmake/build/bin/cmake" -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_669bd/fast
+/usr/bin/gmake  -f CMakeFiles/cmTC_669bd.dir/build.make CMakeFiles/cmTC_669bd.dir/build
+gmake[1]: Entering directory `/tmp/ii/CMakeFiles/CMakeTmp'
+Building C object CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o
+/opt/csw/gcc5/bin/gcc   -v -o CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o -c "/tmp/cmake/Modules/CMakeCCompilerABI.c"
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/gcc
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=pentium4'
+ /opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/cc1 -quiet -v /tmp/cmake/Modules/CMakeCCompilerABI.c -quiet -dumpbase CMakeCCompilerABI.c -mtune=generic -march=pentium4 -auxbase-strip CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o -version -o /var/tmp//ccnKHDt6.s
+GNU C11 (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU C11 (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: e033aa0b1a20b1a1618cfcdc112301a7
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=pentium4'
+ /opt/csw/bin/gas -v -V -Qy -s --32 -o CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o /var/tmp//ccnKHDt6.s
+GNU assembler version 2.24 (i386-pc-solaris2.10) using BFD version (GNU Binutils) 2.24
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o' '-c' '-mtune=generic' '-march=pentium4'
+Linking C executable cmTC_669bd
+"/tmp/cmake/build/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_669bd.dir/link.txt --verbose=1
+/opt/csw/gcc5/bin/gcc  -v CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o -o cmTC_669bd
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/gcc
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/lto-wrapper
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_669bd' '-mtune=generic' '-march=pentium4'
+ /usr/ccs/bin/ld -V -Y P,/lib:/usr/lib -Qy -o cmTC_669bd /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_669bd.dir/CMakeCCompilerABI.c.o -lgcc -lgcc_eh -lc -R /opt/csw/lib -lgcc -lgcc_eh /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.11-1.2458
+gmake[1]: Leaving directory `/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-CXX-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-CXX-GNU-5.5.0.input
new file mode 100644
index 0000000..0f0c712
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-CXX-GNU-5.5.0.input
@@ -0,0 +1,69 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=GNU
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=5.5.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): "/tmp/cmake/build/bin/cmake" -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_1c2eb/fast
+/usr/bin/gmake  -f CMakeFiles/cmTC_1c2eb.dir/build.make CMakeFiles/cmTC_1c2eb.dir/build
+gmake[1]: Entering directory `/tmp/ii/CMakeFiles/CMakeTmp'
+Building CXX object CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o
+/opt/csw/gcc5/bin/g++   -v -o CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o -c "/tmp/cmake/Modules/CMakeCXXCompilerABI.cpp"
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/g++
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+ /opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/cc1plus -quiet -v /tmp/cmake/Modules/CMakeCXXCompilerABI.cpp -quiet -dumpbase CMakeCXXCompilerABI.cpp -mtune=generic -march=pentium4 -auxbase-strip CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o -version -o /var/tmp//cca5Tfac.s
+GNU C++ (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../include/c++/5.5.0
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../include/c++/5.5.0/i386-pc-solaris2.10
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../include/c++/5.5.0/backward
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU C++ (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+Compiler executable checksum: d6737be2d7a7f1ff0cf51e522387e31b
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+ /opt/csw/bin/gas -v -V -Qy -s --32 -o CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o /var/tmp//cca5Tfac.s
+GNU assembler version 2.24 (i386-pc-solaris2.10) using BFD version (GNU Binutils) 2.24
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o' '-c' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+Linking CXX executable cmTC_1c2eb
+"/tmp/cmake/build/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_1c2eb.dir/link.txt --verbose=1
+/opt/csw/gcc5/bin/g++  -v CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_1c2eb
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/g++
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/lto-wrapper
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_1c2eb' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+ /usr/ccs/bin/ld -V -M /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../libgcc-unwind.map -Y P,/lib:/usr/lib -Qy -o cmTC_1c2eb /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_1c2eb.dir/CMakeCXXCompilerABI.cpp.o -lstdc++ -lm -lrt -lgcc_s -lgcc -lc -R /opt/csw/lib -lgcc_s -lgcc /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.11-1.2458
+gmake[1]: Leaving directory `/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-Fortran-GNU-5.5.0.input b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-Fortran-GNU-5.5.0.input
new file mode 100644
index 0000000..bcd5114
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/sunos5.11_i386-Fortran-GNU-5.5.0.input
@@ -0,0 +1,70 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=/usr/bin/ld
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=/opt/csw/bin/gcc-ar-5.5
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=GNU
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=/opt/csw/bin/gcc-ranlib-5.5
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=5.5.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+Change Dir: '/tmp/ii/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): "/tmp/cmake/build/bin/cmake" -E env VERBOSE=1 /usr/bin/gmake -f Makefile cmTC_437d4/fast
+/usr/bin/gmake  -f CMakeFiles/cmTC_437d4.dir/build.make CMakeFiles/cmTC_437d4.dir/build
+gmake[1]: Entering directory `/tmp/ii/CMakeFiles/CMakeTmp'
+Building Fortran object CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o
+/opt/csw/gcc5/bin/gfortran   -v -c "/tmp/cmake/Modules/CMakeFortranCompilerABI.F" -o CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/gfortran
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=pentium4'
+ /opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/f951 /tmp/cmake/Modules/CMakeFortranCompilerABI.F -ffixed-form -cpp=/var/tmp//ccq6gvEa.f90 -quiet -v /tmp/cmake/Modules/CMakeFortranCompilerABI.F -quiet -dumpbase CMakeFortranCompilerABI.F -mtune=generic -march=pentium4 -auxbase-strip CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o -version -fintrinsic-modules-path /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/finclude -o /var/tmp//ccSeT27a.s
+GNU Fortran (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+ignoring nonexistent directory "/usr/local/include"
+ignoring nonexistent directory "/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/finclude
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include
+ /opt/csw/include
+ /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed
+ /usr/include
+End of search list.
+GNU Fortran2008 (GCC) version 5.5.0 (i386-pc-solaris2.10)
+	compiled by GNU C version 5.5.0, GMP version 6.1.2, MPFR version 3.1.5, MPC version 1.0.2
+GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=pentium4'
+ /opt/csw/bin/gas -v -V -Qy -s --32 -o CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o /var/tmp//ccSeT27a.s
+GNU assembler version 2.24 (i386-pc-solaris2.10) using BFD version (GNU Binutils) 2.24
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-c' '-o' 'CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o' '-mtune=generic' '-march=pentium4'
+Linking Fortran executable cmTC_437d4
+"/tmp/cmake/build/bin/cmake" -E cmake_link_script CMakeFiles/cmTC_437d4.dir/link.txt --verbose=1
+/opt/csw/gcc5/bin/gfortran  -v CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o -o cmTC_437d4
+Driving: /opt/csw/gcc5/bin/gfortran -v CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o -o cmTC_437d4 -l gfortran -l m -shared-libgcc
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/specs
+COLLECT_GCC=/opt/csw/gcc5/bin/gfortran
+COLLECT_LTO_WRAPPER=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/lto-wrapper
+Target: i386-pc-solaris2.10
+Configured with: /home/dam/mgar/pkg/gcc5/trunk/work/solaris10-i386/build-isa-pentium_pro/gcc-5.5.0/configure --prefix=/opt/csw --exec_prefix=/opt/csw --bindir=/opt/csw/bin --sbindir=/opt/csw/sbin --libexecdir=/opt/csw/libexec --datadir=/opt/csw/share --sysconfdir=/etc/opt/csw --sharedstatedir=/opt/csw/share --localstatedir=/var/opt/csw --libdir=/opt/csw/lib --infodir=/opt/csw/share/info --includedir=/opt/csw/include --mandir=/opt/csw/share/man --enable-cloog-backend=isl --enable-java-awt=xlib --enable-languages=ada,c,c++,fortran,go,java,objc --enable-libada --enable-libssp --enable-nls --enable-objc-gc --enable-threads=posix --program-suffix=-5.5 --with-cloog=/opt/csw --with-gmp=/opt/csw --with-included-gettext --with-ld=/usr/ccs/bin/ld --without-gnu-ld --with-libiconv-prefix=/opt/csw --with-mpfr=/opt/csw --with-ppl=/opt/csw --with-system-zlib=/opt/csw --with-gnu-as --with-as=/opt/csw/bin/gas
+Thread model: posix
+gcc version 5.5.0 (GCC)
+Reading specs from /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../libgfortran.spec
+rename spec lib to liborig
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_437d4' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+COMPILER_PATH=/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/libexec/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/bin/:/usr/ccs/bin/
+LIBRARY_PATH=/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib/:/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../:/lib/:/usr/lib/
+COLLECT_GCC_OPTIONS='-v' '-o' 'cmTC_437d4' '-shared-libgcc' '-mtune=generic' '-march=pentium4'
+ /usr/ccs/bin/ld -V -M /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../libgcc-unwind.map -Y P,/lib:/usr/lib -Qy -o cmTC_437d4 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/values-Xa.o /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtbegin.o -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0 -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../../../i386-pc-solaris2.10/lib -L/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/../../.. CMakeFiles/cmTC_437d4.dir/CMakeFortranCompilerABI.F.o -lgfortran -lm -lgcc_s -lgcc -lquadmath -lm -lgcc_s -lgcc -lc -R /opt/csw/lib -lgcc_s -lgcc /opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/crtend.o /usr/lib/crtn.o
+ld: Software Generation Utilities - Solaris Link Editors: 5.11-1.2458
+gmake[1]: Leaving directory `/tmp/ii/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Intel-2021.9.0.20230302.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Intel-2021.9.0.20230302.input
new file mode 100644
index 0000000..a54d54b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Intel-2021.9.0.20230302.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=C
+CMAKE_LINKER=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=Intel
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=2021.9.0.20230302
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_250e1
+[1/2] C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\icl.exe  /nologo   /DWIN32 /D_WINDOWS /W3  /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_250e1.dir\CMakeCCompilerABI.c.obj /FdCMakeFiles\cmTC_250e1.dir\ -c "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_250e1.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\xilink.exe /nologo CMakeFiles\cmTC_250e1.dir\CMakeCCompilerABI.c.obj  /out:cmTC_250e1.exe /implib:cmTC_250e1.lib /pdb:cmTC_250e1.pdb /version:0.0 /machine:x64  /debug /INCREMENTAL /subsystem:console   && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-IntelLLVM-2023.1.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-IntelLLVM-2023.1.0.input
new file mode 100644
index 0000000..710892c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-IntelLLVM-2023.1.0.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=C
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=C:/DoesNotExist/intel/compiler/latest/windows/bin-llvm/llvm-ar.exe
+CMAKE_C_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=IntelLLVM
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/llvm-ranlib.exe
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=2023.1.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_084c6
+[1/2] C:\DoesNotExist\intel\compiler\latest\windows\bin\icx.exe  /nologo   /DWIN32 /D_WINDOWS /W3  /MDd /Zi /Ob0 /Od /RTC1 -QMD -QMT CMakeFiles\cmTC_084c6.dir\CMakeCCompilerABI.c.obj -QMF CMakeFiles\cmTC_084c6.dir\CMakeCCompilerABI.c.obj.d /FoCMakeFiles\cmTC_084c6.dir\CMakeCCompilerABI.c.obj /FdCMakeFiles\cmTC_084c6.dir\ -c "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_084c6.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\DoesNotExist\intel\compiler\latest\windows\bin\icx.exe /nologo CMakeFiles\cmTC_084c6.dir\CMakeCCompilerABI.c.obj  /Qoption,link,/machine:x64  /debug /INCREMENTAL /Qoption,link,/subsystem:console   /link /out:cmTC_084c6.exe /implib:cmTC_084c6.lib /pdb:cmTC_084c6.pdb /version:0.0  && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.36.32543.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.36.32543.0.input
new file mode 100644
index 0000000..cc73ff7
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.36.32543.0.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=C
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=MSVC
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=19.36.32543.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_ccd96
+[1/2] C:\PROGRA~1\MIB055~1\2022\PROFES~1\VC\Tools\MSVC\1436~1.325\bin\Hostx64\x64\cl.exe  /nologo   /DWIN32 /D_WINDOWS /W3  /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_ccd96.dir\CMakeCCompilerABI.c.obj /FdCMakeFiles\cmTC_ccd96.dir\ /FS -c "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_ccd96.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~1\MIB055~1\2022\PROFES~1\VC\Tools\MSVC\1436~1.325\bin\Hostx64\x64\link.exe /nologo CMakeFiles\cmTC_ccd96.dir\CMakeCCompilerABI.c.obj  /out:cmTC_ccd96.exe /implib:cmTC_ccd96.lib /pdb:cmTC_ccd96.pdb /version:0.0 /machine:x64  /debug /INCREMENTAL /subsystem:console  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.38.33130.0-VS.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.38.33130.0-VS.input
new file mode 100644
index 0000000..77bae76
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-MSVC-19.38.33130.0-VS.input
@@ -0,0 +1,48 @@
+CMAKE_LANG=C
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/link.exe
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=MSVC
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=19.38.33130.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): "C:/Program Files/Microsoft Visual Studio/2022/Professional/MSBuild/Current/Bin/amd64/MSBuild.exe" cmTC_7cc99.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:VisualStudioVersion=17.0 /v:n
+MSBuild version 17.8.3+195e7f5a3 for .NET Framework
+Build started 12/14/2023 11:53:27 AM.
+
+Project "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\cmTC_7cc99.vcxproj" on node 1 (default targets).
+PrepareForBuild:
+  Creating directory "cmTC_7cc99.dir\Debug\".
+  Structured output is enabled. The formatting of compiler diagnostics will reflect the error hierarchy. See https://aka.ms/cpp/structured-output for more details.
+  Creating directory "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\".
+  Creating directory "cmTC_7cc99.dir\Debug\cmTC_7cc99.tlog\".
+InitializeBuildStatus:
+  Creating "cmTC_7cc99.dir\Debug\cmTC_7cc99.tlog\unsuccessfulbuild" because "AlwaysCreate" was specified.
+  Touching "cmTC_7cc99.dir\Debug\cmTC_7cc99.tlog\unsuccessfulbuild".
+ClCompile:
+  C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\HostX64\x64\CL.exe /c /Zi /W3 /WX- /diagnostics:column /Od /Ob0 /D _MBCS /D WIN32 /D _WINDOWS /D "CMAKE_INTDIR=\"Debug\"" /Gm- /RTC1 /MDd /GS /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"cmTC_7cc99.dir\Debug\\" /Fd"cmTC_7cc99.dir\Debug\vc143.pdb" /external:W3 /Gd /TC /errorReport:queue "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c"
+  Microsoft (R) C/C++ Optimizing Compiler Version 19.38.33130 for x64
+  Copyright (C) Microsoft Corporation.  All rights reserved.
+  cl /c /Zi /W3 /WX- /diagnostics:column /Od /Ob0 /D _MBCS /D WIN32 /D _WINDOWS /D "CMAKE_INTDIR=\"Debug\"" /Gm- /RTC1 /MDd /GS /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"cmTC_7cc99.dir\Debug\\" /Fd"cmTC_7cc99.dir\Debug\vc143.pdb" /external:W3 /Gd /TC /errorReport:queue "C:\DoesNotExist\CMake\Modules\CMakeCCompilerABI.c"
+  CMakeCCompilerABI.c
+Link:
+  C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\HostX64\x64\link.exe /ERRORREPORT:QUEUE /OUT:"C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\cmTC_7cc99.exe" /INCREMENTAL /ILK:"cmTC_7cc99.dir\Debug\cmTC_7cc99.ilk" /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG /PDB:"C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp/Debug/cmTC_7cc99.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp/Debug/cmTC_7cc99.lib" /MACHINE:X64  /machine:x64 cmTC_7cc99.dir\Debug\CMakeCCompilerABI.obj
+  cmTC_7cc99.vcxproj -> C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\cmTC_7cc99.exe
+FinalizeBuildStatus:
+  Deleting file "cmTC_7cc99.dir\Debug\cmTC_7cc99.tlog\unsuccessfulbuild".
+  Touching "cmTC_7cc99.dir\Debug\cmTC_7cc99.tlog\cmTC_7cc99.lastbuildstate".
+Done Building Project "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\cmTC_7cc99.vcxproj" (default targets).
+
+Build succeeded.
+    0 Warning(s)
+    0 Error(s)
+
+Time Elapsed 00:00:00.86
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Intel-2021.9.0.20230302.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Intel-2021.9.0.20230302.input
new file mode 100644
index 0000000..ecb3f5c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Intel-2021.9.0.20230302.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=Intel
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=2021.9.0.20230302
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_3490c
+[1/2] C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\icl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /W3 /GR /EHsc  /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_3490c.dir\CMakeCXXCompilerABI.cpp.obj /FdCMakeFiles\cmTC_3490c.dir\ -c "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_3490c.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\xilink.exe /nologo CMakeFiles\cmTC_3490c.dir\CMakeCXXCompilerABI.cpp.obj  /out:cmTC_3490c.exe /implib:cmTC_3490c.lib /pdb:cmTC_3490c.pdb /version:0.0 /machine:x64  /debug /INCREMENTAL /subsystem:console   && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-IntelLLVM-2023.1.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-IntelLLVM-2023.1.0.input
new file mode 100644
index 0000000..2650ba7
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-IntelLLVM-2023.1.0.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=C:/DoesNotExist/intel/compiler/latest/windows/bin-llvm/llvm-ar.exe
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=IntelLLVM
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/llvm-ranlib.exe
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=2023.1.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_3f9c2
+[1/2] C:\DoesNotExist\intel\compiler\latest\windows\bin\icx.exe  /nologo /TP   /DWIN32 /D_WINDOWS /W3 /GR /EHsc  /MDd /Zi /Ob0 /Od /RTC1 -QMD -QMT CMakeFiles\cmTC_3f9c2.dir\CMakeCXXCompilerABI.cpp.obj -QMF CMakeFiles\cmTC_3f9c2.dir\CMakeCXXCompilerABI.cpp.obj.d /FoCMakeFiles\cmTC_3f9c2.dir\CMakeCXXCompilerABI.cpp.obj /FdCMakeFiles\cmTC_3f9c2.dir\ -c "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_3f9c2.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\DoesNotExist\intel\compiler\latest\windows\bin\icx.exe /nologo CMakeFiles\cmTC_3f9c2.dir\CMakeCXXCompilerABI.cpp.obj  /Qoption,link,/machine:x64  /debug /INCREMENTAL /Qoption,link,/subsystem:console   /link /out:cmTC_3f9c2.exe /implib:cmTC_3f9c2.lib /pdb:cmTC_3f9c2.pdb /version:0.0  && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.36.32543.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.36.32543.0.input
new file mode 100644
index 0000000..5575537
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.36.32543.0.input
@@ -0,0 +1,19 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=MSVC
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=19.36.32543.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_bb116
+[1/2] C:\PROGRA~1\MIB055~1\2022\PROFES~1\VC\Tools\MSVC\1436~1.325\bin\Hostx64\x64\cl.exe  /nologo /TP   /DWIN32 /D_WINDOWS /W3 /GR /EHsc  /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /FoCMakeFiles\cmTC_bb116.dir\CMakeCXXCompilerABI.cpp.obj /FdCMakeFiles\cmTC_bb116.dir\ /FS -c "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp"
+[2/2] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_bb116.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100190~1.0\x64\mt.exe --manifests  -- C:\PROGRA~1\MIB055~1\2022\PROFES~1\VC\Tools\MSVC\1436~1.325\bin\Hostx64\x64\link.exe /nologo CMakeFiles\cmTC_bb116.dir\CMakeCXXCompilerABI.cpp.obj  /out:cmTC_bb116.exe /implib:cmTC_bb116.lib /pdb:cmTC_bb116.pdb /version:0.0 /machine:x64  /debug /INCREMENTAL /subsystem:console  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.input
new file mode 100644
index 0000000..16d1b37
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.input
@@ -0,0 +1,48 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/link.exe
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=MSVC
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=19.38.33130.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): "C:/Program Files/Microsoft Visual Studio/2022/Professional/MSBuild/Current/Bin/amd64/MSBuild.exe" cmTC_29675.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:VisualStudioVersion=17.0 /v:n
+MSBuild version 17.8.3+195e7f5a3 for .NET Framework
+Build started 12/14/2023 11:53:28 AM.
+
+Project "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\cmTC_29675.vcxproj" on node 1 (default targets).
+PrepareForBuild:
+  Creating directory "cmTC_29675.dir\Debug\".
+  Structured output is enabled. The formatting of compiler diagnostics will reflect the error hierarchy. See https://aka.ms/cpp/structured-output for more details.
+  Creating directory "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\".
+  Creating directory "cmTC_29675.dir\Debug\cmTC_29675.tlog\".
+InitializeBuildStatus:
+  Creating "cmTC_29675.dir\Debug\cmTC_29675.tlog\unsuccessfulbuild" because "AlwaysCreate" was specified.
+  Touching "cmTC_29675.dir\Debug\cmTC_29675.tlog\unsuccessfulbuild".
+ClCompile:
+  C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\HostX64\x64\CL.exe /c /Zi /W3 /WX- /diagnostics:column /Od /Ob0 /D _MBCS /D WIN32 /D _WINDOWS /D "CMAKE_INTDIR=\"Debug\"" /Gm- /EHsc /RTC1 /MDd /GS /Zc:wchar_t /Zc:forScope /Zc:inline /GR /Fo"cmTC_29675.dir\Debug\\" /Fd"cmTC_29675.dir\Debug\vc143.pdb" /external:W3 /Gd /TP /errorReport:queue "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp"
+  Microsoft (R) C/C++ Optimizing Compiler Version 19.38.33130 for x64
+  Copyright (C) Microsoft Corporation.  All rights reserved.
+  cl /c /Zi /W3 /WX- /diagnostics:column /Od /Ob0 /D _MBCS /D WIN32 /D _WINDOWS /D "CMAKE_INTDIR=\"Debug\"" /Gm- /EHsc /RTC1 /MDd /GS /Zc:wchar_t /Zc:forScope /Zc:inline /GR /Fo"cmTC_29675.dir\Debug\\" /Fd"cmTC_29675.dir\Debug\vc143.pdb" /external:W3 /Gd /TP /errorReport:queue "C:\DoesNotExist\CMake\Modules\CMakeCXXCompilerABI.cpp"
+  CMakeCXXCompilerABI.cpp
+Link:
+  C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\HostX64\x64\link.exe /ERRORREPORT:QUEUE /OUT:"C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\cmTC_29675.exe" /INCREMENTAL /ILK:"cmTC_29675.dir\Debug\cmTC_29675.ilk" /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG /PDB:"C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp/Debug/cmTC_29675.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp/Debug/cmTC_29675.lib" /MACHINE:X64  /machine:x64 cmTC_29675.dir\Debug\CMakeCXXCompilerABI.obj
+  cmTC_29675.vcxproj -> C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\Debug\cmTC_29675.exe
+FinalizeBuildStatus:
+  Deleting file "cmTC_29675.dir\Debug\cmTC_29675.tlog\unsuccessfulbuild".
+  Touching "cmTC_29675.dir\Debug\cmTC_29675.tlog\cmTC_29675.lastbuildstate".
+Done Building Project "C:\DoesNotExist\Temp\CMakeFiles\CMakeTmp\cmTC_29675.vcxproj" (default targets).
+
+Build succeeded.
+    0 Warning(s)
+    0 Error(s)
+
+Time Elapsed 00:00:00.92
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-Intel-2021.9.0.20230302.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-Intel-2021.9.0.20230302.input
new file mode 100644
index 0000000..75e49a6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-Intel-2021.9.0.20230302.input
@@ -0,0 +1,21 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=Intel
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=2021.9.0.20230302
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_40ac9
+[1/4] C:\Windows\system32\cmd.exe /C "C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\ifort.exe -fpp   /W1 /nologo /fpp /libs:dll /threads  /Od /debug:full /dbglibs -P "C:\DoesNotExist\CMake\Modules\CMakeFortranCompilerABI.F" -FiCMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F-pp.f && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E cmake_ninja_depends --tdi=CMakeFiles\cmTC_40ac9.dir\FortranDependInfo.json --lang=Fortran --src=CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F-pp.f --out=CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F-pp.f --dep=CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F-pp.f.d --obj=CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F.obj --ddi=CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F.obj.ddi"
+[2/4] "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E cmake_ninja_dyndep --tdi=CMakeFiles\cmTC_40ac9.dir\FortranDependInfo.json --lang=Fortran --dd=CMakeFiles\cmTC_40ac9.dir\Fortran.dd @CMakeFiles\cmTC_40ac9.dir\Fortran.dd.rsp
+[3/4] C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\ifort.exe  /nologo /fpp  -I"C:\DoesNotExist\CMake\Modules" /W1 /nologo /fpp /libs:dll /threads  /Od /debug:full /dbglibs /FoCMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F.obj /Fd -c CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F-pp.f
+[4/4] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_40ac9.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt="" --manifests  -- C:\DoesNotExist\Intel\oneAPI\compiler\latest\windows\bin\intel64\xilink.exe /nologo CMakeFiles\cmTC_40ac9.dir\CMakeFortranCompilerABI.F.obj  /out:cmTC_40ac9.exe /implib:cmTC_40ac9.lib /pdb:cmTC_40ac9.exe.dbg /version:0.0 /machine:x64  /debug /INCREMENTAL /subsystem:console   && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-IntelLLVM-2023.1.0.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-IntelLLVM-2023.1.0.input
new file mode 100644
index 0000000..e8e0d1a
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-IntelLLVM-2023.1.0.input
@@ -0,0 +1,21 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=C:/DoesNotExist/intel/compiler/latest/windows/bin-llvm/llvm-ar.exe
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=IntelLLVM
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/Llvm/x64/bin/llvm-ranlib.exe
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=2023.1.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+CMAKE_HOST_SYSTEM_NAME=Windows
+Change Dir: 'C:/DoesNotExist/Temp/CMakeFiles/CMakeTmp'
+
+Run Build Command(s): C:/DoesNotExist/ninja/bin/ninja.exe -v cmTC_26e9b
+[1/4] C:\Windows\system32\cmd.exe /C "C:\DoesNotExist\intel\compiler\latest\windows\bin\ifx.exe -fpp   /W1 /nologo /fpp /libs:dll /threads  /Od /debug:full /dbglibs -P "C:\DoesNotExist\CMake\Modules\CMakeFortranCompilerABI.F" -FiCMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F-pp.f && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E cmake_ninja_depends --tdi=CMakeFiles\cmTC_26e9b.dir\FortranDependInfo.json --lang=Fortran --src=CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F-pp.f --out=CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F-pp.f --dep=CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F-pp.f.d --obj=CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F.obj --ddi=CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F.obj.ddi"
+[2/4] "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E cmake_ninja_dyndep --tdi=CMakeFiles\cmTC_26e9b.dir\FortranDependInfo.json --lang=Fortran --dd=CMakeFiles\cmTC_26e9b.dir\Fortran.dd @CMakeFiles\cmTC_26e9b.dir\Fortran.dd.rsp
+[3/4] C:\DoesNotExist\intel\compiler\latest\windows\bin\ifx.exe  /nologo /fpp  -I"C:\DoesNotExist\CMake\Modules" /W1 /nologo /fpp /libs:dll /threads  /Od /debug:full /dbglibs /FoCMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F.obj /Fd -c CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F-pp.f
+[4/4] C:\Windows\system32\cmd.exe /C "cd . && "C:\DoesNotExist\CMake\build\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_26e9b.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt="" --manifests  -- C:\DoesNotExist\intel\compiler\latest\windows\bin\ifx.exe /nologo CMakeFiles\cmTC_26e9b.dir\CMakeFortranCompilerABI.F.obj  /Qoption,link,/machine:x64  /debug /INCREMENTAL /Qoption,link,/subsystem:console   /link /out:cmTC_26e9b.exe /implib:cmTC_26e9b.lib /pdb:cmTC_26e9b.exe.dbg /version:0.0  && cd ."
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input
new file mode 100644
index 0000000..c567f06
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=LLVMFlang
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=18.0.0
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+CMAKE_Fortran_SIMULATE_ID=MSVC
+flang-new version 18.0.0
+Target: x86_64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\flang-new" -fc1 -triple x86_64-pc-windows-msvc19.36.32543 -emit-obj -mrelocation-model pic -pic-level 2 -target-cpu x86-64 --dependent-lib=clang_rt.builtins-x86_64.lib -D_MT --dependent-lib=libcmt --dependent-lib=Fortran_main.static.lib --dependent-lib=FortranRuntime.static.lib --dependent-lib=FortranDecimal.static.lib -D_MSC_VER=1936 -D_MSC_FULL_VER=193632543 -D_WIN32 -D_M_X64=100 -mframe-pointer=none -o "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-e91f95.o" -x f95-cpp-input CMakeFortranCompilerABI.F
+ "C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC\\14.36.32532\\bin\\Hostx64\\x64\\link.exe" -out:a.exe "-libpath:C:\\DoesNotExist\\LLVM\\lib" /WHOLEARCHIVE:Fortran_main.static.lib /subsystem:console "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\18\\lib\\windows" -nologo "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-e91f95.o"
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
index 6027f03..c5bb5d7 100644
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
@@ -8,6 +8,7 @@
 set(targets
   aix-C-XL-13.1.3 aix-CXX-XL-13.1.3
   aix-C-XLClang-16.1.0.1 aix-CXX-XLClang-16.1.0.1
+  aix-C-IBMClang-17.1.1.2 aix-CXX-IBMClang-17.1.1.2
   craype-C-Cray-8.7 craype-CXX-Cray-8.7 craype-Fortran-Cray-8.7
   craype-C-Cray-9.0-hlist-ad craype-CXX-Cray-9.0-hlist-ad craype-Fortran-Cray-9.0-hlist-ad
   craype-C-GNU-7.3.0 craype-CXX-GNU-7.3.0 craype-Fortran-GNU-7.3.0
@@ -23,7 +24,10 @@
   linux-C-GNU-10.2.1-static-libgcc
     linux-CXX-GNU-10.2.1-static-libstdc++
     linux-Fortran-GNU-10.2.1-static-libgfortran
+  linux-C-GNU-12.2.0 linux-CXX-GNU-12.2.0 linux-Fortran-GNU-12.2.0
   linux-C-Intel-18.0.0.20170811 linux-CXX-Intel-18.0.0.20170811
+  linux-C-Intel-2021.10.0.20230609 linux-CXX-Intel-2021.10.0.20230609 linux-Fortran-Intel-2021.10.0.20230609
+  linux-C-IntelLLVM-2023.2.0 linux-CXX-IntelLLVM-2023.2.0 linux-Fortran-IntelLLVM-2023.2.0
   linux-C-PGI-18.10.1 linux-CXX-PGI-18.10.1
     linux-Fortran-PGI-18.10.1 linux_pgf77-Fortran-PGI-18.10.1
     linux_nostdinc-C-PGI-18.10.1 linux_nostdinc-CXX-PGI-18.10.1
@@ -41,6 +45,8 @@
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
   openbsd-C-Clang-5.0.1 openbsd-CXX-Clang-5.0.1
   sunos-C-SunPro-5.13.0 sunos-CXX-SunPro-5.13.0 sunos-Fortran-SunPro-8.8.0
+  sunos5.10_sparc32-C-GNU-5.5.0 sunos5.10_sparc32-CXX-GNU-5.5.0 sunos5.10_sparc32-Fortran-GNU-5.5.0
+  sunos5.11_i386-C-GNU-5.5.0 sunos5.11_i386-CXX-GNU-5.5.0 sunos5.11_i386-Fortran-GNU-5.5.0
   )
 
 if(CMAKE_HOST_WIN32)
@@ -88,15 +94,6 @@
 endfunction()
 
 #
-# unload_compiler_info: clear out any CMAKE_* vars load previously set
-#
-function(unload_compiler_info cmvars)
-  foreach(var IN LISTS cmvars)
-    unset("${var}" PARENT_SCOPE)
-  endforeach()
-endfunction()
-
-#
 # main test loop
 #
 foreach(t ${targets})
@@ -113,17 +110,18 @@
     continue()
   endif()
 
-  load_compiler_info(${infile} lang cmvars input)
-  file(READ ${outfile} output)
-  string(STRIP "${output}" output)
-  cmake_parse_implicit_include_info("${input}" "${lang}" idirs log state)
+  block()
+    load_compiler_info(${infile} lang cmvars input)
+    file(READ ${outfile} output)
+    string(STRIP "${output}" output)
+    cmake_parse_implicit_include_info("${input}" "${lang}" idirs log state)
 
-  if(t MATCHES "-empty$")          # empty isn't supposed to parse
-    if("${state}" STREQUAL "done")
-      message("empty parse failed: ${idirs}, log=${log}")
+    if(t MATCHES "-empty$")          # empty isn't supposed to parse
+      if("${state}" STREQUAL "done")
+        message("empty parse failed: ${idirs}, log=${log}")
+      endif()
+    elseif(NOT "${state}" STREQUAL "done" OR NOT "${idirs}" MATCHES "^${output}$")
+      message("${t} parse failed: state=${state}, '${idirs}' does not match '^${output}$', log=${log}")
     endif()
-  elseif(NOT "${state}" STREQUAL "done" OR NOT "${idirs}" MATCHES "^${output}$")
-    message("${t} parse failed: state=${state}, '${idirs}' does not match '^${output}$', log=${log}")
-  endif()
-  unload_compiler_info("${cmvars}")
+  endblock()
 endforeach(t)
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-IBMClang-17.1.1.2.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-IBMClang-17.1.1.2.output
new file mode 100644
index 0000000..4839eed
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-C-IBMClang-17.1.1.2.output
@@ -0,0 +1 @@
+/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers;/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include;/usr/include;/opt/IBM/xlmass/10.1.1/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-IBMClang-17.1.1.2.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-IBMClang-17.1.1.2.output
new file mode 100644
index 0000000..8831d58
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/aix-CXX-IBMClang-17.1.1.2.output
@@ -0,0 +1 @@
+/opt/IBM/openxlC/17.1.1/include/c\+\+/v1;/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include/ppc_wrappers;/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/include;/usr/include;/opt/IBM/xlmass/10.1.1/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-GNU-12.2.0.output
new file mode 100644
index 0000000..d0fd9ee
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-GNU-12.2.0.output
@@ -0,0 +1 @@
+/usr/lib/gcc/x86_64-linux-gnu/12/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..00614c1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-Intel-2021.10.0.20230609.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/icc;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/usr/local/include;/usr/lib/gcc/x86_64-redhat-linux/8/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..29600d1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-C-IntelLLVM-2023.2.0.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include;/usr/local/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-GNU-12.2.0.output
new file mode 100644
index 0000000..0005fb2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-GNU-12.2.0.output
@@ -0,0 +1 @@
+/usr/include/c\+\+/12;/usr/include/x86_64-linux-gnu/c\+\+/12;/usr/include/c\+\+/12/backward;/usr/lib/gcc/x86_64-linux-gnu/12/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..0df22c3
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-Intel-2021.10.0.20230609.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/icc;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/usr/include/c\+\+/8;/usr/include/c\+\+/8/x86_64-redhat-linux;/usr/include/c\+\+/8/backward;/usr/local/include;/usr/lib/gcc/x86_64-redhat-linux/8/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..dcce0c6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-CXX-IntelLLVM-2023.2.0.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/usr/include/c\+\+/8;/usr/include/c\+\+/8/x86_64-redhat-linux;/usr/include/c\+\+/8/backward;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include;/usr/local/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-12.2.0.output
new file mode 100644
index 0000000..d0fd9ee
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-GNU-12.2.0.output
@@ -0,0 +1 @@
+/usr/lib/gcc/x86_64-linux-gnu/12/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..00614c1
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/icc;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/usr/local/include;/usr/lib/gcc/x86_64-redhat-linux/8/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..e447350
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output
@@ -0,0 +1 @@
+/opt/intel/oneapi/tbb/2021.10.0/include;/opt/intel/oneapi/mpi/2021.10.0/include;/opt/intel/oneapi/mkl/2023.2.0/include;/opt/intel/oneapi/dpcpp-ct/2023.2.0/include;/opt/intel/oneapi/dev-utilities/2021.10.0/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/oclfpga/include;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/include;/usr/local/include;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/include;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output
new file mode 100644
index 0000000..b2c73fb
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output
new file mode 100644
index 0000000..a22ced0
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/include/c\+\+/5.5.0;/opt/csw/include/c\+\+/5.5.0/sparc-sun-solaris2.10;/opt/csw/include/c\+\+/5.5.0/backward;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output
new file mode 100644
index 0000000..0488c01
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/finclude;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-C-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-C-GNU-5.5.0.output
new file mode 100644
index 0000000..0016671
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-C-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output
new file mode 100644
index 0000000..60dfb81
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/include/c\+\+/5.5.0;/opt/csw/include/c\+\+/5.5.0/i386-pc-solaris2.10;/opt/csw/include/c\+\+/5.5.0/backward;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output
new file mode 100644
index 0000000..16673a5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output
@@ -0,0 +1 @@
+/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/finclude;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include;/opt/csw/include;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0/include-fixed;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/DetermineLinkerId.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/DetermineLinkerId.cmake
new file mode 100644
index 0000000..73e5e1b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/DetermineLinkerId.cmake
@@ -0,0 +1,37 @@
+include(${CMAKE_ROOT}/Modules/Internal/CMakeDetermineLinkerId.cmake)
+
+set(tools
+  aix7.3-ld
+  debian12-ld.bfd
+  debian12-ld.gold
+  debian12-ld.lld
+  debian12-ld.mold
+  fedora39-ld.bfd
+  fedora39-ld.gold
+  fedora39-ld.lld
+  fedora39-ld.mold
+  msvc14.36-link
+  sunos5.11-ld
+  xcode15.1-ld
+  )
+
+foreach(tool IN LISTS tools)
+  block()
+    include(${CMAKE_CURRENT_LIST_DIR}/ld-v/${tool}.cmake OPTIONAL)
+    cmake_determine_linker_id(C ${CMAKE_CURRENT_LIST_DIR}/ld-v/${tool}.bash)
+    file(STRINGS ${CMAKE_CURRENT_LIST_DIR}/ld-v/${tool}.txt results)
+    foreach(result IN LISTS results)
+      if(result MATCHES "^([A-Z_]+)='([^']*)'")
+        set(expect_var "${CMAKE_MATCH_1}")
+        set(expect_val "${CMAKE_MATCH_2}")
+        if(NOT "x${${expect_var}}" STREQUAL "x${expect_val}")
+          message(SEND_ERROR "${tool} result\n"
+            "  ${expect_var}='${${expect_var}}'\n"
+            "is not expected\n"
+            "  ${expect_var}='${expect_val}'\n"
+            )
+        endif()
+      endif()
+    endforeach()
+  endblock()
+endforeach()
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/Inspect.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/Inspect.cmake
index 42e1c67..fd93baf 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/Inspect.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/Inspect.cmake
@@ -2,10 +2,17 @@
 
 set(info "")
 foreach(var
+    CMAKE_SYSTEM_NAME
+    CMAKE_C_COMPILER
+    CMAKE_C_COMPILER_ID
+    CMAKE_C_COMPILER_VERSION
+    CMAKE_C_COMPILER_LINKER
+    CMAKE_C_COMPILER_LINKER_ID
+    CMAKE_C_COMPILER_LINKER_VERSION
     CMAKE_C_IMPLICIT_LINK_DIRECTORIES
     )
   if(DEFINED ${var})
-    string(APPEND info "set(INFO_${var} \"${${var}}\")\n")
+    string(APPEND info "set(${var} \"${${var}}\")\n")
   endif()
 endforeach()
 
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
index 04998a2..8d6c739 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -8,6 +8,7 @@
 set(targets
   aix-C-XL-13.1.3 aix-CXX-XL-13.1.3
   aix-C-XLClang-16.1.0.1 aix-CXX-XLClang-16.1.0.1
+  aix-C-IBMClang-17.1.1.2 aix-CXX-IBMClang-17.1.1.2
   craype-C-Cray-8.7 craype-CXX-Cray-8.7 craype-Fortran-Cray-8.7
   craype-C-Cray-9.0-hlist-ad craype-CXX-Cray-9.0-hlist-ad craype-Fortran-Cray-9.0-hlist-ad
   craype-C-GNU-7.3.0 craype-CXX-GNU-7.3.0 craype-Fortran-GNU-7.3.0
@@ -23,7 +24,10 @@
   linux-C-GNU-10.2.1-static-libgcc
     linux-CXX-GNU-10.2.1-static-libstdc++
     linux-Fortran-GNU-10.2.1-static-libgfortran
+  linux-C-GNU-12.2.0 linux-CXX-GNU-12.2.0 linux-Fortran-GNU-12.2.0
   linux-C-Intel-18.0.0.20170811 linux-CXX-Intel-18.0.0.20170811
+  linux-C-Intel-2021.10.0.20230609 linux-CXX-Intel-2021.10.0.20230609 linux-Fortran-Intel-2021.10.0.20230609
+  linux-C-IntelLLVM-2023.2.0 linux-CXX-IntelLLVM-2023.2.0 linux-Fortran-IntelLLVM-2023.2.0
   linux-C-PGI-18.10.1 linux-CXX-PGI-18.10.1
     linux-Fortran-PGI-18.10.1 linux_pgf77-Fortran-PGI-18.10.1
     linux_nostdinc-C-PGI-18.10.1 linux_nostdinc-CXX-PGI-18.10.1
@@ -42,7 +46,14 @@
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
   openbsd-C-Clang-5.0.1 openbsd-CXX-Clang-5.0.1
   sunos-C-SunPro-5.13.0 sunos-CXX-SunPro-5.13.0 sunos-Fortran-SunPro-8.8.0
+  sunos5.10_sparc32-C-GNU-5.5.0 sunos5.10_sparc32-CXX-GNU-5.5.0 sunos5.10_sparc32-Fortran-GNU-5.5.0
+  sunos5.11_i386-C-GNU-5.5.0 sunos5.11_i386-CXX-GNU-5.5.0 sunos5.11_i386-Fortran-GNU-5.5.0
+  windows_x86_64-C-MSVC-19.36.32543.0 windows_x86_64-CXX-MSVC-19.36.32543.0
+  windows_x86_64-C-MSVC-19.38.33130.0-VS windows_x86_64-CXX-MSVC-19.38.33130.0-VS
   windows_x86_64-C-Clang-17.0.1-MSVC windows_x86_64-CXX-Clang-17.0.1-MSVC windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC
+  windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC
+  windows_x86_64-C-Intel-2021.9.0.20230302 windows_x86_64-CXX-Intel-2021.9.0.20230302 windows_x86_64-Fortran-Intel-2021.9.0.20230302
+  windows_x86_64-C-IntelLLVM-2023.1.0 windows_x86_64-CXX-IntelLLVM-2023.1.0 windows_x86_64-Fortran-IntelLLVM-2023.1.0
   windows_arm64-C-Clang-17.0.1-MSVC windows_arm64-CXX-Clang-17.0.1-MSVC windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC
   )
 
@@ -93,16 +104,6 @@
 endfunction()
 
 #
-# unload_compiler_info: clear out any CMAKE_* vars load previously set
-#
-function(unload_compiler_info cmvars)
-  foreach(var IN LISTS cmvars)
-    unset("${var}" PARENT_SCOPE)
-  endforeach()
-endfunction()
-
-
-#
 # load_platform_info: establish CMAKE_LIBRARY_ARCHITECTURE_REGEX
 # based on the target platform.
 #
@@ -131,49 +132,64 @@
     continue()
   endif()
 
-  load_compiler_info(${infile} lang cmvars input)
-  load_platform_info(${t})
+  block()
+    load_compiler_info(${infile} lang cmvars input)
+    load_platform_info(${t})
 
-  # Need to handle files with empty entries for both libs or dirs
-  set(implicit_lib_output "")
-  set(idirs_output "")
-  set(implicit_objs "")
-  set(library_arch_output "")
-  file(STRINGS ${outfile} outputs)
-  foreach(line IN LISTS outputs)
-    if(line MATCHES "libs=")
-      string(REPLACE "libs=" "" implicit_lib_output "${line}")
+    # Need to handle files with empty entries for both libs or dirs
+    set(implicit_lib_output "")
+    set(idirs_output "")
+    set(implicit_objs "")
+    set(library_arch_output "")
+    set(linker_tool_output "")
+    file(STRINGS ${outfile} outputs)
+    foreach(line IN LISTS outputs)
+      if(line MATCHES "libs=")
+        string(REPLACE "libs=" "" implicit_lib_output "${line}")
+      endif()
+      if(line MATCHES "dirs=")
+        string(REPLACE "dirs=" "" idirs_output "${line}")
+      endif()
+      if(line MATCHES "library_arch=")
+        string(REPLACE "library_arch=" "" library_arch_output "${line}")
+      endif()
+      if(line MATCHES "linker_tool=")
+        string(REPLACE "linker_tool=" "" linker_tool_output "${line}")
+      endif()
+    endforeach()
+
+    cmake_parse_implicit_link_info2("${input}" log
+        "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
+        LANGUAGE ${lang}
+        COMPUTE_LINKER linker_tool
+        COMPUTE_IMPLICIT_LIBS implicit_libs
+        COMPUTE_IMPLICIT_DIRS idirs
+        COMPUTE_IMPLICIT_FWKS implicit_fwks
+        COMPUTE_IMPLICIT_OBJECTS implicit_objs)
+
+    set(library_arch)
+    cmake_parse_library_architecture(${lang} "${idirs}" "${implicit_objs}" library_arch)
+
+    # File format
+    # file(WRITE ${outfile} "libs=${implicit_libs}\ndirs=${idirs}\nlibrary_arch=${library_arch}\nlinker_tool=${linker_tool}\n")
+
+    if(t MATCHES "windows" AND NOT CMAKE_HOST_WIN32)
+      string(REPLACE "\\" "/" linker_tool "${linker_tool}")
+      cmake_path(NORMAL_PATH linker_tool)
     endif()
-    if(line MATCHES "dirs=")
-      string(REPLACE "dirs=" "" idirs_output "${line}")
+
+    if(t MATCHES "-empty$")          # empty isn't supposed to parse
+      if("${state}" STREQUAL "done")
+        message("empty parse failed: ${idirs}, log=${log}")
+      endif()
+    elseif(NOT "${idirs}" MATCHES "^${idirs_output}$")
+      message("${t} parse failed: state=${state}, '${idirs}' does not match '^${idirs_output}$'")
+    elseif(NOT "${implicit_libs}" MATCHES "^${implicit_lib_output}$")
+      message("${t} parse failed: state=${state}, '${implicit_libs}' does not match '^${implicit_lib_output}$'")
+    elseif((library_arch OR library_arch_output) AND NOT "${library_arch}" MATCHES "^${library_arch_output}$")
+      message("${t} parse failed: state=${state}, '${library_arch}' does not match '^${library_arch_output}$'")
+    elseif((linker_tool OR linker_tool_output) AND NOT "${linker_tool}" MATCHES "^${linker_tool_output}$")
+      message("${t} parse failed: state=${state}, '${linker_tool}' does not match '^${linker_tool_output}$'")
     endif()
-    if(line MATCHES "library_arch=")
-      string(REPLACE "library_arch=" "" library_arch_output "${line}")
-    endif()
-  endforeach()
-
-  cmake_parse_implicit_link_info("${input}" implicit_libs idirs implicit_fwks log
-      "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
-      LANGUAGE ${lang}
-      COMPUTE_IMPLICIT_OBJECTS implicit_objs)
-
-  set(library_arch)
-  cmake_parse_library_architecture(${lang} "${idirs}" "${implicit_objs}" library_arch)
-
-  # File format
-  # file(WRITE ${outfile} "libs=${implicit_libs}\ndirs=${idirs}\nlibrary_arch=${library_arch}")
-
-  if(t MATCHES "-empty$")          # empty isn't supposed to parse
-    if("${state}" STREQUAL "done")
-      message("empty parse failed: ${idirs}, log=${log}")
-    endif()
-  elseif(NOT "${idirs}" MATCHES "^${idirs_output}$")
-    message("${t} parse failed: state=${state}, '${idirs}' does not match '^${idirs_output}$'")
-  elseif(NOT "${implicit_libs}" MATCHES "^${implicit_lib_output}$")
-    message("${t} parse failed: state=${state}, '${implicit_libs}' does not match '^${implicit_lib_output}$'")
-  elseif((library_arch OR library_arch_output) AND NOT "${library_arch}" MATCHES "^${library_arch_output}$")
-    message("${t} parse failed: state=${state}, '${library_arch}' does not match '^${library_arch_output}$'")
-  endif()
-
-  unload_compiler_info("${cmvars}")
+  endblock()
 endforeach(t)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
index c7655d2..9514c97 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
@@ -2,10 +2,41 @@
 
 run_cmake(ParseImplicitLinkInfo)
 
+# Detect information from the toolchain:
+# - CMAKE_SYSTEM_NAME
+# - CMAKE_C_COMPILER
+# - CMAKE_C_COMPILER_ID
+# - CMAKE_C_COMPILER_VERSION
+# - CMAKE_C_COMPILER_LINKER
+# - CMAKE_C_COMPILER_LINKER_ID
+# - CMAKE_C_COMPILER_LINKER_VERSION
+# - CMAKE_C_IMPLICIT_LINK_DIRECTORIES
 run_cmake(Inspect)
 set(info "${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
 include("${info}")
 
-if(INFO_CMAKE_C_IMPLICIT_LINK_DIRECTORIES MATCHES ";")
+if(CMAKE_HOST_UNIX)
+  run_cmake_script(DetermineLinkerId)
+endif()
+
+if(CMAKE_C_IMPLICIT_LINK_DIRECTORIES MATCHES ";")
   run_cmake_with_options(ExcludeDirs "-Dinfo=${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
 endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows|AIX|SunOS)$|BSD"
+    AND NOT CMAKE_C_COMPILER_ID MATCHES "^(Borland|Embarcadero|OpenWatcom|OrangeC|Watcom)$"
+    AND NOT (CMAKE_C_COMPILER_ID MATCHES "^(Intel|IntelLLVM)$" AND CMAKE_SYSTEM_NAME STREQUAL "Windows")
+    AND NOT CMAKE_C_COMPILER_LINKER MATCHES "Visual Studio 9\\.0"
+    AND NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 "
+    )
+  if(NOT CMAKE_C_COMPILER_LINKER OR NOT CMAKE_C_COMPILER_LINKER_ID OR NOT CMAKE_C_COMPILER_LINKER_VERSION)
+    message(SEND_ERROR "C compiler's linker not identified:\n"
+      "  CMAKE_C_COMPILER='${CMAKE_C_COMPILER}'\n"
+      "  CMAKE_C_COMPILER_ID='${CMAKE_C_COMPILER_ID}'\n"
+      "  CMAKE_C_COMPILER_VERSION='${CMAKE_C_COMPILER_VERSION}'\n"
+      "  CMAKE_C_COMPILER_LINKER='${CMAKE_C_COMPILER_LINKER}'\n"
+      "  CMAKE_C_COMPILER_LINKER_ID='${CMAKE_C_COMPILER_LINKER_ID}'\n"
+      "  CMAKE_C_COMPILER_LINKER_VERSION='${CMAKE_C_COMPILER_LINKER_VERSION}'\n"
+      )
+  endif()
+endif()
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.bash
new file mode 100755
index 0000000..b996b53
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'ld: LD 7.3.1(6/9/22)'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.cmake
new file mode 100644
index 0000000..e01e467
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.cmake
@@ -0,0 +1 @@
+set(CMAKE_SYSTEM_NAME "AIX")
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.txt
new file mode 100644
index 0000000..a708bd2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/aix7.3-ld.txt
@@ -0,0 +1,2 @@
+CMAKE_C_COMPILER_LINKER_ID='AIX'
+CMAKE_C_COMPILER_LINKER_VERSION='7.3.1'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.bash
new file mode 100755
index 0000000..d63fd30
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'GNU ld (GNU Binutils for Debian) 2.40'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.txt
new file mode 100644
index 0000000..802becd
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.bfd.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='GNU'
+CMAKE_C_COMPILER_LINKER_VERSION='2.40'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.bash
new file mode 100755
index 0000000..894219c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'GNU gold (GNU Binutils for Debian 2.40) 1.16'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.txt
new file mode 100644
index 0000000..0b4e7c2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.gold.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='GNUgold'
+CMAKE_C_COMPILER_LINKER_VERSION='1.16'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.bash
new file mode 100755
index 0000000..8b3099e
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'Debian LLD 14.0.6 (compatible with GNU linkers)'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.txt
new file mode 100644
index 0000000..e689c2c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.lld.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='LLD'
+CMAKE_C_COMPILER_LINKER_VERSION='14.0.6'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.bash
new file mode 100755
index 0000000..99bf1a3
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'mold 1.10.1 (compatible with GNU ld)'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.txt
new file mode 100644
index 0000000..0f4d63e
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/debian12-ld.mold.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='MOLD'
+CMAKE_C_COMPILER_LINKER_VERSION='1.10.1'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.bash
new file mode 100755
index 0000000..c74ea41
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'GNU ld version 2.40-13.fc39'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.txt
new file mode 100644
index 0000000..802becd
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.bfd.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='GNU'
+CMAKE_C_COMPILER_LINKER_VERSION='2.40'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.bash
new file mode 100755
index 0000000..c79bc24
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'GNU gold (version 2.40-13.fc39) 1.16'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.txt
new file mode 100644
index 0000000..0b4e7c2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.gold.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='GNUgold'
+CMAKE_C_COMPILER_LINKER_VERSION='1.16'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.bash
new file mode 100755
index 0000000..fef53f9
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'LLD 17.0.4 (compatible with GNU linkers)'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.txt
new file mode 100644
index 0000000..e9d0d36
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.lld.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='LLD'
+CMAKE_C_COMPILER_LINKER_VERSION='17.0.4'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.bash
new file mode 100755
index 0000000..c20639c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'mold 2.4.0 (compatible with GNU ld)'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.txt
new file mode 100644
index 0000000..708c5dc
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/fedora39-ld.mold.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='MOLD'
+CMAKE_C_COMPILER_LINKER_VERSION='2.4.0'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.bash
new file mode 100755
index 0000000..3599216
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.bash
@@ -0,0 +1,5 @@
+#!/bin/sh
+echo 'Microsoft (R) Incremental Linker Version 14.36.32543.0
+Copyright (C) Microsoft Corporation.  All rights reserved.
+
+ usage: LINK [options] [files] [@commandfile]'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.txt
new file mode 100644
index 0000000..b15aaf0
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/msvc14.36-link.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='MSVC'
+CMAKE_C_COMPILER_LINKER_VERSION='14.36.32543.0'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='MSVC'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.bash
new file mode 100755
index 0000000..b296462
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.bash
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo 'ld: Software Generation Utilities - Solaris Link Editors: 5.11-1.2458'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.cmake
new file mode 100644
index 0000000..323087d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.cmake
@@ -0,0 +1 @@
+set(CMAKE_SYSTEM_NAME "SunOS")
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.txt
new file mode 100644
index 0000000..6c645a6
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/sunos5.11-ld.txt
@@ -0,0 +1,2 @@
+CMAKE_C_COMPILER_LINKER_ID='Solaris'
+CMAKE_C_COMPILER_LINKER_VERSION='5.11-1.2458'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.bash b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.bash
new file mode 100755
index 0000000..8dd9267
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.bash
@@ -0,0 +1,9 @@
+#!/bin/sh
+echo '@(#)PROGRAM:ld  PROJECT:dyld-1022.1
+BUILD 13:21:42 Nov 10 2023
+configured to support archs: armv6 armv7 armv7s arm64 arm64e arm64_32 i386 x86_64 x86_64h
+will use ld-classic for: armv6 armv7 armv7s arm64_32 i386 armv6m armv7k armv7m armv7em
+LTO support using: LLVM version 15.0.0 (static support for 29, runtime is 29)
+TAPI support using: Apple TAPI version 15.0.0 (tapi-1500.0.12.8)
+Library search paths:
+Framework search paths:'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.cmake
new file mode 100644
index 0000000..9982824
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.cmake
@@ -0,0 +1 @@
+set(CMAKE_EFFECTIVE_SYSTEM_NAME "Apple")
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.txt b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.txt
new file mode 100644
index 0000000..f2eb083
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ld-v/xcode15.1-ld.txt
@@ -0,0 +1,3 @@
+CMAKE_C_COMPILER_LINKER_ID='AppleClang'
+CMAKE_C_COMPILER_LINKER_VERSION='1022.1'
+CMAKE_C_COMPILER_LINKER_FRONTEND_VARIANT='GNU'
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-IBMClang-17.1.1.2.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-IBMClang-17.1.1.2.output
new file mode 100644
index 0000000..9979735
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-IBMClang-17.1.1.2.output
@@ -0,0 +1,3 @@
+libs=/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libxlopt.a;unwind;pthreads;c
+dirs=/opt/IBM/xlmass/10.1.1/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output
index 4e030b3..17e6308 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XL-13.1.3.output
@@ -1,2 +1,3 @@
 libs=xlopt;xlipa;xl;c
 dirs=/opt/IBM/xlmass/8.1.3/lib/aix61;/opt/IBM/xlc/13.1.3/lib
+linker_tool=/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output
index 6f677a0..462d852 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-C-XLClang-16.1.0.1.output
@@ -1,2 +1,3 @@
 libs=xlopt;xlipa;xl;c;pthreads
 dirs=/opt/IBM/xlmass/9.1.0/lib/aix61;/opt/IBM/xlc/16.1.0/lib
+linker_tool=/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-IBMClang-17.1.1.2.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-IBMClang-17.1.1.2.output
new file mode 100644
index 0000000..8dbb500
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-IBMClang-17.1.1.2.output
@@ -0,0 +1,3 @@
+libs=c\+\+;c\+\+abi;/opt/IBM/openxlC/17.1.1/lib/clang/15.0.0/lib/aix/libxlopt.a;unwind;pthreads;m;c
+dirs=/opt/IBM/xlmass/10.1.1/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output
index 6cbc792..33a2f44 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XL-13.1.3.output
@@ -1,2 +1,3 @@
 libs=xlopt;xlipa;xl;C;m;c
 dirs=/opt/IBM/xlmass/8.1.3/lib/aix61;/opt/IBM/xlc/13.1.3/lib;/opt/IBM/xlC/13.1.3/lib
+linker_tool=/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output
index 3a57464..3599785 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/aix-CXX-XLClang-16.1.0.1.output
@@ -1,2 +1,3 @@
 libs=xlopt;xlipa;xl;c\+\+;Ccore;pthreads;m;c
 dirs=/opt/IBM/xlmass/9.1.0/lib/aix61;/opt/IBM/xlc/16.1.0/lib;/opt/IBM/xlC/16.1.0/lib
+linker_tool=/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output
index 0373d89..8f68c4c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-8.7.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;pthread;sci_cray_mpi_mp;m;f;sci_cray_mp;craymp;m;pthread;f;hugetlbfs;mpich_cray;rt;pthread;ugni;pmi;pgas-dmapp;fi;u;rt;dmapp;ugni;udreg;pthread;m;cray-c\+\+-rts;stdc\+\+;xpmem;dmapp;pthread;pmi;pthread;alpslli;pthread;wlm_detect;ugni;pthread;alpsutil;pthread;rca;udreg;quadmath;m;omp;rt;craymp;pthread;rt;dl;cray-c\+\+-rts;stdc\+\+;m;modules;m;rt;fi;m;quadmath;rt;craymath;m;gfortran;quadmath;rt;f;m;pthread;rt;u;rt;dl;cray-c\+\+-rts;stdc\+\+;m;csup;rt;atomic;stdc\+\+;pthread;c;csup;m;gcc
 dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/dmapp/7.1.1-6.0.5.0_49.8__g1125556.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output
index 0f52e8b..b192e8b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Cray-9.0-hlist-ad.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;atomic;tcmalloc_minimal;cray-c\+\+-rts;stdc\+\+;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
 dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output
index 267bf58..bd6add6 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-GNU-7.3.0.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;pthread;sci_gnu_71_mpi;sci_gnu_71;pthread;hugetlbfs;mpich_gnu_71;rt;ugni;pthread;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;xpmem;ugni;pthread;udreg;gfortran;quadmath;mvec;m;pthread;gcc;c
 dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output
index 5b8ae8d..a3f11f4 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-C-Intel-18.0.2.20180210.output
@@ -1,2 +1,3 @@
 libs=imf;svml;irng;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
 dirs=/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output
index 00281d5..0b25047 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-8.7.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;pthread;sci_cray_mpi_mp;m;f;sci_cray_mp;craymp;m;pthread;f;hugetlbfs;mpichcxx_cray;rt;pthread;ugni;pmi;mpich_cray;rt;pthread;ugni;pmi;pgas-dmapp;fi;u;rt;dmapp;ugni;udreg;pthread;m;cray-c\+\+-rts;stdc\+\+;xpmem;dmapp;pthread;pmi;pthread;alpslli;pthread;wlm_detect;ugni;pthread;alpsutil;pthread;rca;udreg;quadmath;m;omp;rt;craymp;pthread;rt;dl;cray-c\+\+-rts;stdc\+\+;m;modules;m;rt;fi;m;quadmath;rt;craymath;m;gfortran;quadmath;rt;f;m;pthread;rt;u;rt;dl;cray-c\+\+-rts;stdc\+\+;m;csup;rt;atomic;cray-c\+\+-rts;stdc\+\+;supc\+\+;stdc\+\+;pthread;c;csup;m;gcc
 dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/dmapp/7.1.1-6.0.5.0_49.8__g1125556.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
index a7287d3..861ba7b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Cray-9.0-hlist-ad.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;mpichcxx_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;atomic;cray-c\+\+-rts;cray-c\+\+-rts;stdc\+\+;supc\+\+;tcmalloc_minimal;cray-c\+\+-rts;stdc\+\+;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
 dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output
index ead4804..fa56fbb 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-GNU-7.3.0.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;pthread;sci_gnu_71_mpi;sci_gnu_71;pthread;hugetlbfs;mpichcxx_gnu_71;rt;ugni;pthread;pmi;mpich_gnu_71;rt;ugni;pthread;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;ugni;pthread;xpmem;udreg;gfortran;quadmath;mvec;m;pthread;stdc\+\+;m;gcc;c
 dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output
index 1a3b736..0d77d39 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-CXX-Intel-18.0.2.20180210.output
@@ -1,2 +1,3 @@
 libs=imf;svml;irng;stdc\+\+;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
 dirs=/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output
index 3b26f40..f580b77 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-8.7.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;rca;sci_cray_mpi_mp;sci_cray_mp;mpich_cray;mpichf90_cray;pgas-dmapp;quadmath;omp;craymp;modules;fi;craymath;f;u;csup;atomic;gfortran;tcmalloc_minimal;stdc\+\+;pthread;c;csup;m;gcc
 dirs=/opt/gcc/6.1.0/snos/lib64;/opt/cray/pe/libsci/18.07.1/CRAY/8.6/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-cray/8.6/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/8.7.4/cce/x86_64/lib;/usr/lib64;/lib64;/opt/gcc/6.1.0/snos/lib/gcc/x86_64-suse-linux/6.1.0;/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/8.7.4/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
index d15e5a7..312bc13 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Cray-9.0-hlist-ad.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;rca;mpich_cray_90;mpichf90_cray_90;sci_cray_mpi;sci_cray;pgas-dmapp;quadmath;modules;fi;craymath;f;u;csup;gfortran;tcmalloc_minimal;cray-c\+\+-rts;stdc\+\+;pthread;c;csup;m;clang_rt.craypgo-x86_64;gcc
 dirs=/opt/gcc/8.1.0/snos/lib64;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.8/gni/mpich-cray/9.0/lib;/opt/cray/pe/libsci/19.06.1/CRAY/9.0/x86_64/lib;/opt/cray/rca/2.2.18-6.0.7.0_33.3__g2aa4f39.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/pe/cce/9.0.0/cce/x86_64/lib;/usr/lib64;/lib64;/opt/cray/pe/cce/9.0.0/cce-clang/x86_64/lib/clang/9.0.0/lib/linux;/opt/gcc/8.1.0/snos/lib/gcc/x86_64-suse-linux/8.1.0;/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-unknown-linux-gnu/lib
+linker_tool=/opt/cray/pe/cce/9.0.0/binutils/x86_64/x86_64-pc-linux-gnu/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output
index da2e557..32d4057 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-GNU-7.3.0.output
@@ -1,2 +1,3 @@
 libs=AtpSigHandler;AtpSigHCommData;rca;sci_gnu_71_mpi;sci_gnu_71;mpich_gnu_71;mpichf90_gnu_71;gfortran;quadmath;pthread;gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/opt/cray/pe/libsci/18.07.1/GNU/7.1/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-gnu/7.1/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/gcc/7.3.0/snos/lib/gcc/x86_64-suse-linux/7.3.0;/opt/gcc/7.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/7.3.0/snos/lib
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
index e73cbe9..8ff73bb 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/craype-Fortran-Intel-18.0.2.20180210.output
@@ -1,2 +1,3 @@
 libs=hugetlbfs;AtpSigHandler;AtpSigHCommData;pthread;mpichf90_intel;rt;ugni;pmi;imf;m;pthread;dl;sci_intel_mpi;sci_intel;imf;m;dl;mpich_intel;rt;ugni;pthread;pmi;imf;m;dl;pmi;pthread;alpslli;pthread;wlm_detect;alpsutil;pthread;rca;xpmem;ugni;pthread;udreg;sci_intel;imf;m;pthread;dl;hugetlbfs;imf;m;pthread;ifport;ifcore;imf;svml;m;ipgo;irc;svml;c;gcc;irc_s;dl;c
 dirs=/opt/cray/pe/libsci/18.07.1/INTEL/16.0/x86_64/lib;/opt/cray/dmapp/default/lib64;/opt/cray/pe/mpt/7.7.3/gni/mpich-intel/16.0/lib;/opt/cray/rca/2.2.16-6.0.5.0_15.34__g5e09e6d.ari/lib64;/opt/cray/alps/6.5.28-6.0.5.0_18.6__g13a91b6.ari/lib64;/opt/cray/xpmem/2.2.4-6.0.5.1_8.26__g35d5e73.ari/lib64;/opt/cray/pe/pmi/5.0.14/lib64;/opt/cray/ugni/6.0.14-6.0.5.0_16.9__g19583bb.ari/lib64;/opt/cray/udreg/2.3.2-6.0.5.0_13.12__ga14955a.ari/lib64;/opt/cray/pe/atp/2.1.3/libApp;/opt/cray/wlm_detect/1.3.2-6.0.5.0_3.1__g388ccd5.ari/lib64;/opt/intel/2018.2.199/compilers_and_libraries_2018/linux/mkl/lib/intel64;/opt/intel/2018.2.199/compilers_and_libraries_2018.2.199/linux/compiler/lib/intel64_lin;/opt/gcc/6.3.0/snos/lib/gcc/x86_64-suse-linux/6.3.0;/opt/gcc/6.3.0/snos/lib64;/lib64;/usr/lib64;/opt/gcc/6.3.0/snos/lib;/lib;/usr/lib
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
index c041faa..0728e9a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-C-AppleClang-8.0.0.8000042.output
@@ -1,2 +1,3 @@
 libs=
 dirs=/usr/lib;/usr/local/lib
+linker_tool=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
index 47a362a..c3e82ce 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin-CXX-AppleClang-8.0.0.8000042.output
@@ -1,2 +1,3 @@
 libs=c\+\+
 dirs=/usr/lib;/usr/local/lib
+linker_tool=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
index c041faa..0728e9a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-C-AppleClang-8.0.0.8000042.output
@@ -1,2 +1,3 @@
 libs=
 dirs=/usr/lib;/usr/local/lib
+linker_tool=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
index 47a362a..c3e82ce 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/darwin_nostdinc-CXX-AppleClang-8.0.0.8000042.output
@@ -1,2 +1,3 @@
 libs=c\+\+
 dirs=/usr/lib;/usr/local/lib
+linker_tool=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output
index 4ce854a..1fe59bb 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-C-Clang-3.3.0.output
@@ -1,2 +1,3 @@
 libs=gcc;gcc_s;c;gcc;gcc_s
 dirs=/usr/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output
index 18d7cd1..b2df7d5 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-CXX-Clang-3.3.0.output
@@ -1,2 +1,3 @@
 libs=c\+\+;m;gcc;gcc_s;c;gcc;gcc_s
 dirs=/usr/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output
index 1228333..41c3c30 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/freebsd-Fortran-GNU-4.6.4.output
@@ -1,2 +1,3 @@
 libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/local/lib/gcc46/gcc/x86_64-portbld-freebsd10.0/4.6.4;/usr/local/x86_64-portbld-freebsd10.0/lib;/usr/local/lib/gcc46
+linker_tool=/usr/local/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output
index 1b14cd5..51ab39b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-empty.output
@@ -1,2 +1,3 @@
 libs=
 dirs=
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output
index 9bb651a..4bbc6c3 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-C-relative.output
@@ -1,2 +1,3 @@
 libs=
 dirs=/usr/lib64
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output
index 1b14cd5..51ab39b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-empty.output
@@ -1,2 +1,3 @@
 libs=
 dirs=
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output
index 9bb651a..4bbc6c3 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/hand-CXX-relative.output
@@ -1,2 +1,3 @@
 libs=
 dirs=/usr/lib64
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-10.2.1-static-libgcc.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-10.2.1-static-libgcc.output
index 8bcd8b2..ffb5f0b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-10.2.1-static-libgcc.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-10.2.1-static-libgcc.output
@@ -1,3 +1,4 @@
 libs=gcc;c;gcc
 dirs=/usr/lib/gcc/x86_64-linux-gnu/10;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-12.2.0.output
new file mode 100644
index 0000000..9eeea34
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-12.2.0.output
@@ -0,0 +1,4 @@
+libs=gcc;gcc_s;c;gcc;gcc_s
+dirs=/usr/lib/gcc/x86_64-linux-gnu/12;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
+library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output
index 0cf3a49..ff5e486 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-GNU-7.3.0.output
@@ -1,3 +1,4 @@
 libs=gcc;gcc_s;c;gcc;gcc_s
 dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output
index d78c9f4..7dead45 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-18.0.0.20170811.output
@@ -1,3 +1,4 @@
 libs=imf;svml;irng;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
 dirs=/opt/intel/compilers_and_libraries_2018.0.128/linux/ipp/lib/intel64;/opt/intel/compilers_and_libraries_2018.0.128/linux/compiler/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/mkl/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/tbb/lib/intel64/gcc4.7;/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib;/lib
 library_arch=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..68b9916
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-Intel-2021.10.0.20230609.output
@@ -0,0 +1,4 @@
+libs=imf;svml;irng;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib
+library_arch=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..06fa9cb
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-IntelLLVM-2023.2.0.output
@@ -0,0 +1,4 @@
+libs=svml;irng;imf;m;gcc;gcc_s;irc;dl;gcc;gcc_s;c;gcc;gcc_s;irc_s
+dirs=/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib;/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64
+library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-NVHPC-21.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-NVHPC-21.1.0.output
index e932be9..496484c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-NVHPC-21.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-NVHPC-21.1.0.output
@@ -1,3 +1,4 @@
 libs=nvomp;dl;nvhpcatm;atomic;pthread;nvcpumath;nsnvc;nvc;m;gcc;c;gcc;gcc_s
 dirs=/opt/nvidia/hpc_sdk/Linux_x86_64/21.1/compilers/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/9
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output
index 7931102..eb9a55d 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output
index 81ac0ba..46ed2d9 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-12.1.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output
index b88a48d..ab0db93 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-C-XL-16.1.0.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;dl;gcc_s;pthread;gcc;m;c;gcc_s;gcc
 dirs=/opt/ibm/xlsmp/5.1.0/lib;/opt/ibm/xlmass/9.1.0/lib;/opt/ibm/xlC/16.1.0/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
index be6b906..6d17cdc 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-CLANG.output
@@ -1,3 +1,4 @@
 libs=cudadevrt;cudart_static;rt;pthread;dl;stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/local/cuda/targets/x86_64-linux/lib/stubs;/usr/local/cuda/targets/x86_64-linux/lib;/usr/lib/gcc/x86_64-linux-gnu/8;/usr/lib/x86_64-linux-gnu;/lib/x86_64-linux-gnu;/lib64;/usr/lib;/usr/lib/llvm-8/lib;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output
index d3f3627..6cd069a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-10.1.168-XLClang-v.output
@@ -1,3 +1,4 @@
 libs=cudadevrt;cudart_static;rt;pthread;dl;xlopt;xl;ibmc\+\+;stdc\+\+;m;dl;gcc_s;gcc;pthread;m;c;gcc_s;gcc
 dirs=/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib/stubs;/sw/summit/cuda/10.1.168/targets/ppc64le-linux/lib;/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/xl-16.1.1-3/spectrum-mpi-10.3.0.1-20190611-aqjt3jo53mogrrhcrd2iufr435azcaha/lib;/autofs/nccs-svm1_sw/summit/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-csygoqyym3m3ysoaperhxlhoiluvpa2u/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlsmp/5.1.1/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlmass/9.1.1/lib;/autofs/nccs-svm1_sw/summit/xl/16.1.1-3/xlC/16.1.1/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/autofs/nccs-svm1_sw/peak/.swci/1-compute/opt/spack/20180914/linux-rhel7-ppc64le/gcc-4.8.5/darshan-runtime-3.1.7-ytwv7xbkub6mqnpvygdthwqa7mhjqbc5/lib;/usr/lib
 library_arch=
+linker_tool=/sw/summit/xalt/1.1.3/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
index 958f41d..400f377 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CUDA-NVIDIA-9.2.148-GCC.output
@@ -1,3 +1,4 @@
 libs=cudadevrt;cudart_static;rt;pthread;dl;stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/lib/x86_64-linux-gnu/stubs;/usr/lib/gcc/x86_64-linux-gnu/5;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-10.2.1-static-libstdc++.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-10.2.1-static-libstdc++.output
index d38dfee..a9cb5b1 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-10.2.1-static-libstdc++.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-10.2.1-static-libstdc++.output
@@ -1,3 +1,4 @@
 libs=/usr/lib/gcc/x86_64-linux-gnu/10/libstdc\+\+.a;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/lib/gcc/x86_64-linux-gnu/10;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-12.2.0.output
new file mode 100644
index 0000000..65da5ea
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-12.2.0.output
@@ -0,0 +1,4 @@
+libs=stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/lib/gcc/x86_64-linux-gnu/12;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
+library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output
index f87ecff..1698ced 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-GNU-7.3.0.output
@@ -1,3 +1,4 @@
 libs=stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output
index 832f218..598ec80 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-18.0.0.20170811.output
@@ -1,3 +1,4 @@
 libs=imf;svml;irng;stdc\+\+;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
 dirs=/opt/intel/compilers_and_libraries_2018.0.128/linux/ipp/lib/intel64;/opt/intel/compilers_and_libraries_2018.0.128/linux/compiler/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/mkl/lib/intel64_lin;/opt/intel/compilers_and_libraries_2018.0.128/linux/tbb/lib/intel64/gcc4.7;/usr/lib/gcc/x86_64-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib;/lib
 library_arch=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..6e2d13c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-Intel-2021.10.0.20230609.output
@@ -0,0 +1,4 @@
+libs=imf;svml;irng;stdc\+\+;m;ipgo;decimal;cilkrts;stdc\+\+;gcc;gcc_s;irc;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib
+library_arch=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..3da108c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-IntelLLVM-2023.2.0.output
@@ -0,0 +1,4 @@
+libs=svml;irng;stdc\+\+;imf;m;gcc_s;gcc;irc;dl;gcc_s;gcc;c;gcc_s;gcc;irc_s
+dirs=/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib;/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64
+library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-NVHPC-21.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-NVHPC-21.1.0.output
index 5e93f6d..ce39fc6 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-NVHPC-21.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-NVHPC-21.1.0.output
@@ -1,3 +1,4 @@
 libs=atomic;nvhpcatm;stdc\+\+;nvomp;dl;nvhpcatm;atomic;pthread;nvcpumath;nsnvc;nvc;m;gcc;c;gcc;gcc_s
 dirs=/opt/nvidia/hpc_sdk/Linux_x86_64/21.1/compilers/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/9
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output
index 0e95961..e37e49c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=atomic;pgatm;stdc\+\+;pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output
index 34cab2e..9e69183 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-12.1.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;ibmc\+\+;xlopt;xl;stdc\+\+;m;dl;gcc_s;gcc;m;c;gcc_s;gcc;dl;gcc_s;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output
index c754bda..30e16c9 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-CXX-XL-16.1.0.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;ibmc\+\+;stdc\+\+;m;dl;gcc_s;gcc;pthread;m;c;gcc_s;gcc
 dirs=/opt/ibm/xlsmp/5.1.0/lib;/opt/ibm/xlmass/9.1.0/lib;/opt/ibm/xlC/16.1.0/lib;/usr/lib/gcc/ppc64le-redhat-linux/4.8.5;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-10.2.1-static-libgfortran.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-10.2.1-static-libgfortran.output
index edeb20c..d105cd7 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-10.2.1-static-libgfortran.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-10.2.1-static-libgfortran.output
@@ -1,3 +1,4 @@
 libs=/usr/lib/gcc/x86_64-linux-gnu/10/libgfortran.a;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/lib/gcc/x86_64-linux-gnu/10;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-12.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-12.2.0.output
new file mode 100644
index 0000000..0fcf92b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-12.2.0.output
@@ -0,0 +1,4 @@
+libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/usr/lib/gcc/x86_64-linux-gnu/12;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
+library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output
index 09b720e..27db155 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-GNU-7.3.0.output
@@ -1,3 +1,4 @@
 libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=/usr/lib/gcc/x86_64-linux-gnu/7;/usr/lib/x86_64-linux-gnu;/usr/lib;/lib/x86_64-linux-gnu;/lib
 library_arch=x86_64-linux-gnu
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output
new file mode 100644
index 0000000..784711f
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-Intel-2021.10.0.20230609.output
@@ -0,0 +1,4 @@
+libs=ifport;ifcoremt;imf;svml;m;ipgo;irc;pthread;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib
+library_arch=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output
new file mode 100644
index 0000000..0129854
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-IntelLLVM-2023.2.0.output
@@ -0,0 +1,4 @@
+libs=ifport;ifcoremt;imf;svml;m;ipgo;irc;pthread;svml;c;gcc;gcc_s;irc_s;dl;c
+dirs=/opt/intel/oneapi/tbb/2021.10.0/lib/intel64/gcc4.8;/opt/intel/oneapi/mpi/2021.10.0/libfabric/lib;/opt/intel/oneapi/mpi/2021.10.0/lib/release;/opt/intel/oneapi/mpi/2021.10.0/lib;/opt/intel/oneapi/mkl/2023.2.0/lib/intel64;/opt/intel/oneapi/compiler/2023.2.1/linux/compiler/lib/intel64_lin;/opt/intel/oneapi/compiler/2023.2.1/linux/lib;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/lib/linux;/opt/intel/oneapi/compiler/2023.2.1/linux/lib/clang/17/lib/x86_64-unknown-linux-gnu;/usr/lib/gcc/x86_64-redhat-linux/8;/usr/lib64;/lib64;/usr/lib;/lib
+library_arch=x86_64-unknown-linux-gnu
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LLVMFlang-15.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LLVMFlang-15.0.0.output
index cea8a68..17d16d1 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LLVMFlang-15.0.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-LLVMFlang-15.0.0.output
@@ -1,2 +1,3 @@
 libs=Fortran_main;FortranRuntime;FortranDecimal;m;gcc;gcc_s;c;gcc;gcc_s
 dirs=/usr/lib/gcc/x86_64-redhat-linux/12;/usr/lib64;/lib64;/lib;/usr/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output
index f3cc551..4cb8199 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=pgf90rtl;pgf90;pgf90_rpm1;pgf902;pgf90rtl;pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=x86_64-linux-gnu
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output
index 3c07cf8..526914b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-Fortran-XL-14.1.0.output
@@ -1,3 +1,4 @@
 libs=xlf90;xlopt;xlomp_ser;xl;xlfmath;gcc_s;dl;rt;pthread;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/xlf/bg/14.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output
index 108712d..680a74e 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output
@@ -1,3 +1,4 @@
 libs=-l:libunwind.so;c;-l:libunwind.so
 dirs=/opt/llvm-13/lib/x86_64-pc-linux-gnu;/usr/lib/gcc/x86_64-linux-gnu/9;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/opt/llvm-13/lib;/lib;/usr/lib
 library_arch=x86_64-linux-gnu
+linker_tool=/opt/llvm-13/bin/ld.lld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
index e4a8a70..90c411f 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
@@ -1,3 +1,4 @@
 libs=c\+\+;m;-l:libunwind.so;c;-l:libunwind.so
 dirs=/opt/llvm-13/lib/x86_64-pc-linux-gnu;/usr/lib/gcc/x86_64-linux-gnu/9;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/opt/llvm-13/lib;/lib;/usr/lib
 library_arch=x86_64-linux-gnu
+linker_tool=/opt/llvm-13/bin/ld.lld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output
index dc17ce7..60662e2 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output
index 81ac0ba..46ed2d9 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-C-XL-12.1.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
index 848e8c0..37c8129 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=atomic;pgatm;stdc\+\+;pgmp;numa;pthread;pgmath;nspgc;pgc;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
index 34cab2e..9e69183 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-CXX-XL-12.1.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;ibmc\+\+;xlopt;xl;stdc\+\+;m;dl;gcc_s;gcc;m;c;gcc_s;gcc;dl;gcc_s;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
index 955d540..d0055d5 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc-Fortran-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=pgf90rtl;pgf90;pgf90_rpm1;pgf902;pgf90rtl;pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
index 81ac0ba..46ed2d9 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_nostdinc_i-C-XL-12.1.0.output
@@ -1,3 +1,4 @@
 libs=xlopt;xl;dl;gcc_s;gcc;m;c;gcc_s;gcc
 dirs=/soft/compilers/ibmcmp-oct2017/xlsmp/bg/3.1/lib64;/soft/compilers/ibmcmp-oct2017/xlmass/bg/7.3/lib64;/soft/compilers/ibmcmp-oct2017/vac/bg/12.1/lib64;/soft/compilers/ibmcmp-oct2017/vacpp/bg/12.1/lib64;/usr/lib/gcc/ppc64-redhat-linux/4.4.7;/usr/lib64;/lib64;/usr/lib
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
index 30b86e6..2570a4a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux_pgf77-Fortran-PGI-18.10.1.output
@@ -1,3 +1,4 @@
 libs=pgftnrtl;pgmp;numa;pthread;pgmath;nspgc;pgc;rt;pthread;m;gcc;c;gcc;gcc_s
 dirs=/mnt/pgi/linux86-64/18.10/lib;/usr/lib64;/usr/lib/gcc/x86_64-linux-gnu/7
 library_arch=
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output
index 8aee7cf..495f4de 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-C-GNU-4.9.3.output
@@ -1,2 +1,3 @@
 libs=mingw32;gcc;moldname;mingwex;advapi32;shell32;user32;kernel32;mingw32;gcc;moldname;mingwex
 dirs=C:/DoesNotExist/mingw/lib/gcc/mingw32/4.9.3;C:/DoesNotExist/mingw/lib/gcc;C:/DoesNotExist/mingw/mingw32/lib;C:/DoesNotExist/mingw/lib
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output
index 7852bfd..8661aee 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/mingw.org-CXX-GNU-4.9.3.output
@@ -1,2 +1,3 @@
 libs=stdc\+\+;mingw32;gcc_s;gcc;moldname;mingwex;advapi32;shell32;user32;kernel32;mingw32;gcc_s;gcc;moldname;mingwex
 dirs=C:/DoesNotExist/mingw/lib/gcc/mingw32/4.9.3;C:/DoesNotExist/mingw/lib/gcc;C:/DoesNotExist/mingw/mingw32/lib;C:/DoesNotExist/mingw/lib
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output
index 4a09c5b..afaa59a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-C-GNU-4.8.5.output
@@ -1,2 +1,3 @@
 libs=gcc;gcc_s;c;gcc;gcc_s
 dirs=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output
index c6a098e..43003b0 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd-CXX-GNU-4.8.5.output
@@ -1,2 +1,3 @@
 libs=stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
index 4a09c5b..afaa59a 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-C-GNU-4.8.5.output
@@ -1,2 +1,3 @@
 libs=gcc;gcc_s;c;gcc;gcc_s
 dirs=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
index c6a098e..43003b0 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/netbsd_nostdinc-CXX-GNU-4.8.5.output
@@ -1,2 +1,3 @@
 libs=stdc\+\+;m;gcc_s;gcc;c;gcc_s;gcc
 dirs=
+linker_tool=ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output
index 5bb5db4..76ec3b9 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-C-Clang-5.0.1.output
@@ -1,2 +1,3 @@
 libs=compiler_rt;c;compiler_rt
 dirs=/usr/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output
index 711225c..791249e 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/openbsd-CXX-Clang-5.0.1.output
@@ -1,2 +1,3 @@
 libs=c\+\+;c\+\+abi;pthread;m;compiler_rt;c;compiler_rt
 dirs=/usr/lib
+linker_tool=/usr/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output
index 0d636e6..13b67f6 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-C-SunPro-5.13.0.output
@@ -1,2 +1,3 @@
 libs=c
 dirs=/opt/solarisstudio12.4/lib/compilers/staticlib;/opt/solarisstudio12.4/lib/compilers/sparc;/opt/solarisstudio12.4/lib/compilers;/usr/ccs/lib;/lib;/usr/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output
index f7c8213..68f46bf 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-CXX-SunPro-5.13.0.output
@@ -1,2 +1,3 @@
 libs=Cstd;Crun;m;c
 dirs=/opt/solarisstudio12.4/lib/compilers/sparc;/opt/solarisstudio12.4/lib/compilers;/opt/solarisstudio12.4/lib/sparc;/opt/solarisstudio12.4/lib;/usr/ccs/lib;/lib;/usr/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output
index b49557a..0847f9b 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos-Fortran-SunPro-8.8.0.output
@@ -1,2 +1,3 @@
 libs=fsu;sunmath;mtsk;m;c
 dirs=/opt/developerstudio12.6/lib/compilers/sparcvis2;/opt/developerstudio12.6/lib/compilers;/opt/developerstudio12.6/lib;/usr/ccs/lib;/lib;/usr/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output
new file mode 100644
index 0000000..3212044
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-C-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=gcc;c;gcc;c
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0;/opt/csw/sparc-sun-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output
new file mode 100644
index 0000000..742e608
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-CXX-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=stdc\+\+;m;rt;gcc_s;gcc;c;gcc_s;gcc;c
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0;/opt/csw/sparc-sun-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output
new file mode 100644
index 0000000..7169169
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.10_sparc32-Fortran-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=gfortran;m;gcc_s;gcc;m;gcc_s;gcc;c;gcc_s;gcc;m;gcc_s;gcc;c
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/sparc-sun-solaris2.10/5.5.0;/opt/csw/sparc-sun-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-C-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-C-GNU-5.5.0.output
new file mode 100644
index 0000000..8c31ef5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-C-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=gcc;c;gcc
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0;/opt/csw/i386-pc-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output
new file mode 100644
index 0000000..fa771f2
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-CXX-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=stdc\+\+;m;rt;gcc_s;gcc;c;gcc_s;gcc
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0;/opt/csw/i386-pc-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output
new file mode 100644
index 0000000..726988d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/sunos5.11_i386-Fortran-GNU-5.5.0.output
@@ -0,0 +1,3 @@
+libs=gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc
+dirs=/lib;/usr/lib;/opt/csw/lib/gcc/i386-pc-solaris2.10/5.5.0;/opt/csw/i386-pc-solaris2.10/lib;/opt/csw/lib
+linker_tool=/usr/ccs/bin/ld
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output
index df9ef98..1633a9c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=
 dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/DoesNotExist/LLVM/bin/lld-link
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output
index df9ef98..1633a9c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=
 dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/DoesNotExist/LLVM/bin/lld-link
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output
index 65f3494..bb0e012 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=Fortran_main\.lib;FortranRuntime\.lib;FortranDecimal\.lib
 dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/DoesNotExist/LLVM/bin/lld-link
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output
index df9ef98..5bf205c 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=
 dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/Program Files \(x86\)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Intel-2021.9.0.20230302.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Intel-2021.9.0.20230302.output
new file mode 100644
index 0000000..158a90b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Intel-2021.9.0.20230302.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-IntelLLVM-2023.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-IntelLLVM-2023.1.0.output
new file mode 100644
index 0000000..51ab39b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-IntelLLVM-2023.1.0.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.36.32543.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.36.32543.0.output
new file mode 100644
index 0000000..e159ed5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.36.32543.0.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/PROGRA~1/MIB055~1/2022/PROFES~1/VC/Tools/MSVC/1436~1.325/bin/Hostx64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.38.33130.0-VS.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.38.33130.0-VS.output
new file mode 100644
index 0000000..0ffc15b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-MSVC-19.38.33130.0-VS.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/bin/HostX64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output
index df9ef98..7cfbad5 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=
 dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/Program Files \(x86\)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14\.29\.30133/bin/Hostx64/x64/link\.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Intel-2021.9.0.20230302.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Intel-2021.9.0.20230302.output
new file mode 100644
index 0000000..158a90b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Intel-2021.9.0.20230302.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-IntelLLVM-2023.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-IntelLLVM-2023.1.0.output
new file mode 100644
index 0000000..51ab39b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-IntelLLVM-2023.1.0.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.36.32543.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.36.32543.0.output
new file mode 100644
index 0000000..e159ed5
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.36.32543.0.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/PROGRA~1/MIB055~1/2022/PROFES~1/VC/Tools/MSVC/1436~1.325/bin/Hostx64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.output
new file mode 100644
index 0000000..0ffc15b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-MSVC-19.38.33130.0-VS.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/bin/HostX64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-Intel-2021.9.0.20230302.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-Intel-2021.9.0.20230302.output
new file mode 100644
index 0000000..158a90b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-Intel-2021.9.0.20230302.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=C:/DoesNotExist/Intel/oneAPI/compiler/latest/windows/bin/intel64/xilink.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-IntelLLVM-2023.1.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-IntelLLVM-2023.1.0.output
new file mode 100644
index 0000000..51ab39b
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-IntelLLVM-2023.1.0.output
@@ -0,0 +1,3 @@
+libs=
+dirs=
+linker_tool=
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output
index 65f3494..b10f19d 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output
@@ -1,2 +1,3 @@
 libs=Fortran_main\.lib;FortranRuntime\.lib;FortranDecimal\.lib
 dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
+linker_tool=C:/Program Files \(x86\)/Microsoft Visual Studio/2019/Professional/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/link.exe
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output
new file mode 100644
index 0000000..54195df
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-18.0.0-MSVC.output
@@ -0,0 +1,3 @@
+libs=
+dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/18/lib/windows
+linker_tool=C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe
diff --git a/Tests/RunCMake/PrecompileHeaders/foo.c b/Tests/RunCMake/PrecompileHeaders/foo.c
index eb88726..a4710fd 100644
--- a/Tests/RunCMake/PrecompileHeaders/foo.c
+++ b/Tests/RunCMake/PrecompileHeaders/foo.c
@@ -2,12 +2,12 @@
 
 #include "foo2.h"
 
-int foo()
+int foo(void)
 {
   return 0;
 }
 
-int foo2()
+int foo2(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/PrecompileHeaders/foobar.c b/Tests/RunCMake/PrecompileHeaders/foobar.c
index 97d465c..71ebe37 100644
--- a/Tests/RunCMake/PrecompileHeaders/foobar.c
+++ b/Tests/RunCMake/PrecompileHeaders/foobar.c
@@ -2,7 +2,7 @@
 #include "foo.h"
 #include "foo2.h"
 
-int main()
+int main(void)
 {
   int zeroSize = 0;
 
diff --git a/Tests/RunCMake/PrecompileHeaders/include/bar.h b/Tests/RunCMake/PrecompileHeaders/include/bar.h
index 5feb983..89a156c 100644
--- a/Tests/RunCMake/PrecompileHeaders/include/bar.h
+++ b/Tests/RunCMake/PrecompileHeaders/include/bar.h
@@ -1,7 +1,7 @@
 #ifndef bar_h
 #define bar_h
 
-static int bar()
+static int bar(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/PrintHelpers/nothing.c b/Tests/RunCMake/PrintHelpers/nothing.c
index 32b7b39..1d11f33 100644
--- a/Tests/RunCMake/PrintHelpers/nothing.c
+++ b/Tests/RunCMake/PrintHelpers/nothing.c
@@ -1,6 +1,6 @@
 #include "nothing.h"
 
-void nothing()
+void nothing(void)
 {
   (void*)0;
 }
diff --git a/Tests/RunCMake/PrintHelpers/something.c b/Tests/RunCMake/PrintHelpers/something.c
index a2bc425..90482c9 100644
--- a/Tests/RunCMake/PrintHelpers/something.c
+++ b/Tests/RunCMake/PrintHelpers/something.c
@@ -1,6 +1,6 @@
 #include "something.h"
 
-int main()
+int main(void)
 {
   nothing();
   return 0;
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index fcf904e..ecd7fad 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -1,3 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
 foreach(
   arg
   IN ITEMS
@@ -214,6 +217,7 @@
     "|[^\n]*offset in archive not a multiple of 8"
     "|[^\n]*from Time Machine by path"
     "|[^\n]*Bullseye Testing Technology"
+    ${RunCMake_TEST_EXTRA_IGNORE_LINE_REGEX}
     ")[^\n]*\n)+"
     )
   if(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION)
@@ -323,5 +327,21 @@
   endif()
 endfunction()
 
+# Get the user id on unix if possible.
+function(get_unix_uid var)
+  set("${var}" "" PARENT_SCOPE)
+  if(UNIX)
+    set(ID "id")
+    if(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND EXISTS "/usr/xpg4/bin/id")
+      set (ID "/usr/xpg4/bin/id")
+    endif()
+    execute_process(COMMAND ${ID} -u $ENV{USER} OUTPUT_VARIABLE uid ERROR_QUIET
+                    RESULT_VARIABLE status OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if(status EQUAL 0)
+      set("${var}" "${uid}" PARENT_SCOPE)
+    endif()
+  endif()
+endfunction()
+
 # Protect RunCMake tests from calling environment.
 unset(ENV{MAKEFLAGS})
diff --git a/Tests/RunCMake/RunCTest.cmake b/Tests/RunCMake/RunCTest.cmake
index d46f6ad..87d17f2 100644
--- a/Tests/RunCMake/RunCTest.cmake
+++ b/Tests/RunCMake/RunCTest.cmake
@@ -1,3 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
 include(RunCMake)
 
 # Isolate our ctest runs from external environment.
diff --git a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
index ad446e9..180a0fe 100644
--- a/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
+++ b/Tests/RunCMake/RuntimePath/RunCMakeTest.cmake
@@ -21,12 +21,39 @@
   set(cfg_dir /Debug)
 endif()
 
-run_RuntimePath(SymlinkImplicit)
-run_cmake_command(SymlinkImplicitCheck
-  ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/SymlinkImplicit-build -Dcfg_dir=${cfg_dir} -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake)
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  run_RuntimePath(SymlinkImplicit)
+  run_cmake_command(SymlinkImplicitCheck
+    ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/SymlinkImplicit-build -Dcfg_dir=${cfg_dir} -P ${RunCMake_SOURCE_DIR}/SymlinkImplicitCheck.cmake)
 
-run_RuntimePath(Relative)
+  run_RuntimePath(Relative)
 
-run_RuntimePath(Genex)
-run_cmake_command(GenexCheck
-  ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/Genex-build -P ${RunCMake_SOURCE_DIR}/GenexCheck.cmake)
+  run_RuntimePath(Genex)
+  run_cmake_command(GenexCheck
+    ${CMAKE_COMMAND} -Ddir=${RunCMake_BINARY_DIR}/Genex-build -P ${RunCMake_SOURCE_DIR}/GenexCheck.cmake)
+endif()
+
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Stub-build)
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(bin_dir "${RunCMake_TEST_BINARY_DIR}/Debug")
+    set(lib_dir "${RunCMake_TEST_BINARY_DIR}/lib/Debug")
+  else()
+    set(bin_dir "${RunCMake_TEST_BINARY_DIR}")
+    set(lib_dir "${RunCMake_TEST_BINARY_DIR}/lib")
+  endif()
+  run_cmake(Stub)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(Stub-build ${CMAKE_COMMAND} --build . --config Debug)
+  if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|SunOS)$|BSD")
+    set(ldpath LD_LIBRARY_PATH)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "^(Darwin)$")
+    set(ldpath DYLD_LIBRARY_PATH)
+  elseif(CMAKE_SYSTEM_NAME MATCHES "^(AIX)$")
+    set(ldpath LIBPATH)
+  endif()
+  if(ldpath)
+    run_cmake_command(Stub-fail ${CMAKE_COMMAND} -E env LANG=C ${bin_dir}/StubExe)
+    run_cmake_command(Stub-pass ${CMAKE_COMMAND} -E env --modify ${ldpath}=path_list_prepend:${lib_dir} ${bin_dir}/StubExe)
+  endif()
+endblock()
diff --git a/Tests/RunCMake/RuntimePath/Stub-fail-result.txt b/Tests/RunCMake/RuntimePath/Stub-fail-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/Stub-fail-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/RuntimePath/Stub-fail-stderr.txt b/Tests/RunCMake/RuntimePath/Stub-fail-stderr.txt
new file mode 100644
index 0000000..9c17414
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/Stub-fail-stderr.txt
@@ -0,0 +1 @@
+(error while loading shared libraries: libStub\.so\.1|Library not loaded: '?@rpath/libStub\.1\.dylib'?|(Cannot|Could not) load module libStub\.so|fatal: libStub\.so\.1: open failed|Shared object "libStub\.so\.1" not found)
diff --git a/Tests/RunCMake/RuntimePath/Stub.c b/Tests/RunCMake/RuntimePath/Stub.c
new file mode 100644
index 0000000..2ff333a
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/Stub.c
@@ -0,0 +1,4 @@
+int Stub(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/RuntimePath/Stub.cmake b/Tests/RunCMake/RuntimePath/Stub.cmake
new file mode 100644
index 0000000..18e99c1
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/Stub.cmake
@@ -0,0 +1,25 @@
+enable_language(C)
+
+add_library(Stub SHARED Stub.c)
+set_target_properties(Stub PROPERTIES
+  SOVERSION 1
+  LIBRARY_OUTPUT_DIRECTORY lib
+  )
+
+set(StubDir ${CMAKE_CURRENT_BINARY_DIR}/lib/stubs)
+set(Stub "${StubDir}/${CMAKE_SHARED_LIBRARY_PREFIX}Stub${CMAKE_SHARED_LIBRARY_SUFFIX}")
+add_custom_target(StubCopy
+  COMMAND ${CMAKE_COMMAND} -E make_directory "${StubDir}"
+  COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_SONAME_FILE:Stub>" "${Stub}"
+  BYPRODUCTS ${Stub}
+  )
+add_dependencies(StubCopy Stub)
+add_library(Imp::Stub SHARED IMPORTED)
+set_property(TARGET Imp::Stub PROPERTY IMPORTED_IMPLIB "${Stub}")
+add_dependencies(Imp::Stub StubCopy)
+
+add_library(StubUse SHARED StubUse.c)
+target_link_libraries(StubUse PRIVATE Imp::Stub)
+
+add_executable(StubExe StubExe.c)
+target_link_libraries(StubExe PRIVATE StubUse)
diff --git a/Tests/RunCMake/RuntimePath/StubExe.c b/Tests/RunCMake/RuntimePath/StubExe.c
new file mode 100644
index 0000000..14b3fe4
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/StubExe.c
@@ -0,0 +1,5 @@
+extern int StubUse(void);
+int main(void)
+{
+  return StubUse();
+}
diff --git a/Tests/RunCMake/RuntimePath/StubUse.c b/Tests/RunCMake/RuntimePath/StubUse.c
new file mode 100644
index 0000000..ffdaf6d
--- /dev/null
+++ b/Tests/RunCMake/RuntimePath/StubUse.c
@@ -0,0 +1,5 @@
+extern int Stub(void);
+int StubUse(void)
+{
+  return Stub();
+}
diff --git a/Tests/RunCMake/SourceProperties/empty.c b/Tests/RunCMake/SourceProperties/empty.c
index a9ec102..768abc2 100644
--- a/Tests/RunCMake/SourceProperties/empty.c
+++ b/Tests/RunCMake/SourceProperties/empty.c
@@ -1,5 +1,5 @@
 
-int empty()
+int empty(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt
new file mode 100644
index 0000000..82adcda
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-NEW-stderr.txt
@@ -0,0 +1,3 @@
+CMake Warning \(dev\) in CMakeLists.txt:
+  Unknown Swift_COMPILATION_MODE on target 'greetings_who_knows'
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/Swift/CMP0157-NEW.cmake b/Tests/RunCMake/Swift/CMP0157-NEW.cmake
new file mode 100644
index 0000000..96c2ff4
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-NEW.cmake
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.28)
+
+cmake_policy(SET CMP0157 NEW)
+include(CMP0157-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt
new file mode 100644
index 0000000..d593b92
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-OLD-build-stdout.txt
@@ -0,0 +1 @@
+swiftc(.exe)? .* -output-file-map CMakeFiles(/|\\)greetings_default.dir(//|\\\\)output-file-map.json .*
diff --git a/Tests/RunCMake/Swift/CMP0157-OLD.cmake b/Tests/RunCMake/Swift/CMP0157-OLD.cmake
new file mode 100644
index 0000000..6b0ec94
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-OLD.cmake
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.28)
+
+cmake_policy(SET CMP0157 OLD)
+include(CMP0157-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0157-WARN.cmake b/Tests/RunCMake/Swift/CMP0157-WARN.cmake
new file mode 100644
index 0000000..7d8c01d
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-WARN.cmake
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.28)
+
+include(CMP0157-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0157-common.cmake b/Tests/RunCMake/Swift/CMP0157-common.cmake
new file mode 100644
index 0000000..53f14f6
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0157-common.cmake
@@ -0,0 +1,19 @@
+enable_language(Swift)
+
+add_executable(greetings_default hello.swift)
+
+add_executable(greetings_wmo hello.swift)
+set_target_properties(greetings_wmo PROPERTIES
+  Swift_COMPILATION_MODE "wholemodule")
+
+add_executable(greetings_incremental hello.swift)
+set_target_properties(greetings_incremental PROPERTIES
+  Swift_COMPILATION_MODE "incremental")
+
+add_executable(greetings_singlefile hello.swift)
+set_target_properties(greetings_singlefile PROPERTIES
+  Swift_COMPILATION_MODE "singlefile")
+
+add_executable(greetings_who_knows hello.swift)
+set_target_properties(greetings_who_knows PROPERTIES
+  Swift_COMPILATION_MODE "not-a-real-mode")
diff --git a/Tests/RunCMake/Swift/CompileCommands-check.cmake b/Tests/RunCMake/Swift/CompileCommands-check.cmake
new file mode 100644
index 0000000..6450745
--- /dev/null
+++ b/Tests/RunCMake/Swift/CompileCommands-check.cmake
@@ -0,0 +1,30 @@
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/compile_commands.json")
+  set(RunCMake_TEST_FAILED "compile_commands.json not generated")
+  return()
+endif()
+
+# The compile command for both files should contain all Swift source files in
+# the module
+set(expected_compile_commands
+[==[^\[
+{
+  "directory": ".*(/Tests/RunCMake/Swift/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\Swift\\\\CompileCommands-build)",
+  "command": ".*swiftc .* (\\")?.*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?.*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?",
+  "file": ".*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)",
+  "output": "CMakeFiles/CompileCommandLib.dir/E.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\E.swift.obj"
+},
+{
+  "directory": ".*(/Tests/RunCMake/Swift/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\Swift\\\\CompileCommands-build)",
+  "command": ".*swiftc .* (\\")?.*(/Tests/RunCMake/Swift/E.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\E.swift)(\\")? (\\")?.*(/Tests/RunCMake/Swift/L.swift|\\\\Tests\\\\RunCMake\\\\Swift\\\\L.swift)(\\")?",
+  "file": ".*/Tests/RunCMake/Swift/L.swift",
+  "output": "CMakeFiles/CompileCommandLib.dir/L.swift.o|CMakeFiles\\\\CompileCommandLib.dir\\\\L.swift.obj"
+}
+]$]==]
+)
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" compile_commands)
+if(NOT compile_commands MATCHES "${expected_compile_commands}")
+  string(REPLACE "\n" "\n  " expected_compile_commands_formatted "${expected_compile_commands}")
+  string(REPLACE "\n" "\n  " compile_commands_formatted "${compile_commands}")
+  string(APPEND RunCMake_TEST_FAILED "Expected compile_commands.json to match:\n  ${expected_compile_commands_formatted}\nActual compile_commands.json:\n  ${compile_commands_formatted}\n")
+endif()
diff --git a/Tests/RunCMake/Swift/CompileCommands.cmake b/Tests/RunCMake/Swift/CompileCommands.cmake
new file mode 100644
index 0000000..f859693
--- /dev/null
+++ b/Tests/RunCMake/Swift/CompileCommands.cmake
@@ -0,0 +1,9 @@
+if(POLICY CMP0157)
+  cmake_policy(SET CMP0157 NEW)
+endif()
+set(CMAKE_Swift_COMPILATION_MODE "singlefile")
+
+enable_language(Swift)
+
+add_library(CompileCommandLib STATIC E.swift L.swift)
+set_target_properties(CompileCommandLib PROPERTIES EXPORT_COMPILE_COMMANDS YES)
diff --git a/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt b/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt
new file mode 100644
index 0000000..ec396d7
--- /dev/null
+++ b/Tests/RunCMake/Swift/ForceResponseFile-check-stdout.txt
@@ -0,0 +1,2 @@
+swiftc(.exe)? -j [0-9]+ -num-threads [0-9]+ -c @CMakeFiles(/|\\)L.dir(/|\\)L.o(bj)?.swift.rsp
+.*swiftc(.exe)? -emit-library -static -o (libL.a|L.lib) @CMakeFiles(/|\\)L.rsp
diff --git a/Tests/RunCMake/Swift/ForceResponseFile.cmake b/Tests/RunCMake/Swift/ForceResponseFile.cmake
new file mode 100644
index 0000000..7fd4636
--- /dev/null
+++ b/Tests/RunCMake/Swift/ForceResponseFile.cmake
@@ -0,0 +1,13 @@
+if(POLICY CMP0157)
+  cmake_policy(SET CMP0157 NEW)
+endif()
+
+if(NOT CMAKE_GENERATOR STREQUAL "Ninja")
+  message(SEND_ERROR "this test must use Ninja generator, found ${CMAKE_GENERATOR} ")
+endif()
+
+set(CMAKE_NINJA_FORCE_RESPONSE_FILE TRUE)
+
+enable_language(Swift)
+
+add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/ImportLibraryFlags-check-stdout.txt b/Tests/RunCMake/Swift/ImportLibraryFlags-check-stdout.txt
new file mode 100644
index 0000000..9b53820
--- /dev/null
+++ b/Tests/RunCMake/Swift/ImportLibraryFlags-check-stdout.txt
@@ -0,0 +1 @@
+.*-implib:lib\\L.lib
diff --git a/Tests/RunCMake/Swift/ImportLibraryFlags.cmake b/Tests/RunCMake/Swift/ImportLibraryFlags.cmake
new file mode 100644
index 0000000..4c76b40
--- /dev/null
+++ b/Tests/RunCMake/Swift/ImportLibraryFlags.cmake
@@ -0,0 +1,6 @@
+cmake_policy(SET CMP0157 NEW)
+enable_language(Swift)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+add_library(L SHARED L.swift)
diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt
index 7a882f8..efa3336 100644
--- a/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt
+++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stderr.txt
@@ -1,2 +1,2 @@
 ninja explain: A.swiftmodule is dirty
-ninja explain: libB.a is dirty
+ninja explain: (libB.a|B.lib) is dirty
diff --git a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt
index bb08a49..8e5b97c 100644
--- a/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt
+++ b/Tests/RunCMake/Swift/IncrementalSwift-second-stdout.txt
@@ -1,3 +1,3 @@
-.*Linking Swift static library libA.a
-.*Linking Swift static library libB.a
-FAILED: libB.a CMakeFiles/B.dir/b.swift.o B.swiftmodule
+.*Building Swift object A.swiftmodule CMakeFiles(/|\\)A.dir(/|\\)a.swift.o(bj)?
+.*Building Swift object B.swiftmodule CMakeFiles(/|\\)B.dir(/|\\)b.swift.o(bj)?
+FAILED: B.swiftmodule CMakeFiles(/|\\)B.dir(/|\\)b.swift.o(bj)?
diff --git a/Tests/RunCMake/Swift/IncrementalSwift.cmake b/Tests/RunCMake/Swift/IncrementalSwift.cmake
index 092269f..08f3fff 100644
--- a/Tests/RunCMake/Swift/IncrementalSwift.cmake
+++ b/Tests/RunCMake/Swift/IncrementalSwift.cmake
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0157 NEW)
 enable_language(Swift)
 
 # Write initial files to build directory
diff --git a/Tests/RunCMake/Swift/NoWorkToDo-norelink-stdout.txt b/Tests/RunCMake/Swift/NoWorkToDo-norelink-stdout.txt
new file mode 100644
index 0000000..e7b31b7
--- /dev/null
+++ b/Tests/RunCMake/Swift/NoWorkToDo-norelink-stdout.txt
@@ -0,0 +1,2 @@
+.*\[1\/4\].*
+.*\[2\/3\].*
diff --git a/Tests/RunCMake/Swift/NoWorkToDo.cmake b/Tests/RunCMake/Swift/NoWorkToDo.cmake
index 51c2ff3..b58f8ff 100644
--- a/Tests/RunCMake/Swift/NoWorkToDo.cmake
+++ b/Tests/RunCMake/Swift/NoWorkToDo.cmake
@@ -1,5 +1,9 @@
+cmake_policy(SET CMP0157 NEW)
 enable_language(Swift)
-add_executable(hello1 hello.swift)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/hello.swift "")
+
+add_executable(hello1 ${CMAKE_CURRENT_BINARY_DIR}/hello.swift)
 set_target_properties(hello1 PROPERTIES ENABLE_EXPORTS TRUE)
 
-add_executable(hello2 hello.swift)
+add_executable(hello2 ${CMAKE_CURRENT_BINARY_DIR}/hello.swift)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake
index 5537c01..3711efb 100644
--- a/Tests/RunCMake/Swift/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -1,11 +1,15 @@
 include(RunCMake)
 
 if(RunCMake_GENERATOR STREQUAL Xcode)
-  if(XCODE_BELOW_6_1)
+  if(XCODE_VERSION VERSION_LESS 6.1)
     run_cmake(XcodeTooOld)
+  elseif(CMake_TEST_Swift)
+    run_cmake(CMP0157-NEW)
+    run_cmake(CMP0157-OLD)
+    run_cmake(CMP0157-WARN)
   endif()
 elseif(RunCMake_GENERATOR STREQUAL Ninja)
-  if(CMAKE_Swift_COMPILER)
+  if(CMake_TEST_Swift)
     if (CMAKE_SYSTEM_NAME MATCHES "Windows")
       run_cmake_with_options(Win32ExecutableDisallowed)
     else()
@@ -23,33 +27,80 @@
       set(RunCMake_TEST_OUTPUT_MERGE 1)
       run_cmake_command(NoWorkToDo-build ${CMAKE_COMMAND} --build .)
       run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain)
+      file(WRITE ${RunCMake_TEST_BINARY_DIR}/hello.swift "//No-op change\n")
+      run_cmake_command(NoWorkToDo-norelink ${CMAKE_COMMAND} --build . -- -d explain)
+      run_cmake_command(NoWorkToDo-nowork ${CMAKE_COMMAND} --build . -- -d explain)
     endblock()
 
     # Test that intermediate static libraries are rebuilt when the public
     # interface of their dependency changes
     block()
-      set(IncrementalSwift_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/IncrementalSwift-build)
-      set(IncrementalSwift_TEST_NO_CLEAN 1)
-      set(IncrementalSwift_TEST_OUTPUT_MERGE 1)
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/IncrementalSwift-build)
       # Since files are modified during test, the files are created in the cmake
       # file into the build directory
       run_cmake(IncrementalSwift)
-      run_cmake_command(IncrementalSwift-first ${CMAKE_COMMAND} --build ${IncrementalSwift_TEST_BINARY_DIR})
+      set(RunCMake_TEST_NO_CLEAN 1)
+      run_cmake_command(IncrementalSwift-first ${CMAKE_COMMAND} --build .)
 
       # Modify public interface of libA requiring rebuild of libB
-      file(WRITE ${IncrementalSwift_TEST_BINARY_DIR}/a.swift
+      file(WRITE ${RunCMake_TEST_BINARY_DIR}/a.swift
         "public func callA() -> Float { return 32.0 }\n")
 
       # Note: We still expect this to fail, but instead of failure at link time,
       # it should fail while re-compiling libB because the function changed
-      run_cmake_command(IncrementalSwift-second ${CMAKE_COMMAND} --build ${IncrementalSwift_TEST_BINARY_DIR} -- -d explain)
+      run_cmake_command(IncrementalSwift-second ${CMAKE_COMMAND} --build . -- -d explain)
     endblock()
 
+    block()
+      run_cmake(CMP0157-NEW)
+      run_cmake(CMP0157-WARN)
+
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0157-OLD-build)
+      run_cmake(CMP0157-OLD)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command
+      run_cmake_command(CMP0157-OLD-build ${CMAKE_COMMAND} --build . -- -n -v)
+    endblock()
+
+    block()
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CompileCommands-build)
+      run_cmake(CompileCommands)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      run_cmake_command(CompileCommands-check ${CMAKE_COMMAND} --build .)
+    endblock()
+
+    block()
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ForceResponseFile-build)
+      run_cmake(ForceResponseFile)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling
+      run_cmake_command(ForceResponseFile-check ${CMAKE_COMMAND} --build . -- -vn)
+    endblock()
+
+    block()
+      if(CMAKE_SYSTEM_NAME MATCHES Windows)
+        set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build)
+        run_cmake(ImportLibraryFlags)
+        set(RunCMake_TEST_NO_CLEAN 1)
+        run_cmake_command(ImportLibraryFlags-check ${CMAKE_COMMAND} --build . -- -n -v)
+      endif()
+    endblock()
+
+    block()
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftLibraryModuleCommand-build)
+      run_cmake(SwiftLibraryModuleCommand)
+      set(RunCMake_TEST_NO_CLEAN 1)
+      run_cmake_command(SwiftLibraryModuleCommand-check ${CMAKE_COMMAND} --build . -- -n -v)
+    endblock()
   endif()
 elseif(RunCMake_GENERATOR STREQUAL "Ninja Multi-Config")
-  if(CMAKE_Swift_COMPILER)
+  if(CMake_TEST_Swift)
     set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release")
     run_cmake(SwiftSimple)
+
+    run_cmake(CMP0157-NEW)
+    run_cmake(CMP0157-OLD)
+    run_cmake(CMP0157-WARN)
     unset(RunCMake_TEST_OPTIONS)
   endif()
 else()
diff --git a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt
new file mode 100644
index 0000000..85767eb
--- /dev/null
+++ b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand-check-stdout.txt
@@ -0,0 +1,3 @@
+.*swiftc(.exe)? .* -parse-as-library -static -emit-module .* -module-name StaticLibrary [^
+]*
+.*swiftc(.exe)? .* -parse-as-library -emit-module .* -module-name DynamicLibrary
diff --git a/Tests/RunCMake/Swift/SwiftLibraryModuleCommand.cmake b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand.cmake
new file mode 100644
index 0000000..7127751
--- /dev/null
+++ b/Tests/RunCMake/Swift/SwiftLibraryModuleCommand.cmake
@@ -0,0 +1,8 @@
+if(POLICY CMP0157)
+  cmake_policy(SET CMP0157 NEW)
+endif()
+
+enable_language(Swift)
+
+add_library(StaticLibrary STATIC L.swift)
+add_library(DynamicLibrary SHARED L.swift)
diff --git a/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake b/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake
index 02d5447..5e52911 100644
--- a/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake
+++ b/Tests/RunCMake/Swift/Win32ExecutableIgnored.cmake
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0157 NEW)
 enable_language(Swift)
 add_executable(E E.swift)
 set_target_properties(E PROPERTIES
diff --git a/Tests/RunCMake/TIClang/CMakeLists.txt b/Tests/RunCMake/TIClang/CMakeLists.txt
new file mode 100644
index 0000000..94e43ba
--- /dev/null
+++ b/Tests/RunCMake/TIClang/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.29)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/TIClang/RunCMakeTest.cmake b/Tests/RunCMake/TIClang/RunCMakeTest.cmake
new file mode 100644
index 0000000..898de74
--- /dev/null
+++ b/Tests/RunCMake/TIClang/RunCMakeTest.cmake
@@ -0,0 +1,61 @@
+include(RunCMake)
+
+# Test expects to be given a LIST of toolchain directories where a TIClang
+# compiler binary is expected to be found relative to the "bin" directory:
+# "-DCMake_TEST_TICLANG_TOOLCHAINS=<path1>;<path2>;<path3>"
+if(RunCMake_GENERATOR MATCHES "Makefile|Ninja")
+  set(_ticlang_toolchains "${CMake_TEST_TICLANG_TOOLCHAINS}" )
+endif()
+
+function(run_toolchain case)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
+  run_cmake_with_options(${case} ${ARGN})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${case}-build ${CMAKE_COMMAND} --build .)
+endfunction()
+
+foreach(_ticlang_toolchain_path IN LISTS _ticlang_toolchains)
+  file(GLOB _ticlang_toolchain "${_ticlang_toolchain_path}/bin/*clang" )
+  if(_ticlang_toolchain STREQUAL "")
+    message(WARNING
+          "Could not find a TIClang toolchain at: ${_ticlang_toolchain_path}.")
+    continue()
+  endif()
+
+  message(STATUS "Found TIClang toolchain: ${_ticlang_toolchain}")
+
+  if(_ticlang_toolchain MATCHES "tiarmclang")
+    set(LINK_OPTS "--use_memcpy=fast,--use_memset=fast,-llnk.cmd")
+    set(CMAKE_FLAGS "-mcpu=cortex-r5 -Oz")
+  else()
+    set(CMAKE_FLAGS "")
+    set(LINK_OPTS "")
+  endif()
+
+  run_toolchain(ticlang-c
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_C_COMPILER=${_ticlang_toolchain}
+    -DCMAKE_C_FLAGS=${CMAKE_FLAGS}
+    -DCMAKE_C_LINKER_FLAGS=${LINK_OPTS}
+  )
+
+  run_toolchain(ticlang-cxx
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_CXX_COMPILER=${_ticlang_toolchain}
+    -DCMAKE_CXX_FLAGS=${CMAKE_FLAGS}
+    -DCMAKE_CXX_LINKER_FLAGS=${LINK_OPTS}
+  )
+
+  run_toolchain(ticlang-asm
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_ASM_COMPILER=${_ticlang_toolchain}
+    -DCMAKE_ASM_FLAGS=${CMAKE_FLAGS}
+  )
+
+  run_toolchain(ticlang-lib
+    -DCMAKE_SYSTEM_NAME=Generic
+    -DCMAKE_C_COMPILER=${_ticlang_toolchain}
+    -DCMAKE_C_FLAGS=${CMAKE_FLAGS}
+    -DCMAKE_C_LINKER_FLAGS=${LINK_OPTS}
+  )
+endforeach()
diff --git a/Tests/RunCMake/TIClang/libmod.c b/Tests/RunCMake/TIClang/libmod.c
new file mode 100644
index 0000000..50666c9
--- /dev/null
+++ b/Tests/RunCMake/TIClang/libmod.c
@@ -0,0 +1,4 @@
+int ticlang_libfun()
+{
+  return 42;
+}
diff --git a/Tests/RunCMake/TIClang/module.c b/Tests/RunCMake/TIClang/module.c
new file mode 100644
index 0000000..46d7571
--- /dev/null
+++ b/Tests/RunCMake/TIClang/module.c
@@ -0,0 +1,14 @@
+#include "module.h"
+#if defined(__USE_LIBFUN)
+extern int ticlang_libfun();
+#endif
+int i;
+int main()
+{
+#if defined(__USE_LIBFUN)
+  i = ticlang_libfun();
+#else
+  i = INTERNAL;
+#endif
+  return i;
+}
diff --git a/Tests/RunCMake/TIClang/module.cxx b/Tests/RunCMake/TIClang/module.cxx
new file mode 100644
index 0000000..b4d46b1
--- /dev/null
+++ b/Tests/RunCMake/TIClang/module.cxx
@@ -0,0 +1,7 @@
+#include "module.h"
+int i;
+int main()
+{
+  i = INTERNAL;
+  return i;
+}
diff --git a/Tests/RunCMake/TIClang/module.h b/Tests/RunCMake/TIClang/module.h
new file mode 100644
index 0000000..a8a85a6
--- /dev/null
+++ b/Tests/RunCMake/TIClang/module.h
@@ -0,0 +1,12 @@
+#ifndef __MODULE_H__
+#define __MODULE_H__
+
+#if defined(__cplusplus)
+#  define INTERNAL 64
+#elif !defined(__cplusplus)
+#  define INTERNAL 32
+#else
+#  error "Unable to determine INTERNAL symbol."
+#endif
+
+#endif /* __MODULE_H__ */
diff --git a/Tests/RunCMake/TIClang/module.s b/Tests/RunCMake/TIClang/module.s
new file mode 100644
index 0000000..df16350
--- /dev/null
+++ b/Tests/RunCMake/TIClang/module.s
@@ -0,0 +1,9 @@
+	.text
+	.syntax unified
+	.section	.text.main,"ax",%progbits
+	.hidden	main
+	.globl	main
+	.p2align	4
+main:
+	nop
+	bx	lr
diff --git a/Tests/RunCMake/TIClang/ticlang-asm.cmake b/Tests/RunCMake/TIClang/ticlang-asm.cmake
new file mode 100644
index 0000000..f6c27fc
--- /dev/null
+++ b/Tests/RunCMake/TIClang/ticlang-asm.cmake
@@ -0,0 +1,5 @@
+enable_language(ASM)
+
+add_executable(exec-asm)
+target_sources(exec-asm PRIVATE module.s)
+target_link_options(exec-asm PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/TIClang/ticlang-c.cmake b/Tests/RunCMake/TIClang/ticlang-c.cmake
new file mode 100644
index 0000000..a36f096
--- /dev/null
+++ b/Tests/RunCMake/TIClang/ticlang-c.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+add_executable(exec-c)
+target_sources(exec-c PRIVATE module.c)
+target_link_options(exec-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/TIClang/ticlang-cxx.cmake b/Tests/RunCMake/TIClang/ticlang-cxx.cmake
new file mode 100644
index 0000000..6b005b5
--- /dev/null
+++ b/Tests/RunCMake/TIClang/ticlang-cxx.cmake
@@ -0,0 +1,5 @@
+enable_language(CXX)
+
+add_executable(exec-cxx)
+target_sources(exec-cxx PRIVATE module.cxx)
+target_link_options(exec-cxx PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/TIClang/ticlang-lib.cmake b/Tests/RunCMake/TIClang/ticlang-lib.cmake
new file mode 100644
index 0000000..e47647c
--- /dev/null
+++ b/Tests/RunCMake/TIClang/ticlang-lib.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+add_library(ticlang-test-lib)
+target_sources(ticlang-test-lib PRIVATE libmod.c)
+
+add_executable(exec-lib-c)
+target_sources(exec-lib-c PRIVATE module.c)
+target_compile_definitions(exec-lib-c PRIVATE __USE_LIBFUN)
+target_link_libraries(exec-lib-c LINK_PUBLIC ticlang-test-lib)
+target_link_options(exec-lib-c PRIVATE ${LINKER_OPTS})
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index c2187ae..6b462d0 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -39,6 +39,9 @@
    \* CMP0142
    \* CMP0154
    \* CMP0155
+   \* CMP0156
+   \* CMP0157
+   \* CMP0160
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt
index f3c068a..efa7b32 100644
--- a/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt
+++ b/Tests/RunCMake/ToolchainFile/CMP0126-OLD-stderr.txt
@@ -1 +1,12 @@
-^try_compile CMP0126='OLD' VAR='2'
+^CMake Deprecation Warning at CMP0126-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0126 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+try_compile CMP0126='OLD' VAR='2'
diff --git a/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt b/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt
new file mode 100644
index 0000000..8956278
--- /dev/null
+++ b/Tests/RunCMake/TransformDepfile/noexist-gcc-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning:
+  Expected depfile does not exist.
+
+    .*/Tests/RunCMake/TransformDepfile/noexist.d$
diff --git a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
index e3643c0..0ec8c42 100644
--- a/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
+++ b/Tests/RunCMake/UnityBuild/RunCMakeTest.cmake
@@ -14,6 +14,13 @@
 run_cmake(unitybuild_cxx_group)
 run_cmake(unitybuild_c_and_cxx)
 run_cmake(unitybuild_c_and_cxx_group)
+if(CMake_TEST_OBJC)
+  run_cmake(unitybuild_objc)
+  run_cmake(unitybuild_objc_group)
+  run_cmake(unitybuild_objcxx)
+  run_cmake(unitybuild_objcxx_group)
+  run_cmake(unitybuild_c_and_cxx_and_objc_and_objcxx)
+endif()
 run_cmake(unitybuild_batchsize)
 run_cmake(unitybuild_default_batchsize)
 run_cmake(unitybuild_skip)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake
new file mode 100644
index 0000000..096a86b
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_c_and_cxx_and_objc_and_objcxx.cmake
@@ -0,0 +1,25 @@
+project(unitybuild_c_and_cxx_and_objc_and_objcxx C CXX OBJC OBJCXX)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src_c "${CMAKE_CURRENT_BINARY_DIR}/s${s}.c")
+  file(WRITE "${src_c}" "int s${s}(void) { return 0; }\n")
+
+  set(src_cxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.cxx")
+  file(WRITE "${src_cxx}" "int s${s}(void) { return 0; }\n")
+
+  set(src_objc "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m")
+  file(WRITE "${src_objc}" "int s${s}(void) { return 0; }\n")
+
+  set(src_objcxx "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm")
+  file(WRITE "${src_objcxx}" "int s${s}(void) { return 0; }\n")
+
+  list(APPEND srcs "${src_c}")
+  list(APPEND srcs "${src_cxx}")
+  list(APPEND srcs "${src_objc}")
+  list(APPEND srcs "${src_objcxx}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake
new file mode 100644
index 0000000..cc88d98
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_objc.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_objc OBJC)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake
new file mode 100644
index 0000000..384c98a
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_objc_group.cmake
@@ -0,0 +1,27 @@
+project(unitybuild_objc_group OBJC)
+
+set(srcs "")
+foreach(s RANGE 1 4)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.m")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+foreach(s RANGE 1 2)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/odr${s}.m")
+  file(WRITE "${src}" "namespace odr { int s${s}(void) { return 0; } }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON
+                          UNITY_BUILD_MODE GROUP
+                          )
+
+set_source_files_properties(s1.m s2.m odr1.m
+                            PROPERTIES UNITY_GROUP "a"
+                            )
+set_source_files_properties(s3.m s4.m odr2.m
+                            PROPERTIES UNITY_GROUP "b"
+                            )
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake
new file mode 100644
index 0000000..fd0f743
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_objcxx.cmake
@@ -0,0 +1,12 @@
+project(unitybuild_objcxx OBJCXX)
+
+set(srcs "")
+foreach(s RANGE 1 8)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON)
diff --git a/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake b/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake
new file mode 100644
index 0000000..517703e
--- /dev/null
+++ b/Tests/RunCMake/UnityBuild/unitybuild_objcxx_group.cmake
@@ -0,0 +1,27 @@
+project(unitybuild_objcxx_group OBJCXX)
+
+set(srcs "")
+foreach(s RANGE 1 4)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/s${s}.mm")
+  file(WRITE "${src}" "int s${s}(void) { return 0; }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+foreach(s RANGE 1 2)
+  set(src "${CMAKE_CURRENT_BINARY_DIR}/odr${s}.mm")
+  file(WRITE "${src}" "namespace odr { int s${s}(void) { return 0; } }\n")
+  list(APPEND srcs "${src}")
+endforeach()
+
+add_library(tgt SHARED ${srcs})
+
+set_target_properties(tgt PROPERTIES UNITY_BUILD ON
+                          UNITY_BUILD_MODE GROUP
+                          )
+
+set_source_files_properties(s1.mm s2.mm odr1.mm
+                            PROPERTIES UNITY_GROUP "a"
+                            )
+set_source_files_properties(s3.mm s4.mm odr2.mm
+                            PROPERTIES UNITY_GROUP "b"
+                            )
diff --git a/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake b/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake
index a26c278..df14d15 100644
--- a/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake
+++ b/Tests/RunCMake/UseSWIG/CMP0122-NEW-check.cmake
@@ -1,5 +1,5 @@
 
-cmake_policy(VERSION 3.1)
+cmake_policy(VERSION 3.5)
 
 file(STRINGS "${RunCMake_TEST_BINARY_DIR}/CMP0122-library-name.txt" prefixes)
 
diff --git a/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake b/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake
index 01657d0..6eb5c20 100644
--- a/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake
+++ b/Tests/RunCMake/UseSWIG/CMP0122-OLD-check.cmake
@@ -1,5 +1,5 @@
 
-cmake_policy(VERSION 3.1)
+cmake_policy(VERSION 3.5)
 
 file(STRINGS "${RunCMake_TEST_BINARY_DIR}/CMP0122-library-name.txt" prefixes)
 
diff --git a/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt
new file mode 100644
index 0000000..84f2ee7
--- /dev/null
+++ b/Tests/RunCMake/UseSWIG/CMP0122-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0122-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0122 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 669049a..e0d74cf 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -11,6 +11,9 @@
 if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 1[1-5] ")
   run_cmake(CustomCommandParallel)
 endif()
+run_cmake_with_options(VsCharacterSet -DSET_CHARSET=MultiByte)
+run_cmake_with_options(VsCharacterSet -DSET_CHARSET=Unicode)
+run_cmake_with_options(VsCharacterSet -DSET_CHARSET=NotSet)
 run_cmake(VsCsharpSourceGroup)
 run_cmake(VsCSharpCompilerOpts)
 run_cmake(ExplicitCMakeLists)
diff --git a/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake
index 17e7b46..c904cd0 100644
--- a/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake
+++ b/Tests/RunCMake/VS10Project/UnityBuildPre2017-check.cmake
@@ -27,7 +27,7 @@
   endif()
 endforeach()
 
-string(REPLACE "\\" "/" unity_source_line ${unity_source_line})
+string(REPLACE "\\" "/" unity_source_line "${unity_source_line}")
 string(FIND "${unity_source_line}" "CMakeFiles/tgt.dir/Unity/unity_0_c.c" unity_source_file_position)
 if (unity_source_file_position EQUAL "-1")
   set(RunCMake_TEST_FAILED "Generated project should include the generated unity source file.")
diff --git a/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake b/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake
new file mode 100644
index 0000000..93770a1
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCharacterSet-check.cmake
@@ -0,0 +1,49 @@
+macro(check_project_file projectFile outvar)
+  set(insideConfiguration FALSE)
+  set(characterSetFound FALSE)
+
+  if(NOT EXISTS "${projectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.")
+    return()
+  endif()
+
+  string(REPLACE "${RunCMake_TEST_BINARY_DIR}/" "" projectName ${projectFile})
+
+  file(STRINGS "${projectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<PropertyGroup Condition=\"'[$][(]Configuration[)]|[$][(]Platform[)]'=='([^\"])+\" Label=\"Configuration\">.*$")
+      set(insideConfiguration TRUE)
+    elseif(insideConfiguration)
+      if(line MATCHES "^ *</PropertyGroup>.*$")
+        set(insideConfiguration FALSE)
+      elseif(line MATCHES "^ *<CharacterSet>(.+)</CharacterSet>*$")
+        message(STATUS "Found CharacterSet = ${CMAKE_MATCH_1} in PropertyGroup 'Configuration' in ${projectName}")
+        set(characterSetFound TRUE)
+        set(${outvar} ${CMAKE_MATCH_1})
+      endif()
+    endif()
+  endforeach()
+  if(NOT characterSetFound)
+    set(RunCMake_TEST_FAILED "CharacterSet not found in \"Configuration\" propertygroup in ${projectName}")
+    return() # This should intentionally return from the caller, not the macro
+  endif()
+endmacro()
+
+check_project_file("${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${CMAKE_VERSION}/CompilerIdCXX/CompilerIdCXX.vcxproj" MULTI_BYTE_CHARSET)
+check_project_file("${RunCMake_TEST_BINARY_DIR}/foo.vcxproj" OVERRIDDEN_CHARSET)
+
+if (NOT "${MULTI_BYTE_CHARSET}" STREQUAL "MultiByte")
+  set(RunCMake_TEST_FAILED "Default character-set (\"MultiByte\") was overridden (it shouldn't)")
+  return()
+endif()
+
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/set_charset.txt")
+  set(RunCMake_TEST_FAILED "File 'set_charset.txt' with set charset does not exist.")
+  return()
+endif()
+file(STRINGS "${RunCMake_TEST_BINARY_DIR}/set_charset.txt" SET_CHARSET)
+
+if (NOT "${OVERRIDDEN_CHARSET}" STREQUAL "${SET_CHARSET}")
+  set(RunCMake_TEST_FAILED "Failed to override the character-set with \"${SET_CHARSET}\"")
+  return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsCharacterSet.cmake b/Tests/RunCMake/VS10Project/VsCharacterSet.cmake
new file mode 100644
index 0000000..c8c3e0e
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsCharacterSet.cmake
@@ -0,0 +1,17 @@
+enable_language(CXX)
+
+# Write value of `SET_CHARSET` for comparison later.
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/set_charset.txt" "${SET_CHARSET}")
+
+# Set macro which determines the character-set.
+if("${SET_CHARSET}" STREQUAL "MultiByte")
+  add_compile_definitions(_MBCS=1)
+endif()
+if("${SET_CHARSET}" STREQUAL "NotSet")
+  add_compile_definitions(_SBCS=1)
+endif()
+if("${SET_CHARSET}" STREQUAL "Unicode")
+  add_compile_definitions(_UNICODE=1)
+endif()
+
+add_library(foo foo.cpp)
diff --git a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake
index 75adb00..0d181ca 100644
--- a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake
@@ -114,3 +114,173 @@
 
 run_cmake(find-library)
 run_cmake_command(find-library-script ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/find-library.cmake)
+
+file(REMOVE_RECURSE ${RunCMake_BINARY_DIR}/export-install)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-macos-build)
+run_cmake_with_options(export-macos -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(export-macos-build ${CMAKE_COMMAND} --build . ${_config_arg})
+run_cmake_command(export-macos-install ${CMAKE_COMMAND} --install . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-build)
+run_cmake_with_options(export-ios -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos "-DCMAKE_OSX_ARCHITECTURES=arm64" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(export-ios-build ${CMAKE_COMMAND} --build . ${_config_arg})
+run_cmake_command(export-ios-install ${CMAKE_COMMAND} --install . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-ios-simulator-build)
+run_cmake_with_options(export-ios-simulator -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphonesimulator "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/export-install)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(export-ios-simulator-build ${CMAKE_COMMAND} --build . ${_config_arg})
+run_cmake_command(export-ios-simulator-install ${CMAKE_COMMAND} --install . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-no-xcframework-build)
+run_cmake_with_options(import-macos-install-specific-no-xcframework -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+set(_config_dir)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+  set(_config_dir /Release)
+endif()
+run_cmake_command(import-macos-install-specific-no-xcframework-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_NO_CLEAN 1)
+if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  # 'xcodebuild -create-xcframework' fails on symlinked paths.
+  file(REAL_PATH "${RunCMake_SOURCE_DIR}" src_dir)
+  file(REAL_PATH "${RunCMake_BINARY_DIR}" bld_dir)
+else()
+  set(src_dir "${RunCMake_SOURCE_DIR}")
+  set(bld_dir "${RunCMake_BINARY_DIR}")
+endif()
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-install)
+run_cmake_command(export-install-xcframework xcodebuild -create-xcframework
+  -output ${bld_dir}/export-install/lib/mylib.xcframework
+  -library ${bld_dir}/export-install/lib/macos/libmylib.a
+  -headers ${src_dir}/mylib/include
+  -library ${bld_dir}/export-install/lib/ios/libmylib.a
+  -headers ${src_dir}/mylib/include
+  -library ${bld_dir}/export-install/lib/ios-simulator/libmylib.a
+  -headers ${src_dir}/mylib/include
+  )
+run_cmake_command(export-install-xcframework-genex xcodebuild -create-xcframework
+  -output ${bld_dir}/export-install/lib2/mylib-genex.xcframework
+  -library ${bld_dir}/export-install/lib/macos/libmylib-genex.a
+  -headers ${src_dir}/mylib/include
+  -library ${bld_dir}/export-install/lib/ios/libmylib-genex.a
+  -headers ${src_dir}/mylib/include
+  -library ${bld_dir}/export-install/lib/ios-simulator/libmylib-genex.a
+  -headers ${src_dir}/mylib/include
+  )
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/export-macos-build)
+run_cmake_command(export-build-macos-xcframework xcodebuild -create-xcframework
+  -output ${bld_dir}/export-macos-build/lib/mylib.xcframework
+  -library ${bld_dir}/export-macos-build/lib/macos${_config_dir}/libmylib.a
+  -headers ${src_dir}/mylib/include
+  )
+run_cmake_command(export-build-macos-xcframework-genex xcodebuild -create-xcframework
+  -output ${bld_dir}/export-macos-build/lib/mylib-genex.xcframework
+  -library ${bld_dir}/export-macos-build/lib/macos${_config_dir}/libmylib-genex.a
+  -headers ${src_dir}/mylib/include
+  )
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-build)
+run_cmake_with_options(import-macos-install-specific -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-install-specific-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-specific-build)
+run_cmake_with_options(import-macos-build-specific -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/macos/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-build-specific-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-specific-genex-build)
+run_cmake_with_options(import-macos-install-specific-genex -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/macos/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-install-specific-genex-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-specific-genex-build)
+run_cmake_with_options(import-macos-build-specific-genex -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/macos/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-build-specific-genex-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-install-general-build)
+run_cmake_with_options(import-macos-install-general -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-install-general-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-macos-build-general-build)
+run_cmake_with_options(import-macos-build-general -DCMAKE_SYSTEM_NAME=Darwin -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-macos-build/lib/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-macos-build-general-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/import-ios-install-general-build)
+run_cmake_with_options(import-ios-install-general -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_ARCHITECTURES=arm64 -Dmylib_DIR=${RunCMake_BINARY_DIR}/export-install/lib/cmake/mylib)
+set(RunCMake_TEST_NO_CLEAN 1)
+set(_config_arg)
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(_config_arg --config Release)
+endif()
+run_cmake_command(import-ios-install-general-build ${CMAKE_COMMAND} --build . ${_config_arg})
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
diff --git a/Tests/RunCMake/XcFramework/export-common.cmake b/Tests/RunCMake/XcFramework/export-common.cmake
new file mode 100644
index 0000000..844b7d3
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/export-common.cmake
@@ -0,0 +1,46 @@
+enable_language(C)
+
+include(CMakePackageConfigHelpers)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_library(mylib STATIC mylib/mylib.c)
+target_include_directories(mylib INTERFACE $<INSTALL_INTERFACE:include>)
+set_property(TARGET mylib PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib/${platform_name})
+
+add_library(mylib-genex STATIC mylib/mylib.c)
+target_include_directories(mylib-genex INTERFACE $<INSTALL_INTERFACE:include>)
+set_property(TARGET mylib-genex PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib/${platform_name})
+
+install(TARGETS mylib mylib-genex DESTINATION lib/${platform_name} EXPORT mylib)
+install(FILES mylib/include/mylib/mylib.h DESTINATION include/mylib)
+export(SETUP mylib
+  TARGET mylib XCFRAMEWORK_LOCATION lib/mylib.xcframework
+  TARGET mylib-genex XCFRAMEWORK_LOCATION "$<BUILD_INTERFACE:lib/$<TARGET_PROPERTY:NAME>.xcframework>$<INSTALL_INTERFACE:lib2/$<TARGET_PROPERTY:NAME>.xcframework>"
+  )
+install(EXPORT mylib DESTINATION lib/${platform_name}/cmake/mylib FILE mylib-targets.cmake)
+export(EXPORT mylib FILE lib/${platform_name}/cmake/mylib/mylib-targets.cmake)
+
+configure_package_config_file(mylib-config.cmake.in mylib-config-sub.cmake INSTALL_DESTINATION lib/${platform_name}/cmake/mylib)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-sub.cmake DESTINATION lib/${platform_name}/cmake/mylib RENAME mylib-config.cmake)
+
+configure_package_config_file(mylib-config.cmake.in lib/${platform_name}/cmake/mylib/mylib-config.cmake INSTALL_DESTINATION lib/${platform_name}/cmake/mylib)
+
+generate_apple_platform_selection_file(mylib-config-top.cmake
+  INSTALL_DESTINATION lib/cmake/mylib
+  MACOS_INCLUDE_FILE lib/macos/cmake/mylib/mylib-config.cmake
+  IOS_INCLUDE_FILE lib/ios/cmake/mylib/mylib-config.cmake
+  IOS_SIMULATOR_INCLUDE_FILE lib/ios-simulator/cmake/mylib/mylib-config.cmake
+  )
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/mylib-config-top.cmake DESTINATION lib/cmake/mylib RENAME mylib-config.cmake)
+
+generate_apple_platform_selection_file(lib/cmake/mylib/mylib-config.cmake
+  INSTALL_DESTINATION lib/cmake/mylib
+  "${platform_arg}" lib/${platform_name}/cmake/mylib/mylib-config.cmake
+  )
diff --git a/Tests/RunCMake/XcFramework/export-ios-simulator.cmake b/Tests/RunCMake/XcFramework/export-ios-simulator.cmake
new file mode 100644
index 0000000..610e3d1
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/export-ios-simulator.cmake
@@ -0,0 +1,3 @@
+set(platform_name ios-simulator)
+set(platform_arg IOS_SIMULATOR_INCLUDE_FILE)
+include(export-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/export-ios.cmake b/Tests/RunCMake/XcFramework/export-ios.cmake
new file mode 100644
index 0000000..2a81310
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/export-ios.cmake
@@ -0,0 +1,3 @@
+set(platform_name ios)
+set(platform_arg IOS_INCLUDE_FILE)
+include(export-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/export-macos.cmake b/Tests/RunCMake/XcFramework/export-macos.cmake
new file mode 100644
index 0000000..7edc1ed
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/export-macos.cmake
@@ -0,0 +1,3 @@
+set(platform_name macos)
+set(platform_arg MACOS_INCLUDE_FILE)
+include(export-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-common.cmake b/Tests/RunCMake/XcFramework/import-common.cmake
new file mode 100644
index 0000000..4def6a4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-common.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+find_package(mylib REQUIRED)
+
+add_custom_target(print_loc ALL COMMAND ${CMAKE_COMMAND} -E echo "mylib location: $<TARGET_FILE:mylib>")
diff --git a/Tests/RunCMake/XcFramework/import-genex-common.cmake b/Tests/RunCMake/XcFramework/import-genex-common.cmake
new file mode 100644
index 0000000..e46902b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-genex-common.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+
+find_package(mylib REQUIRED)
+
+add_custom_target(print_loc ALL COMMAND ${CMAKE_COMMAND} -E echo "mylib-genex location: $<TARGET_FILE:mylib-genex>")
diff --git a/Tests/RunCMake/XcFramework/import-ios-install-general.cmake b/Tests/RunCMake/XcFramework/import-ios-install-general.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-ios-install-general.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt
new file mode 100644
index 0000000..3ac467d
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-general-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib location: [^
+]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-general.cmake b/Tests/RunCMake/XcFramework/import-macos-build-general.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-general.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt
new file mode 100644
index 0000000..3ac467d
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib location: [^
+]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt
new file mode 100644
index 0000000..d3a20e8
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib-genex location: [^
+]*/Tests/RunCMake/XcFramework/export-macos-build/lib/mylib-genex\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib-genex\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake
new file mode 100644
index 0000000..a061bd3
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-specific-genex.cmake
@@ -0,0 +1 @@
+include(import-genex-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake b/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-build-specific.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt
new file mode 100644
index 0000000..1421246
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-general-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib location: [^
+]*/Tests/RunCMake/XcFramework/export-install/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-general.cmake b/Tests/RunCMake/XcFramework/import-macos-install-general.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-general.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt
new file mode 100644
index 0000000..1421246
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib location: [^
+]*/Tests/RunCMake/XcFramework/export-install/lib/mylib\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt
new file mode 100644
index 0000000..5c88758
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib-genex location: [^
+]*/Tests/RunCMake/XcFramework/export-install/lib2/mylib-genex\.xcframework/macos-(arm64|x86_64|arm64_x86_64)/libmylib-genex\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake
new file mode 100644
index 0000000..a061bd3
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-genex.cmake
@@ -0,0 +1 @@
+include(import-genex-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt
new file mode 100644
index 0000000..1c92972
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework-build-stdout.txt
@@ -0,0 +1,2 @@
+mylib location: [^
+]*/Tests/RunCMake/XcFramework/export-install/lib/macos/libmylib\.a
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific-no-xcframework.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake b/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake
new file mode 100644
index 0000000..08ef6db
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/import-macos-install-specific.cmake
@@ -0,0 +1 @@
+include(import-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/mylib-config.cmake.in b/Tests/RunCMake/XcFramework/mylib-config.cmake.in
new file mode 100644
index 0000000..878d6e8
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/mylib-config.cmake.in
@@ -0,0 +1,3 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/mylib-targets.cmake")
diff --git a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
index 91b413a..630f13a 100644
--- a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
+++ b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
@@ -25,6 +25,6 @@
 #  error unknown OS
 #endif
 
-void foo()
+void foo(void)
 {
 }
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake
new file mode 100644
index 0000000..706add5
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS-check.cmake
@@ -0,0 +1,3 @@
+include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
+
+findAttribute(${test} "Embed XPC Services" TRUE)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake
new file mode 100644
index 0000000..5ad0436
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices-macOS.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/EmbedXPCServices.cmake)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake
new file mode 100644
index 0000000..877a685
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedXPCServices.cmake
@@ -0,0 +1,17 @@
+add_executable(xpc_service MACOSX_BUNDLE main.m)
+set_target_properties(xpc_service PROPERTIES
+  BUNDLE_EXTENSION "xpc"
+  XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
+  XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+  MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/XPCService.Info.plist.in"
+  MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app.xpc_service"
+)
+
+add_executable(app MACOSX_BUNDLE main.m)
+add_dependencies(app xpc_service)
+set_target_properties(app PROPERTIES
+    XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
+    XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+    XCODE_EMBED_XPC_SERVICES xpc_service
+    MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app"
+)
diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
index 3798ddc..77ac63f 100644
--- a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
@@ -122,4 +122,5 @@
   TestEmbedCommon(Resources macOS)
   TestEmbedCommon(Resources iOS)
   TestEmbedCommon(PlugIns macOS)
+  TestEmbedCommon(XPCServices macOS)
 endif()
diff --git a/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in b/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in
new file mode 100644
index 0000000..abc8db2
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/XPCService.Info.plist.in
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleDisplayName</key>
+	<string>SomeExtension</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.example.app.xpc_service</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>example_app_xpc_service</string>
+	<key>CFBundlePackageType</key>
+	<string>XPC!</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0.0</string>
+	<key>CFBundleVersion</key>
+	<string>1.0.0</string>
+</dict>
+</plist>
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake
new file mode 100644
index 0000000..4e139d4
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration-check.cmake
@@ -0,0 +1,19 @@
+
+set(schema "${RunCMake_TEST_BINARY_DIR}/XcodeSchemaGeneration.xcodeproj/xcshareddata/xcschemes/foo.xcscheme")
+
+if(NOT EXISTS "${schema}")
+  set(RunCMake_TEST_FAILED "Generated schema ${schema} does not exist.")
+  return()
+endif()
+
+execute_process(COMMAND
+  /usr/bin/xmllint --xpath "//Scheme/ProfileAction/BuildableProductRunnable" ${schema}
+  OUTPUT_VARIABLE stdout
+  ERROR_VARIABLE stderr
+  RESULT_VARIABLE exit_code
+  ERROR_STRIP_TRAILING_WHITESPACE
+)
+if(exit_code)
+  set(RunCMake_TEST_FAILED "Failed to find BuildableProductRunnable for profile action: ${stderr}")
+  return()
+endif()
diff --git a/Tests/RunCMake/XcodeProject/XcodeXCConfig.c b/Tests/RunCMake/XcodeProject/XcodeXCConfig.c
index ac59a6b..20aab00 100644
--- a/Tests/RunCMake/XcodeProject/XcodeXCConfig.c
+++ b/Tests/RunCMake/XcodeProject/XcodeXCConfig.c
@@ -15,6 +15,6 @@
 #  error TARGET_DEBUG does not match BUILD_DEBUG
 #endif
 
-void some_symbol()
+void some_symbol(void)
 {
 }
diff --git a/Tests/RunCMake/add_compile_definitions/foo.c b/Tests/RunCMake/add_compile_definitions/foo.c
index 74a86e1..7d75e37 100644
--- a/Tests/RunCMake/add_compile_definitions/foo.c
+++ b/Tests/RunCMake/add_compile_definitions/foo.c
@@ -1,4 +1,4 @@
 
-void foo()
+void foo(void)
 {
 }
diff --git a/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt b/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt
index bf49657..76708c4 100644
--- a/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt
+++ b/Tests/RunCMake/add_custom_command/CommentGenex-build-stdout.txt
@@ -1 +1 @@
-lorem ipsum, 01
+"lorem ipsum, 01"
diff --git a/Tests/RunCMake/add_custom_command/CommentGenex.cmake b/Tests/RunCMake/add_custom_command/CommentGenex.cmake
index f517392..b3931f4 100644
--- a/Tests/RunCMake/add_custom_command/CommentGenex.cmake
+++ b/Tests/RunCMake/add_custom_command/CommentGenex.cmake
@@ -3,7 +3,7 @@
 add_custom_command(
   OUTPUT out.txt
   COMMAND ${CMAKE_COMMAND} -E echo true
-  COMMENT "$<TARGET_PROPERTY:helper,MY_TEXT>$<COMMA> $<STREQUAL:foo,bar>$<EQUAL:42,42>"
+  COMMENT "$<QUOTE>$<TARGET_PROPERTY:helper,MY_TEXT>$<COMMA> $<STREQUAL:foo,bar>$<EQUAL:42,42>$<QUOTE>"
 )
 set_property(SOURCE out.txt PROPERTY SYMBOLIC 1)
 add_custom_target(main ALL DEPENDS out.txt)
diff --git a/Tests/RunCMake/add_custom_command/a.c b/Tests/RunCMake/add_custom_command/a.c
index 707c1c3..4ef3698 100644
--- a/Tests/RunCMake/add_custom_command/a.c
+++ b/Tests/RunCMake/add_custom_command/a.c
@@ -1,3 +1,3 @@
-void a()
+void a(void)
 {
 }
diff --git a/Tests/RunCMake/add_dependencies/a.c b/Tests/RunCMake/add_dependencies/a.c
index 707c1c3..4ef3698 100644
--- a/Tests/RunCMake/add_dependencies/a.c
+++ b/Tests/RunCMake/add_dependencies/a.c
@@ -1,3 +1,3 @@
-void a()
+void a(void)
 {
 }
diff --git a/Tests/RunCMake/add_dependencies/b.c b/Tests/RunCMake/add_dependencies/b.c
index 57b2900..c7c7df4 100644
--- a/Tests/RunCMake/add_dependencies/b.c
+++ b/Tests/RunCMake/add_dependencies/b.c
@@ -1,3 +1,3 @@
-void b()
+void b(void)
 {
 }
diff --git a/Tests/RunCMake/add_dependencies/c.c b/Tests/RunCMake/add_dependencies/c.c
index cbf94ca..e2fa6de 100644
--- a/Tests/RunCMake/add_dependencies/c.c
+++ b/Tests/RunCMake/add_dependencies/c.c
@@ -1,3 +1,3 @@
-void c()
+void c(void)
 {
 }
diff --git a/Tests/RunCMake/add_test/RunCMakeTest.cmake b/Tests/RunCMake/add_test/RunCMakeTest.cmake
index ec6f6dd..6e7d53a 100644
--- a/Tests/RunCMake/add_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/add_test/RunCMakeTest.cmake
@@ -41,3 +41,20 @@
   set(RunCMake_TEST_NO_CLEAN 1)
   run_cmake_command(EmptyArgument-ctest ${CMAKE_CTEST_COMMAND} -C Debug)
 endblock()
+
+set(RunCMake_TEST_OPTIONS "-DCMAKE_TEST_LAUNCHER=${PSEUDO_EMULATOR}")
+run_cmake(TestLauncherProperty)
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestLauncher-build)
+
+  run_cmake(TestLauncher)
+  unset(RunCMake_TEST_OPTIONS)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
+  run_cmake_command(TestLauncher-build ${CMAKE_COMMAND} --build . --config Debug)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
+
+  run_cmake_command(TestLauncher-test ${CMAKE_CTEST_COMMAND} -C Debug -V)
+endblock()
+unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/add_test/TestLauncher-check.cmake b/Tests/RunCMake/add_test/TestLauncher-check.cmake
new file mode 100644
index 0000000..d1396c2
--- /dev/null
+++ b/Tests/RunCMake/add_test/TestLauncher-check.cmake
@@ -0,0 +1,38 @@
+set(testfile "${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake")
+if(EXISTS "${testfile}")
+  file(READ "${testfile}" testfile_contents)
+else()
+  set(RunCMake_TEST_FAILED "Could not find expected CTestTestfile.cmake.")
+  return()
+endif()
+
+set(error_details "There is a problem with generated test file:\n  ${testfile}")
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncher [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used test launcher when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncher [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use test launcher when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncherWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used test launcher when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Did not use test launcher when it should be used. ${error_details}")
+  return()
+endif()
+
+if(testfile_contents MATCHES "add_test[(]DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex [^$<>\n]+pseudo_emulator[^$<>\n]+\n")
+  set(RunCMake_TEST_FAILED "Used test launcher when it should not be used. ${error_details}")
+  return()
+endif()
+
+if(NOT testfile_contents MATCHES "add_test[(]UsesLocalLauncher [^$<>\n]+local_launcher[^$<>\n]+use_launcher_local[^$<>\n]+\n")
+  message(SEND_ERROR "Did not use local test launcher when it should be used. ${error_details}")
+endif()
diff --git a/Tests/RunCMake/add_test/TestLauncher-test-stdout.txt b/Tests/RunCMake/add_test/TestLauncher-test-stdout.txt
new file mode 100644
index 0000000..a028f42
--- /dev/null
+++ b/Tests/RunCMake/add_test/TestLauncher-test-stdout.txt
@@ -0,0 +1,58 @@
+test 1
+    Start 1: DoesNotUseLauncher
++
+1: Test command: "?[^
+]*[/\]cmake(\.exe)?"? "-E" "echo" "Hi"
+1: Working Directory: [^
+]*/Tests/RunCMake/add_test/TestLauncher-build
+1: Test timeout computed to be: [0-9]+
+1: Hi
+1/6 Test #1: DoesNotUseLauncher [.]* +Passed +[0-9.]+ sec
+test 2
+    Start 2: UsesTestLauncher
++
+2: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/add_test/TestLauncher-build(/Debug)?/exe(\.exe)?"
+2: Working Directory: [^
+]*Tests/RunCMake/add_test/TestLauncher-build
+2: Test timeout computed to be: [0-9]+
+2: Command: "[^"]*/Tests/RunCMake/add_test/TestLauncher-build(/Debug)?/exe(\.exe)?"
+2/6 Test #2: UsesTestLauncher [.]* +Passed +[0-9.]+ sec
+test 3
+    Start 3: DoesNotUseTestLauncherWithGenex
++
+3: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]add_test[/\]TestLauncher-build([/\]Debug)?[/\]exe(\.exe)?"?
+3: Working Directory: [^
+]*Tests/RunCMake/add_test/TestLauncher-build
+3: Test timeout computed to be: [0-9]+
+3/6 Test #3: DoesNotUseTestLauncherWithGenex [.]* +Passed +[0-9.]+ sec
+test 4
+    Start 4: UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex
++
+4: Test command: "?[^
+]*[/\]Tests[/\]RunCMake([/\][^/\]+)?[/\]pseudo_emulator(\.exe)?"? "[^"]*/Tests/RunCMake/add_test/TestLauncher-build/TestLauncher(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4: Working Directory: [^
+]*Tests/RunCMake/add_test/TestLauncher-build
+4: Test timeout computed to be: [0-9]+
+4: Command: "[^"]*/Tests/RunCMake/add_test/TestLauncher-build/TestLauncher(/Debug)?/subdir_exe_no_genex(\.exe)?"
+4/6 Test #4: UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex [.]* +Passed +[0-9.]+ sec
+test 5
+    Start 5: DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex
++
+5: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]add_test[/\]TestLauncher-build[/\]TestLauncher([/\]Debug)?[/\]subdir_exe_with_genex(\.exe)?"?
+5: Working Directory: [^
+]*Tests/RunCMake/add_test/TestLauncher-build
+5: Test timeout computed to be: [0-9]+
+5/6 Test #5: DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex [.]* +Passed +[0-9.]+ sec
+test 6
+    Start 6: UsesLocalLauncher
+
+6: Test command: "?[^
+]*[/\]Tests[/\]RunCMake[/\]add_test[/\]TestLauncher-build([/\]Debug)?[/\]local_launcher(\.exe)?"? "[^"]*/Tests/RunCMake/add_test/TestLauncher-build(/Debug)?/use_launcher_local(\.exe)?"
+6: Working Directory: [^
+]*/Tests/RunCMake/add_test/TestLauncher-build
+6: Test timeout computed to be: [0-9]+
+6: Command: "[^"]*/Tests/RunCMake/add_test/TestLauncher-build(/Debug)?/use_launcher_local(\.exe)?"
+6/6 Test #6: UsesLocalLauncher [.]* +Passed +[0-9.]+ sec
diff --git a/Tests/RunCMake/add_test/TestLauncher.cmake b/Tests/RunCMake/add_test/TestLauncher.cmake
new file mode 100644
index 0000000..bef441d
--- /dev/null
+++ b/Tests/RunCMake/add_test/TestLauncher.cmake
@@ -0,0 +1,28 @@
+enable_language(C)
+enable_testing()
+
+add_test(NAME DoesNotUseLauncher
+  COMMAND ${CMAKE_COMMAND} -E echo "Hi")
+
+add_executable(exe main.c)
+get_property(test_launcher TARGET exe PROPERTY TEST_LAUNCHER)
+set_property(TARGET exe PROPERTY TEST_LAUNCHER "$<1:${test_launcher}>")
+
+add_test(NAME UsesTestLauncher
+  COMMAND exe)
+
+add_test(NAME DoesNotUseTestLauncherWithGenex
+  COMMAND $<TARGET_FILE:exe>)
+
+add_subdirectory(TestLauncher)
+
+add_test(NAME UsesTestLauncherWithExecTargetFromSubdirAddedWithoutGenex
+  COMMAND subdir_exe_no_genex)
+
+add_test(NAME DoesNotUseTestLauncherWithExecTargetFromSubdirAddedWithGenex
+  COMMAND $<TARGET_FILE:subdir_exe_with_genex>)
+
+add_executable(local_launcher ../pseudo_emulator.c)
+add_executable(use_launcher_local main.c)
+set_property(TARGET use_launcher_local PROPERTY TEST_LAUNCHER "$<TARGET_FILE:local_launcher>")
+add_test(NAME UsesLocalLauncher COMMAND use_launcher_local)
diff --git a/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt b/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt
new file mode 100644
index 0000000..fa20ae9
--- /dev/null
+++ b/Tests/RunCMake/add_test/TestLauncher/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(subdir_exe_no_genex ../main.c)
+add_executable(subdir_exe_with_genex ../main.c)
diff --git a/Tests/RunCMake/add_test/TestLauncherProperty.cmake b/Tests/RunCMake/add_test/TestLauncherProperty.cmake
new file mode 100644
index 0000000..3bc49e6
--- /dev/null
+++ b/Tests/RunCMake/add_test/TestLauncherProperty.cmake
@@ -0,0 +1,38 @@
+
+# This tests setting the TEST_LAUNCHER target property from the
+# CMAKE_TEST_LAUNCHER variable.
+
+enable_language(C)
+
+# -DCMAKE_TEST_LAUNCHER=/path/to/pseudo_emulator is passed to this
+# test
+
+add_executable(target_with_test_launcher main.c)
+get_property(launcher TARGET target_with_test_launcher
+             PROPERTY TEST_LAUNCHER)
+if(NOT "${launcher}" MATCHES "pseudo_emulator")
+  message(SEND_ERROR "Default TEST_LAUNCHER property not set")
+endif()
+
+set_property(TARGET target_with_test_launcher
+             PROPERTY TEST_LAUNCHER "another_test_launcher")
+get_property(launcher TARGET target_with_test_launcher
+             PROPERTY TEST_LAUNCHER)
+if(NOT "${launcher}" MATCHES "another_test_launcher")
+  message(SEND_ERROR
+    "set_property/get_property TEST_LAUNCHER is not consistent")
+endif()
+
+unset(CMAKE_TEST_LAUNCHER CACHE)
+add_executable(target_without_test_launcher main.c)
+get_property(launcher TARGET target_without_test_launcher
+             PROPERTY TEST_LAUNCHER)
+if(NOT "${launcher}" STREQUAL "")
+  message(SEND_ERROR "Default TEST_LAUNCHER property not set to null")
+endif()
+
+add_executable(target_with_empty_test_launcher main.c)
+set_property(TARGET target_with_empty_test_launcher PROPERTY TEST_LAUNCHER "")
+
+enable_testing()
+add_test(NAME test_target_with_empty_test_launcher COMMAND target_with_empty_test_launcher)
diff --git a/Tests/RunCMake/add_test/main.c b/Tests/RunCMake/add_test/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/RunCMake/add_test/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake
index 38ce10b..5fb2de6 100644
--- a/Tests/RunCMake/cmake_language/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_language/RunCMakeTest.cmake
@@ -84,6 +84,20 @@
 run_cmake(defer_missing_arg)
 run_cmake(defer_missing_call)
 run_cmake(defer_unknown_option)
+run_cmake(exit_0)
+run_cmake(exit_5)
+run_cmake_script(exit_0_script)
+run_cmake_script(exit_5_script)
+run_cmake_script(exit_0_script_with_command)
+run_cmake_script(exit_7_script_in_include)
+run_cmake_script(exit_8_script_in_recursive_cmake_language)
+run_cmake_script(exit_9_script_block)
+run_cmake_script(exit_9_script_control)
+run_cmake_script(exit_9_script_if)
+run_cmake_script(exit_9_script_foreach)
+run_cmake_script(exit_9_script_function)
+run_cmake_script(exit_9_script_macro)
+run_cmake_script(exit_9_script_while)
 
 # Default log level
 run_cmake_command(
diff --git a/Tests/RunCMake/cmake_language/exit_0-result.txt b/Tests/RunCMake/cmake_language/exit_0-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_0-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_language/exit_0-stderr.txt b/Tests/RunCMake/cmake_language/exit_0-stderr.txt
new file mode 100644
index 0000000..04a586e
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_0-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at exit_0.cmake:1 \(cmake_language\):
+  cmake_language EXIT can be used only in SCRIPT mode
diff --git a/Tests/RunCMake/cmake_language/exit_0.cmake b/Tests/RunCMake/cmake_language/exit_0.cmake
new file mode 100644
index 0000000..53a150e
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_0.cmake
@@ -0,0 +1 @@
+cmake_language(EXIT 0)
diff --git a/Tests/RunCMake/cmake_language/exit_0_script.cmake b/Tests/RunCMake/cmake_language/exit_0_script.cmake
new file mode 100644
index 0000000..53a150e
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_0_script.cmake
@@ -0,0 +1 @@
+cmake_language(EXIT 0)
diff --git a/Tests/RunCMake/cmake_language/exit_0_script_with_command.cmake b/Tests/RunCMake/cmake_language/exit_0_script_with_command.cmake
new file mode 100644
index 0000000..ebc4ca7
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_0_script_with_command.cmake
@@ -0,0 +1,3 @@
+cmake_language(EXIT 0)
+
+message(FATAL_ERROR "cmake_language(EXIT 0) doesn't work")
diff --git a/Tests/RunCMake/cmake_language/exit_5-result.txt b/Tests/RunCMake/cmake_language/exit_5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_language/exit_5-stderr.txt b/Tests/RunCMake/cmake_language/exit_5-stderr.txt
new file mode 100644
index 0000000..ad232f8
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at exit_5.cmake:1 \(cmake_language\):
+  cmake_language EXIT can be used only in SCRIPT mode
diff --git a/Tests/RunCMake/cmake_language/exit_5.cmake b/Tests/RunCMake/cmake_language/exit_5.cmake
new file mode 100644
index 0000000..5e5c147
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5.cmake
@@ -0,0 +1 @@
+cmake_language(EXIT 5)
diff --git a/Tests/RunCMake/cmake_language/exit_5_script-result.txt b/Tests/RunCMake/cmake_language/exit_5_script-result.txt
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5_script-result.txt
@@ -0,0 +1 @@
+5
diff --git a/Tests/RunCMake/cmake_language/exit_5_script.cmake b/Tests/RunCMake/cmake_language/exit_5_script.cmake
new file mode 100644
index 0000000..5e5c147
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5_script.cmake
@@ -0,0 +1 @@
+cmake_language(EXIT 5)
diff --git a/Tests/RunCMake/cmake_language/exit_5_script_with_command-result.txt b/Tests/RunCMake/cmake_language/exit_5_script_with_command-result.txt
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5_script_with_command-result.txt
@@ -0,0 +1 @@
+5
diff --git a/Tests/RunCMake/cmake_language/exit_5_script_with_command.cmake b/Tests/RunCMake/cmake_language/exit_5_script_with_command.cmake
new file mode 100644
index 0000000..4400307
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_5_script_with_command.cmake
@@ -0,0 +1,3 @@
+cmake_language(EXIT 5)
+
+message(FATAL_ERROR "cmake_language(EXIT 5) doesn't work")
diff --git a/Tests/RunCMake/cmake_language/exit_7_script_in_include-result.txt b/Tests/RunCMake/cmake_language/exit_7_script_in_include-result.txt
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_7_script_in_include-result.txt
@@ -0,0 +1 @@
+7
diff --git a/Tests/RunCMake/cmake_language/exit_7_script_in_include.cmake b/Tests/RunCMake/cmake_language/exit_7_script_in_include.cmake
new file mode 100644
index 0000000..e65fa5c
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_7_script_in_include.cmake
@@ -0,0 +1,3 @@
+include(${CMAKE_CURRENT_LIST_DIR}/exit_7_script_included_with_exit.cmake)
+
+message(FATAL_ERROR "The cmake_language(EXIT 7) from include()-d script doesn't work")
diff --git a/Tests/RunCMake/cmake_language/exit_7_script_included_with_exit.cmake b/Tests/RunCMake/cmake_language/exit_7_script_included_with_exit.cmake
new file mode 100644
index 0000000..ee36ca0
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_7_script_included_with_exit.cmake
@@ -0,0 +1,3 @@
+cmake_language(EXIT 7)
+
+message(FATAL_ERROR "The include()-d script with EXIT 7 doesn't work")
diff --git a/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language-result.txt b/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language-result.txt
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language-result.txt
@@ -0,0 +1 @@
+8
diff --git a/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language.cmake b/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language.cmake
new file mode 100644
index 0000000..96daf86
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_8_script_in_recursive_cmake_language.cmake
@@ -0,0 +1,3 @@
+cmake_language(EVAL CODE "cmake_language(EXIT 8)")
+
+message(FATAL_ERROR "The cmake_language EVAL of EXIT 8 test doesn't work")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_block-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_block-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_block-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_block.cmake b/Tests/RunCMake/cmake_language/exit_9_script_block.cmake
new file mode 100644
index 0000000..b8d8615
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_block.cmake
@@ -0,0 +1,5 @@
+block()
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endblock()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_control-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_control-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_control-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_control.cmake b/Tests/RunCMake/cmake_language/exit_9_script_control.cmake
new file mode 100644
index 0000000..168f81f
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_control.cmake
@@ -0,0 +1,24 @@
+function(exit_macro)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endfunction()
+
+function(exit_function)
+  exit_macro()
+  message(FATAL_ERROR "This should not be reached!")
+endfunction()
+
+block()
+  if(1)
+    foreach(i IN ITEMS a b)
+      while(1)
+        exit_function()
+        message(FATAL_ERROR "This should not be reached!")
+      endwhile()
+      message(FATAL_ERROR "This should not be reached!")
+    endforeach()
+    message(FATAL_ERROR "This should not be reached!")
+  endif()
+  message(FATAL_ERROR "This should not be reached!")
+endblock()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_foreach-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_foreach-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_foreach-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_foreach.cmake b/Tests/RunCMake/cmake_language/exit_9_script_foreach.cmake
new file mode 100644
index 0000000..b205537
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_foreach.cmake
@@ -0,0 +1,5 @@
+foreach(i IN ITEMS a b)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endforeach()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_function-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_function-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_function-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_function.cmake b/Tests/RunCMake/cmake_language/exit_9_script_function.cmake
new file mode 100644
index 0000000..67a2615
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_function.cmake
@@ -0,0 +1,6 @@
+function(exit)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endfunction()
+exit()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_if-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_if-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_if-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_if.cmake b/Tests/RunCMake/cmake_language/exit_9_script_if.cmake
new file mode 100644
index 0000000..935b8d5
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_if.cmake
@@ -0,0 +1,5 @@
+if(1)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endif()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_macro-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_macro-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_macro-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_macro.cmake b/Tests/RunCMake/cmake_language/exit_9_script_macro.cmake
new file mode 100644
index 0000000..133348c
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_macro.cmake
@@ -0,0 +1,6 @@
+macro(exit)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endmacro()
+exit()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_while-result.txt b/Tests/RunCMake/cmake_language/exit_9_script_while-result.txt
new file mode 100644
index 0000000..b38a53d
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_while-result.txt
@@ -0,0 +1 @@
+^9$
diff --git a/Tests/RunCMake/cmake_language/exit_9_script_while.cmake b/Tests/RunCMake/cmake_language/exit_9_script_while.cmake
new file mode 100644
index 0000000..261438c
--- /dev/null
+++ b/Tests/RunCMake/cmake_language/exit_9_script_while.cmake
@@ -0,0 +1,5 @@
+while(1)
+  cmake_language(EXIT 9)
+  message(FATAL_ERROR "This should not be reached!")
+endwhile()
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in
new file mode 100644
index 0000000..408b2f3
--- /dev/null
+++ b/Tests/RunCMake/ctest_empty_binary_directory/CMakeLists.txt.in
@@ -0,0 +1,2 @@
+cmake_minimum_required(VERSION 3.5)
+project(CTestTest@CASE_NAME@ NONE)
diff --git a/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt
new file mode 100644
index 0000000..338ac6d
--- /dev/null
+++ b/Tests/RunCMake/ctest_empty_binary_directory/NoCache-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Error at [^
+]*/Tests/RunCMake/ctest_empty_binary_directory/NoCache/test.cmake:[0-9]+ \(ctest_empty_binary_directory\):
+  Did not remove the binary directory:
+
+   [^
+]*/Tests/RunCMake/ctest_empty_binary_directory/NoCache-build
+
+  because:
+
+   path does not contain an existing CMakeCache\.txt file
++
+script continues after ctest_empty_binary_directory error$
diff --git a/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake b/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake
new file mode 100644
index 0000000..f1d4ca7
--- /dev/null
+++ b/Tests/RunCMake/ctest_empty_binary_directory/RunCMakeTest.cmake
@@ -0,0 +1,3 @@
+include(RunCTest)
+
+run_ctest(NoCache)
diff --git a/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in
new file mode 100644
index 0000000..2e0cfe6
--- /dev/null
+++ b/Tests/RunCMake/ctest_empty_binary_directory/test.cmake.in
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.5)
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY})
+message("script continues after ctest_empty_binary_directory error")
diff --git a/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt b/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt
index 1a45994..1973b2a 100644
--- a/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt
+++ b/Tests/RunCMake/ctest_fixtures/cyclicCleanup-stderr.txt
@@ -1,3 +1,2 @@
-Error: a cycle exists in the test dependency graph for the test "cyclicCleanup".
-Please fix the cycle and run ctest again.
-No tests were found!!!
+^Error: a cycle exists in the test dependency graph for the test "cyclicCleanup"\.
+Please fix the cycle and run ctest again\.$
diff --git a/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt b/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt
index 2aba6c9..aee735a 100644
--- a/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt
+++ b/Tests/RunCMake/ctest_fixtures/cyclicSetup-stderr.txt
@@ -1,3 +1,2 @@
-Error: a cycle exists in the test dependency graph for the test "cyclicSetup".
-Please fix the cycle and run ctest again.
-No tests were found!!!$
+^Error: a cycle exists in the test dependency graph for the test "cyclicSetup"\.
+Please fix the cycle and run ctest again\.$
diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c
index babe82d..009162d 100644
--- a/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c
+++ b/Tests/RunCMake/ctest_labels_for_subprojects/MyThirdPartyDependency/src/thirdparty.c
@@ -7,7 +7,7 @@
   return 0;
 }
 
-int notcalled()
+int notcalled(void)
 {
   printf(This function doesn't get called.\n");
   return 0;
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
index 24083f2..259f7d0 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?-no-site-'?.*|The requested URL returned error:.*|Protocol "https" not supported or disabled in .*|.* was built with SSL disabled.*)
+Error message was: ([Cc]ould *n.t resolve host:? '?-no-site-'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
    Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait-stdout.txt
deleted file mode 100644
index 2f4468f..0000000
--- a/Tests/RunCMake/ctest_test/CTestTestLoadWait-stdout.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Test project [^
-]*/Tests/RunCMake/ctest_test/CTestTestLoadWait-build(
-[^*][^
-]*)*
-\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 4, Smallest test RunCMakeVersion requires 1\*\*\*\*\*
-test 1
-    Start 1: RunCMakeVersion
-+(
-[^*][^
-]*)*
-1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
-+
-100% tests passed, 0 tests failed out of 1
-+
-Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt
new file mode 100644
index 0000000..60d70c9
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait0-stdout.txt
@@ -0,0 +1,15 @@
+Test project [^
+]*/Tests/RunCMake/ctest_test/CTestTestLoadWait0-build(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 6\*\*\*\*\*
+test 1
+    Start 1: RunCMakeVersion
++(
+[^*][^
+]*)*
+1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt
new file mode 100644
index 0000000..70d8d3e
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/CTestTestLoadWait1-stdout.txt
@@ -0,0 +1,15 @@
+Test project [^
+]*/Tests/RunCMake/ctest_test/CTestTestLoadWait1-build(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\*
+test 1
+    Start 1: RunCMakeVersion
++(
+[^*][^
+]*)*
+1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
index d2f3da3..4413a4d 100644
--- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -14,16 +14,20 @@
 # Tests for the 'Test Load' feature of ctest
 #
 # Spoof a load average value to make these tests more reliable.
-set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 5)
+set(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING} 7)
 set(RunCTest_VERBOSE_FLAG -VV)
+set(CASE_CMAKELISTS_SUFFIX_CODE [[
+set_property(TEST RunCMakeVersion PROPERTY PROCESSORS 2)
+]])
 
 # Verify that new tests are started when the load average falls below
 # our threshold.
-run_ctest_test(TestLoadPass TEST_LOAD 6)
+run_ctest_test(TestLoadPass TEST_LOAD 8)
 
 # Verify that new tests are not started when the load average exceeds
 # our threshold and that they then run once the load average drops.
-run_ctest_test(TestLoadWait TEST_LOAD 2)
+run_ctest_test(TestLoadWait0 TEST_LOAD 4 PARALLEL_LEVEL 8)
+run_ctest_test(TestLoadWait1 TEST_LOAD 8 PARALLEL_LEVEL 8)
 
 # Verify that when an invalid "TEST_LOAD" value is given, a warning
 # message is displayed and the value is ignored.
@@ -31,13 +35,15 @@
 
 # Verify that new tests are started when the load average falls below
 # our threshold.
-set(CASE_CTEST_TEST_LOAD 7)
+set(CASE_CTEST_TEST_LOAD 9)
 run_ctest_test(CTestTestLoadPass)
 
 # Verify that new tests are not started when the load average exceeds
 # our threshold and that they then run once the load average drops.
-set(CASE_CTEST_TEST_LOAD 4)
-run_ctest_test(CTestTestLoadWait)
+set(CASE_CTEST_TEST_LOAD 6)
+run_ctest_test(CTestTestLoadWait0 PARALLEL_LEVEL 8)
+set(CASE_CTEST_TEST_LOAD 8)
+run_ctest_test(CTestTestLoadWait1 PARALLEL_LEVEL 8)
 
 # Verify that when an invalid "CTEST_TEST_LOAD" value is given,
 # a warning message is displayed and the value is ignored.
@@ -51,6 +57,7 @@
 
 unset(ENV{__CTEST_FAKE_LOAD_AVERAGE_FOR_TESTING})
 unset(CASE_CTEST_TEST_LOAD)
+unset(CASE_CMAKELISTS_SUFFIX_CODE)
 unset(RunCTest_VERBOSE_FLAG)
 
 block()
@@ -156,6 +163,22 @@
 endfunction()
 run_stop_on_failure()
 
+
+# test include/exclude tests from file
+function(run_tests_from_file mode)
+  set(CASE_CTEST_TEST_ARGS ${mode} ${RunCMake_SOURCE_DIR}/TestsFromFile-TestList.txt)
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_test(NAME Test1 COMMAND ${CMAKE_COMMAND} -E true)
+add_test(NAME Test2 COMMAND ${CMAKE_COMMAND} -E true)
+add_test(NAME Test11 COMMAND ${CMAKE_COMMAND} -E true)
+  ]])
+
+  run_ctest(TestsFromFile-${mode})
+endfunction()
+run_tests_from_file(INCLUDE_FROM_FILE)
+run_tests_from_file(EXCLUDE_FROM_FILE)
+
+
 # Make sure environment gets logged
 function(run_environment)
   set(ENV{BAD_ENVIRONMENT_VARIABLE} "Bad environment variable")
diff --git a/Tests/RunCMake/ctest_test/TestLoadWait-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait-stdout.txt
deleted file mode 100644
index fc32958..0000000
--- a/Tests/RunCMake/ctest_test/TestLoadWait-stdout.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Test project [^
-]*/Tests/RunCMake/ctest_test/TestLoadWait-build(
-[^*][^
-]*)*
-\*\*\*\*\* WAITING, System Load: 5, Max Allowed Load: 2, Smallest test RunCMakeVersion requires 1\*\*\*\*\*
-test 1
-    Start 1: RunCMakeVersion
-+(
-[^*][^
-]*)*
-1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
-+
-100% tests passed, 0 tests failed out of 1
-+
-Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt
new file mode 100644
index 0000000..c7172aa
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestLoadWait0-stdout.txt
@@ -0,0 +1,15 @@
+Test project [^
+]*/Tests/RunCMake/ctest_test/TestLoadWait0-build(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 4\*\*\*\*\*
+test 1
+    Start 1: RunCMakeVersion
++(
+[^*][^
+]*)*
+1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt
new file mode 100644
index 0000000..bca3e54
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestLoadWait1-stdout.txt
@@ -0,0 +1,15 @@
+Test project [^
+]*/Tests/RunCMake/ctest_test/TestLoadWait1-build(
+[^*][^
+]*)*
+\*\*\*\*\* WAITING, System Load: 7, Max Allowed Load: 8, Smallest test RunCMakeVersion requires 2\*\*\*\*\*
+test 1
+    Start 1: RunCMakeVersion
++(
+[^*][^
+]*)*
+1/1 Test #1: RunCMakeVersion ..................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
+Total Test time \(real\) = +[0-9.]+ sec$
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-EXCLUDE_FROM_FILE-stdout.txt b/Tests/RunCMake/ctest_test/TestsFromFile-EXCLUDE_FROM_FILE-stdout.txt
new file mode 100644
index 0000000..29c2b7f
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestsFromFile-EXCLUDE_FROM_FILE-stdout.txt
@@ -0,0 +1,7 @@
+ +Start 3: Test2
+2/3 Test #3: Test2 ............................   Passed +[0-9.]+ sec
+ +Start 4: Test11
+3/3 Test #4: Test11 ...........................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 3
++
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-INCLUDE_FROM_FILE-stdout.txt b/Tests/RunCMake/ctest_test/TestsFromFile-INCLUDE_FROM_FILE-stdout.txt
new file mode 100644
index 0000000..e3ca719
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestsFromFile-INCLUDE_FROM_FILE-stdout.txt
@@ -0,0 +1,5 @@
+ +Start 2: Test1
+1/1 Test #2: Test1 ............................   Passed +[0-9.]+ sec
++
+100% tests passed, 0 tests failed out of 1
++
diff --git a/Tests/RunCMake/ctest_test/TestsFromFile-TestList.txt b/Tests/RunCMake/ctest_test/TestsFromFile-TestList.txt
new file mode 100644
index 0000000..91f6504
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestsFromFile-TestList.txt
@@ -0,0 +1,2 @@
+Test1
+# Test11
diff --git a/Tests/RunCMake/define_property/RunCMakeTest.cmake b/Tests/RunCMake/define_property/RunCMakeTest.cmake
index 93eaf1b..0a457df 100644
--- a/Tests/RunCMake/define_property/RunCMakeTest.cmake
+++ b/Tests/RunCMake/define_property/RunCMakeTest.cmake
@@ -1,6 +1,7 @@
 include(RunCMake)
 
 run_cmake(define_property)
+run_cmake(define_property-redefine)
 run_cmake(define_property-INITIALIZE_FROM_VARIABLE)
 run_cmake(define_property-INITIALIZE_FROM_VARIABLE-invalid_1)
 run_cmake(define_property-INITIALIZE_FROM_VARIABLE-invalid_2)
diff --git a/Tests/RunCMake/define_property/define_property-redefine.cmake b/Tests/RunCMake/define_property/define_property-redefine.cmake
new file mode 100644
index 0000000..b0b5e95
--- /dev/null
+++ b/Tests/RunCMake/define_property/define_property-redefine.cmake
@@ -0,0 +1,88 @@
+function(verify_value type prop attrib expected actual)
+  if(expected STREQUAL "FALSE")
+    if(actual)
+      message(FATAL_ERROR
+        "Expected ${type} property ${prop}'s ${attrib} to be false")
+    endif()
+  elseif(expected STREQUAL "TRUE")
+    if(NOT actual)
+      message(FATAL_ERROR
+        "Expected ${type} property ${prop}'s ${attrib} to be true")
+    endif()
+  elseif(NOT actual STREQUAL expected)
+    message(FATAL_ERROR
+      "Expected value of ${type} property ${prop}'s ${attrib}:\n"
+      "  ${expected}\n"
+      "Actual value:\n"
+      "  ${actual}"
+    )
+  endif()
+endfunction()
+
+function(assert_tgt_prop_attrib_eq prop attrib expected)
+  get_property(actual TARGET NONE PROPERTY "${prop}" "${attrib}")
+  verify_value(TARGET "${prop}" "${attrib}" "${expected}" "${actual}")
+endfunction()
+
+function(assert_dir_prop_attrib_eq prop attrib expected)
+  get_property(actual DIRECTORY "" PROPERTY "${prop}" "${attrib}")
+  verify_value(DIRECTORY "${prop}" "${attrib}" "${expected}" "${actual}")
+endfunction()
+
+#
+# TESTS
+#
+
+# Define a new target property
+message(CHECK_START "Testing define_property(TARGET ...)")
+define_property(TARGET PROPERTY TGT1
+  BRIEF_DOCS "Brief")
+assert_tgt_prop_attrib_eq(TGT1 BRIEF_DOCS "Brief")
+assert_tgt_prop_attrib_eq(TGT1 FULL_DOCS "NOTFOUND")
+message(CHECK_PASS "Complete")
+
+# Attempt to redefine with different/additional attributes
+message(CHECK_START "Testing TARGET property redefinition")
+define_property(TARGET PROPERTY TGT1
+  BRIEF_DOCS "Changed"
+  FULL_DOCS "Full")
+assert_tgt_prop_attrib_eq(TGT1 BRIEF_DOCS "Brief")
+assert_tgt_prop_attrib_eq(TGT1 FULL_DOCS "NOTFOUND")
+message(CHECK_PASS "Complete")
+
+# Query undefined property
+message(CHECK_START "Testing undefined TARGET property query")
+assert_tgt_prop_attrib_eq(TGT2 DEFINED FALSE)
+assert_tgt_prop_attrib_eq(TGT2 BRIEF_DOCS "NOTFOUND")
+message(CHECK_PASS "Complete")
+
+# Define after query
+message(CHECK_START "Testing TARGET property definition after query")
+define_property(TARGET PROPERTY TGT2
+  BRIEF_DOCS "Brief"
+  FULL_DOCS "Full"
+)
+assert_tgt_prop_attrib_eq(TGT2 DEFINED TRUE)
+assert_tgt_prop_attrib_eq(TGT2 BRIEF_DOCS "Brief")
+assert_tgt_prop_attrib_eq(TGT2 FULL_DOCS "Full")
+message(CHECK_PASS "Complete")
+
+# Define a new directory property
+message(CHECK_START "Testing define_property(DIRECTORY ...)")
+define_property(DIRECTORY PROPERTY DIR1
+  BRIEF_DOCS "Brief"
+  FULL_DOCS "Full"
+)
+assert_dir_prop_attrib_eq(DIR1 DEFINED TRUE)
+assert_dir_prop_attrib_eq(DIR1 BRIEF_DOCS "Brief")
+assert_dir_prop_attrib_eq(DIR1 FULL_DOCS "Full")
+message(CHECK_PASS "Complete")
+
+# Attempt to redefine existing attributes
+message(CHECK_START "Testing DIRECTORY property redefinition")
+define_property(DIRECTORY PROPERTY DIR1
+  BRIEF_DOCS "Overwritten"
+)
+assert_dir_prop_attrib_eq(DIR1 BRIEF_DOCS "Brief")
+assert_dir_prop_attrib_eq(DIR1 FULL_DOCS "Full")
+message(CHECK_PASS "Complete")
diff --git a/Tests/RunCMake/detect_jobserver.c b/Tests/RunCMake/detect_jobserver.c
index 8cbfe2e..67cc7db 100644
--- a/Tests/RunCMake/detect_jobserver.c
+++ b/Tests/RunCMake/detect_jobserver.c
@@ -118,7 +118,6 @@
 {
   int read_fd;
   int write_fd;
-  const char* path;
 
   // First try to parse as "R,W" file descriptors
   if (sscanf(jobserver, "%d,%d", &read_fd, &write_fd) == 2) {
@@ -127,7 +126,7 @@
 
   // Then try to parse as "fifo:PATH"
   if (strncmp(jobserver, "fifo:", 5) == 0) {
-    path = jobserver + 5;
+    const char* path = jobserver + 5;
     read_fd = open(path, O_RDONLY);
     write_fd = open(path, O_WRONLY);
     return test_fd(read_fd, write_fd, message);
diff --git a/Tests/RunCMake/export/CMake/FindHasDeps.cmake b/Tests/RunCMake/export/CMake/FindHasDeps.cmake
new file mode 100644
index 0000000..86b2abe
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindHasDeps.cmake
@@ -0,0 +1,17 @@
+find_package(Threads REQUIRED)
+find_package(P4 REQUIRED)
+
+add_library(HasDeps::interface IMPORTED INTERFACE)
+target_link_libraries(HasDeps::interface INTERFACE Threads::Threads l4)
+
+add_library(HasDeps::A IMPORTED UNKNOWN)
+target_link_libraries(HasDeps::A INTERFACE HasDeps::interface)
+file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/a.so")
+set_property(TARGET HasDeps::A PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/a.so")
+
+add_library(HasDeps::B IMPORTED UNKNOWN)
+target_link_libraries(HasDeps::B INTERFACE HasDeps::interface)
+file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/b.so")
+set_property(TARGET HasDeps::B PROPERTY IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/b.so")
+
+set(HASDEPS_FOUND TRUE)
diff --git a/Tests/RunCMake/export/CMake/FindP1.cmake b/Tests/RunCMake/export/CMake/FindP1.cmake
new file mode 100644
index 0000000..e33c3d6
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindP1.cmake
@@ -0,0 +1,2 @@
+add_library(l1 IMPORTED INTERFACE)
+set(P1_FOUND TRUE)
diff --git a/Tests/RunCMake/export/CMake/FindP2.cmake b/Tests/RunCMake/export/CMake/FindP2.cmake
new file mode 100644
index 0000000..6a360ce
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindP2.cmake
@@ -0,0 +1,2 @@
+add_library(l2 IMPORTED INTERFACE)
+set(P2_FOUND TRUE)
diff --git a/Tests/RunCMake/export/CMake/FindP3.cmake b/Tests/RunCMake/export/CMake/FindP3.cmake
new file mode 100644
index 0000000..72bd829
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindP3.cmake
@@ -0,0 +1,2 @@
+add_library(l3 IMPORTED INTERFACE)
+set(P3_FOUND TRUE)
diff --git a/Tests/RunCMake/export/CMake/FindP4.cmake b/Tests/RunCMake/export/CMake/FindP4.cmake
new file mode 100644
index 0000000..b62a040
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindP4.cmake
@@ -0,0 +1,2 @@
+add_library(l4 IMPORTED INTERFACE)
+set(P4_FOUND TRUE)
diff --git a/Tests/RunCMake/export/CMake/FindP9.cmake b/Tests/RunCMake/export/CMake/FindP9.cmake
new file mode 100644
index 0000000..201b86a
--- /dev/null
+++ b/Tests/RunCMake/export/CMake/FindP9.cmake
@@ -0,0 +1,2 @@
+add_library(l9 IMPORTED INTERFACE)
+set(P9_FOUND TRUE)
diff --git a/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt b/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt
index e8f8a09..a884939 100644
--- a/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt
+++ b/Tests/RunCMake/export/DependOnDoubleExport-stderr.txt
@@ -1,7 +1,7 @@
 CMake Error in CMakeLists.txt:
   export called with target "exported" which requires target "doubleexported"
-  that is not in this export set, but in multiple other export sets:
-  .*/Tests/RunCMake/export/DependOnDoubleExport-build/exportset.cmake,
+  that is not in this export set, but in multiple other export sets:.*
+  .*/Tests/RunCMake/export/DependOnDoubleExport-build/exportset.cmake,.*
   .*/Tests/RunCMake/export/DependOnDoubleExport-build/manual.cmake.
 +
   An exported target cannot depend upon another target which is exported
diff --git a/Tests/RunCMake/export/FindDependencyExport-check.cmake b/Tests/RunCMake/export/FindDependencyExport-check.cmake
new file mode 100644
index 0000000..0cbb195
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExport-check.cmake
@@ -0,0 +1,35 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets)
+if("${mytargets}" MATCHES "find_dependency\\(P1")
+  string(APPEND RunCMake_TEST_FAILED "P1 dependency should not be exported but it is\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P2 REQUIRED \"VERSION\" \"1\\.0\"\\)")
+  string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P3 REQUIRED\\)")
+  string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P4 REQUIRED\\)")
+  string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n")
+endif()
+if("${mytargets}" MATCHES "find_dependency\\(P5")
+  string(APPEND RunCMake_TEST_FAILED "P5 dependency should not be exported but it is\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P6 REQUIRED\\)")
+  string(APPEND RunCMake_TEST_FAILED "P6 dependency should be exported but it is not\n")
+endif()
+if("${mytargets}" MATCHES "find_dependency\\(P7")
+  string(APPEND RunCMake_TEST_FAILED "P7 dependency should not be exported but it is\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P3[^
+]*\\)
+find_dependency\\(P2[^
+]*\\)
+find_dependency\\(P8[^
+]*\\)
+find_dependency\\(P6[^
+]*\\)
+find_dependency\\(P9[^
+]*\\)
+find_dependency\\(P4")
+  string(APPEND RunCMake_TEST_FAILED "Dependencies are not in the correct order\n")
+endif()
diff --git a/Tests/RunCMake/export/FindDependencyExport.cmake b/Tests/RunCMake/export/FindDependencyExport.cmake
new file mode 100644
index 0000000..59dd4cc
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExport.cmake
@@ -0,0 +1,30 @@
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
+
+find_package(P1)
+find_package(P2)
+find_package(P9)
+find_package(P4)
+find_package(P3)
+
+add_library(mylib INTERFACE)
+target_link_libraries(mylib INTERFACE l1 l2 l3 l4 l9)
+
+install(TARGETS mylib EXPORT mytargets)
+export(SETUP mytargets
+  PACKAGE_DEPENDENCY P1
+    ENABLED OFF
+  PACKAGE_DEPENDENCY P3
+    ENABLED AUTO
+  PACKAGE_DEPENDENCY P2
+    ENABLED ON
+    EXTRA_ARGS VERSION 1.0
+  PACKAGE_DEPENDENCY P5
+    ENABLED FALSE
+  PACKAGE_DEPENDENCY P8
+    ENABLED TRUE
+  PACKAGE_DEPENDENCY P6
+    ENABLED 1
+  PACKAGE_DEPENDENCY P7
+    ENABLED AUTO
+  )
+export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake)
diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake
new file mode 100644
index 0000000..353bb08
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportFetchContent-check.cmake
@@ -0,0 +1,14 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/my_private_targets.cmake" my_private_targets)
+if(NOT "${my_private_targets}" MATCHES "find_dependency\\(HasDeps")
+  string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n")
+endif()
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/my_static_targets.cmake" my_static_targets)
+if(NOT "${my_static_targets}" MATCHES "find_dependency\\(MyPrivate")
+  string(APPEND RunCMake_TEST_FAILED "HasDeps dependency should be exported but it is not\n")
+endif()
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/my_shared_targets.cmake" my_shared_targets)
+if(NOT "${my_shared_targets}" MATCHES "find_dependency\\(MyPrivate")
+  string(APPEND RunCMake_TEST_FAILED "MyStatic dependency should be exported but it is not\n")
+endif()
diff --git a/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake
new file mode 100644
index 0000000..9e6c565
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportFetchContent.cmake
@@ -0,0 +1,37 @@
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
+
+enable_language(CXX)
+
+find_package(HasDeps)
+
+# replicates FetchContent where a dependency is brought
+# in via source. In these cases we need to extend the `install`
+# `export` commands to allow markup on what `Find<Project>` will
+# map to the export set
+add_library(my_private_lib STATIC empty.cpp)
+target_link_libraries(my_private_lib PUBLIC HasDeps::A)
+set_target_properties(my_private_lib PROPERTIES EXPORT_FIND_PACKAGE_NAME "MyPrivate")
+
+install(TARGETS my_private_lib EXPORT my_private_targets)
+install(EXPORT my_private_targets
+        FILE my_private.cmake
+        DESTINATION lib)
+export(EXPORT my_private_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_private_targets.cmake)
+
+add_library(my_static_lib STATIC empty.cpp)
+target_link_libraries(my_static_lib PRIVATE my_private_lib)
+
+install(TARGETS my_static_lib EXPORT my_static_targets)
+install(EXPORT my_static_targets
+        FILE my_static.cmake
+        DESTINATION lib)
+export(EXPORT my_static_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_static_targets.cmake)
+
+add_library(my_shared_lib SHARED empty.cpp)
+target_link_libraries(my_shared_lib PUBLIC my_private_lib)
+
+install(TARGETS my_shared_lib EXPORT my_shared_targets)
+install(EXPORT my_shared_targets
+        FILE my_shared.cmake
+        DESTINATION lib)
+export(EXPORT my_shared_targets EXPORT_PACKAGE_DEPENDENCIES FILE my_shared_targets.cmake)
diff --git a/Tests/RunCMake/export/FindDependencyExportShared-check.cmake b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake
new file mode 100644
index 0000000..d7a32d1
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportShared-check.cmake
@@ -0,0 +1,4 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets)
+if("${mytargets}" MATCHES "find_dependency")
+  string(APPEND RunCMake_TEST_FAILED "No dependencies should not be exported\n")
+endif()
diff --git a/Tests/RunCMake/export/FindDependencyExportShared.cmake b/Tests/RunCMake/export/FindDependencyExportShared.cmake
new file mode 100644
index 0000000..6e662a1
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportShared.cmake
@@ -0,0 +1,14 @@
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
+
+enable_language(CXX)
+
+find_package(P1)
+find_package(P2)
+find_package(P3)
+find_package(P4)
+
+add_library(mylib SHARED empty.cpp)
+target_link_libraries(mylib PRIVATE l1 l2 l3 l4)
+
+install(TARGETS mylib EXPORT mytargets)
+export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake)
diff --git a/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake
new file mode 100644
index 0000000..b78bccb
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportStatic-check.cmake
@@ -0,0 +1,13 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/mytargets.cmake" mytargets)
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P1")
+  string(APPEND RunCMake_TEST_FAILED "P1 dependency should be exported but it is not\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P2")
+  string(APPEND RunCMake_TEST_FAILED "P2 dependency should be exported but it is not\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P3")
+  string(APPEND RunCMake_TEST_FAILED "P3 dependency should be exported but it is not\n")
+endif()
+if(NOT "${mytargets}" MATCHES "find_dependency\\(P4")
+  string(APPEND RunCMake_TEST_FAILED "P4 dependency should be exported but it is not\n")
+endif()
diff --git a/Tests/RunCMake/export/FindDependencyExportStatic.cmake b/Tests/RunCMake/export/FindDependencyExportStatic.cmake
new file mode 100644
index 0000000..2d32eb2
--- /dev/null
+++ b/Tests/RunCMake/export/FindDependencyExportStatic.cmake
@@ -0,0 +1,14 @@
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
+
+enable_language(CXX)
+
+find_package(P1)
+find_package(P2)
+find_package(P3)
+find_package(P4)
+
+add_library(mylib STATIC empty.cpp)
+target_link_libraries(mylib PRIVATE l1 l2 l3 l4)
+
+install(TARGETS mylib EXPORT mytargets)
+export(EXPORT mytargets EXPORT_PACKAGE_DEPENDENCIES FILE mytargets.cmake)
diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake
index ee00b27..3e1b7d6 100644
--- a/Tests/RunCMake/export/RunCMakeTest.cmake
+++ b/Tests/RunCMake/export/RunCMakeTest.cmake
@@ -19,3 +19,7 @@
 run_cmake(NamelinkOnlyExport)
 run_cmake(SeparateNamelinkExport)
 run_cmake(TryCompileExport)
+run_cmake(FindDependencyExport)
+run_cmake(FindDependencyExportStatic)
+run_cmake(FindDependencyExportShared)
+run_cmake(FindDependencyExportFetchContent)
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-Common.cmake b/Tests/RunCMake/file-STRINGS/CMP0159-Common.cmake
new file mode 100644
index 0000000..3608882
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-Common.cmake
@@ -0,0 +1,12 @@
+function (output_results msg)
+  message(STATUS "results from: ${msg}")
+  message(STATUS "CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--")
+  message(STATUS "CMAKE_MATCH_1: -->${CMAKE_MATCH_1}<--")
+  message(STATUS "CMAKE_MATCH_2: -->${CMAKE_MATCH_2}<--")
+  message(STATUS "CMAKE_MATCH_COUNT: -->${CMAKE_MATCH_COUNT}<--")
+endfunction ()
+
+# Populate `CMAKE_MATCH_<n>` with some initial value
+string(REGEX MATCH "(.*):" _ "Initial-value:")
+file(STRINGS CMP0159.txt _ REGEX "(.*): (.*)")
+output_results(CMP0159)
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-NEW-stdout.txt b/Tests/RunCMake/file-STRINGS/CMP0159-NEW-stdout.txt
new file mode 100644
index 0000000..f163622
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-NEW-stdout.txt
@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->real-value: 1<--
+-- CMAKE_MATCH_1: -->real-value<--
+-- CMAKE_MATCH_2: -->1<--
+-- CMAKE_MATCH_COUNT: -->2<--
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-NEW.cmake b/Tests/RunCMake/file-STRINGS/CMP0159-NEW.cmake
new file mode 100644
index 0000000..8617561
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-NEW.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0159 NEW)
+
+include(CMP0159-Common.cmake)
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-OLD-stdout.txt b/Tests/RunCMake/file-STRINGS/CMP0159-OLD-stdout.txt
new file mode 100644
index 0000000..16ee678
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-OLD-stdout.txt
@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->Initial-value:<--
+-- CMAKE_MATCH_1: -->Initial-value<--
+-- CMAKE_MATCH_2: --><--
+-- CMAKE_MATCH_COUNT: -->1<--
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-OLD.cmake b/Tests/RunCMake/file-STRINGS/CMP0159-OLD.cmake
new file mode 100644
index 0000000..4137aea
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-OLD.cmake
@@ -0,0 +1,4 @@
+
+cmake_policy(SET CMP0159 OLD)
+
+include(CMP0159-Common.cmake)
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt
new file mode 100644
index 0000000..e82486c
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stderr.txt
@@ -0,0 +1,10 @@
+CMake Warning \(dev\) at CMP0159-Common.cmake:[0-9]+ \(file\):
+  Policy CMP0159 is not set: file\(STRINGS\) with REGEX updates
+  CMAKE_MATCH_<n>\.  Run "cmake --help-policy CMP0159" for policy details\.
+  Use the cmake_policy command to set the policy and suppress this warning\.
+
+  For compatibility, CMake is leaving CMAKE_MATCH_<n> unchanged\.
+Call Stack \(most recent call first\):
+  CMP0159-WARN.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stdout.txt b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stdout.txt
new file mode 100644
index 0000000..16ee678
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-WARN-stdout.txt
@@ -0,0 +1,5 @@
+-- results from: CMP0159
+-- CMAKE_MATCH_0: -->Initial-value:<--
+-- CMAKE_MATCH_1: -->Initial-value<--
+-- CMAKE_MATCH_2: --><--
+-- CMAKE_MATCH_COUNT: -->1<--
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159-WARN.cmake b/Tests/RunCMake/file-STRINGS/CMP0159-WARN.cmake
new file mode 100644
index 0000000..1feab4b
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159-WARN.cmake
@@ -0,0 +1,4 @@
+
+set(CMAKE_POLICY_WARNING_CMP0159 TRUE)
+
+include(CMP0159-Common.cmake)
diff --git a/Tests/RunCMake/file-STRINGS/CMP0159.txt b/Tests/RunCMake/file-STRINGS/CMP0159.txt
new file mode 100644
index 0000000..4b31aed
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMP0159.txt
@@ -0,0 +1,3 @@
+overwritten-value: -1
+real-value: 1
+ignored = -2
diff --git a/Tests/RunCMake/file-STRINGS/CMakeLists.txt b/Tests/RunCMake/file-STRINGS/CMakeLists.txt
new file mode 100644
index 0000000..9a66cde
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.13)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/file-STRINGS/RunCMakeTest.cmake b/Tests/RunCMake/file-STRINGS/RunCMakeTest.cmake
new file mode 100644
index 0000000..a22b3d8
--- /dev/null
+++ b/Tests/RunCMake/file-STRINGS/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0159-WARN)
+run_cmake(CMP0159-OLD)
+run_cmake(CMP0159-NEW)
diff --git a/Tests/RunCMake/find_file/NO_CACHE-stderr.txt b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt
new file mode 100644
index 0000000..6da353b
--- /dev/null
+++ b/Tests/RunCMake/find_file/NO_CACHE-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0125 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/NO_CACHE-stderr.txt b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt
new file mode 100644
index 0000000..6da353b
--- /dev/null
+++ b/Tests/RunCMake/find_library/NO_CACHE-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0125 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake
index 0bed252..f9c8528 100644
--- a/Tests/RunCMake/find_library/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake
@@ -24,6 +24,10 @@
   run_cmake(MSYSTEM_PREFIX)
 endif()
 
+if(CMAKE_HOST_WIN32 AND MSVC)
+  run_cmake(Windows-MSVC)
+endif()
+
 run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp")
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=CREATED_LIBRARY)
diff --git a/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt b/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt
new file mode 100644
index 0000000..ef865a0
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC-stdout.txt
@@ -0,0 +1,3 @@
+-- STATIC_LIBRARY='[^']*/Tests/RunCMake/find_library/Windows-MSVC/static.lib'
+-- MESON_STATIC_LIBRARY='[^']*/Tests/RunCMake/find_library/Windows-MSVC/libmeson_static.a'
+-- RUSTC_IMPORT_LIBRARY='[^']*/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib'
diff --git a/Tests/RunCMake/find_library/Windows-MSVC.cmake b/Tests/RunCMake/find_library/Windows-MSVC.cmake
new file mode 100644
index 0000000..c25fd99
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+find_library(STATIC_LIBRARY NAMES static NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Windows-MSVC)
+message(STATUS "STATIC_LIBRARY='${STATIC_LIBRARY}'")
+
+find_library(MESON_STATIC_LIBRARY NAMES meson_static NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Windows-MSVC)
+message(STATUS "MESON_STATIC_LIBRARY='${MESON_STATIC_LIBRARY}'")
+
+find_library(RUSTC_IMPORT_LIBRARY NAMES rustc_import NO_DEFAULT_PATH PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Windows-MSVC)
+message(STATUS "RUSTC_IMPORT_LIBRARY='${RUSTC_IMPORT_LIBRARY}'")
diff --git a/Tests/RunCMake/find_library/Windows-MSVC/libmeson_static.a b/Tests/RunCMake/find_library/Windows-MSVC/libmeson_static.a
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC/libmeson_static.a
diff --git a/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.dll.lib
diff --git a/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC/rustc_import.lib
diff --git a/Tests/RunCMake/find_library/Windows-MSVC/static.lib b/Tests/RunCMake/find_library/Windows-MSVC/static.lib
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/find_library/Windows-MSVC/static.lib
diff --git a/Tests/RunCMake/find_path/NO_CACHE-stderr.txt b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt
new file mode 100644
index 0000000..6da353b
--- /dev/null
+++ b/Tests/RunCMake/find_path/NO_CACHE-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0125 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/NO_CACHE-stderr.txt b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt
new file mode 100644
index 0000000..6da353b
--- /dev/null
+++ b/Tests/RunCMake/find_program/NO_CACHE-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at NO_CACHE\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0125 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt
new file mode 100644
index 0000000..7a49647
--- /dev/null
+++ b/Tests/RunCMake/foreach/foreach-var-scope-CMP0124-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at foreach-var-scope-CMP0124-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0124 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/if/FilePermissions.cmake b/Tests/RunCMake/if/FilePermissions.cmake
new file mode 100644
index 0000000..9edbddb
--- /dev/null
+++ b/Tests/RunCMake/if/FilePermissions.cmake
@@ -0,0 +1,179 @@
+
+file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
+            "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
+            "${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" "foo")
+file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" "foo")
+file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" PERMISSIONS OWNER_WRITE GROUP_WRITE)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" "foo")
+file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+
+if(NOT WIN32)
+  file(REMOVE_RECURSE
+    "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
+    "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
+    "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
+    )
+
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/readable-dir")
+  file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
+
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/writable-dir")
+  file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" PERMISSIONS OWNER_WRITE GROUP_WRITE)
+
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/executable-dir")
+  file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+endif()
+
+function(cleanup)
+  if(NOT WIN32)
+    # CMake versions prior to 3.29 did not know how to remove non-readable directories.
+    file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_WRITE)
+    file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" PERMISSIONS OWNER_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+  endif()
+endfunction()
+
+if(WIN32)
+  # files are always readable and executable
+  # directories are always, readable, writable and executable
+  if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed")
+  endif()
+
+  if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
+  endif()
+else()
+  if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
+      OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed")
+  endif()
+
+  if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
+      OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
+      OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable.txt\" failed")
+  endif()
+
+  if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
+      OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
+  endif()
+
+
+  if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
+      OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable-dir\" failed")
+  endif()
+
+  if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
+      OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
+      OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable-dir\" failed")
+  endif()
+
+  if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
+      OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
+      OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
+  endif()
+endif()
+
+if(UNIX)
+  #
+  # Check that file permissions are on the real file, not the symbolic link
+  #
+  file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
+              "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
+              "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
+              "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
+              "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
+              "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir")
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
+    SYMBOLIC)
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
+    SYMBOLIC)
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
+    SYMBOLIC)
+
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
+    SYMBOLIC)
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
+    SYMBOLIC)
+
+  file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
+    "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
+    SYMBOLIC)
+
+  if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
+     OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
+     OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt\" failed")
+  endif()
+
+  if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
+     OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
+     OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt\" failed")
+  endif()
+
+  if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
+     OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
+     OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt\" failed")
+  endif()
+
+
+  if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
+     OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
+     OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir\" failed")
+  endif()
+
+  if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
+     OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
+     OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir\" failed")
+  endif()
+
+  if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
+     OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
+     OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir")
+    cleanup()
+    message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir\" failed")
+  endif()
+endif()
+
+cleanup()
diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake
index efee116..43dfd3c 100644
--- a/Tests/RunCMake/if/RunCMakeTest.cmake
+++ b/Tests/RunCMake/if/RunCMakeTest.cmake
@@ -2,6 +2,14 @@
 
 run_cmake(InvalidArgument1)
 run_cmake(exists)
+if(NOT MSYS)
+  # permissions and symbolic links are broken on MSYS
+  # if real user is root, tests are irrelevant
+  get_unix_uid(uid)
+  if(NOT uid STREQUAL "0")
+    run_cmake(FilePermissions)
+  endif()
+endif()
 run_cmake(IsDirectory)
 run_cmake(IsDirectoryLong)
 run_cmake(duplicate-deep-else)
diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake
new file mode 100644
index 0000000..6a8b2e5
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport-all-check.cmake
@@ -0,0 +1,4 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/root-all/lib/cmake/mylib/mylib-targets.cmake" contents)
+if(NOT contents MATCHES "include\\(CMakeFindDependencyMacro\\)\nfind_dependency\\(P2 REQUIRED\\)\nfind_dependency\\(P1 REQUIRED\\)\n")
+  set(RunCMake_TEST_FAILED "Dependencies were not properly exported")
+endif()
diff --git a/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake
new file mode 100644
index 0000000..071414b
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-FindDependencyExport.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+find_package(P1 REQUIRED)
+find_package(P2 REQUIRED)
+find_package(P3 REQUIRED)
+
+add_library(mylib INTERFACE)
+target_link_libraries(mylib INTERFACE lib1 lib2 lib3)
+install(TARGETS mylib EXPORT mylib-targets)
+export(SETUP mylib-targets
+  PACKAGE_DEPENDENCY P2
+    ENABLED AUTO
+  PACKAGE_DEPENDENCY P3
+    ENABLED OFF
+  )
+install(EXPORT mylib-targets EXPORT_PACKAGE_DEPENDENCIES FILE mylib-targets.cmake DESTINATION lib/cmake/mylib)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index efafdd1..6f63550 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -176,6 +176,7 @@
 run_install_test(FILES-PERMISSIONS)
 run_install_test(TARGETS-RPATH)
 run_install_test(InstallRequiredSystemLibraries)
+run_install_test(EXPORT-FindDependencyExport)
 
 set(RunCMake_TEST_OPTIONS "-DCMAKE_POLICY_DEFAULT_CMP0087:STRING=NEW")
 run_install_test(SCRIPT)
diff --git a/Tests/RunCMake/install/cmake/FindP1.cmake b/Tests/RunCMake/install/cmake/FindP1.cmake
new file mode 100644
index 0000000..c772836
--- /dev/null
+++ b/Tests/RunCMake/install/cmake/FindP1.cmake
@@ -0,0 +1,2 @@
+add_library(lib1 IMPORTED INTERFACE)
+set(P1_FOUND TRUE)
diff --git a/Tests/RunCMake/install/cmake/FindP2.cmake b/Tests/RunCMake/install/cmake/FindP2.cmake
new file mode 100644
index 0000000..d81b35d
--- /dev/null
+++ b/Tests/RunCMake/install/cmake/FindP2.cmake
@@ -0,0 +1,2 @@
+add_library(lib2 IMPORTED INTERFACE)
+set(P2_FOUND TRUE)
diff --git a/Tests/RunCMake/install/cmake/FindP3.cmake b/Tests/RunCMake/install/cmake/FindP3.cmake
new file mode 100644
index 0000000..2818c3b
--- /dev/null
+++ b/Tests/RunCMake/install/cmake/FindP3.cmake
@@ -0,0 +1,2 @@
+add_library(lib3 IMPORTED INTERFACE)
+set(P3_FOUND TRUE)
diff --git a/Tests/RunCMake/print_stdin.c b/Tests/RunCMake/print_stdin.c
index e083e62..76b3a84 100644
--- a/Tests/RunCMake/print_stdin.c
+++ b/Tests/RunCMake/print_stdin.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   char buf[1024];
   size_t nIn = sizeof(buf);
diff --git a/Tests/RunCMake/project/CodeInjection-stdout.txt b/Tests/RunCMake/project/CodeInjection-stdout.txt
deleted file mode 100644
index 88ac966..0000000
--- a/Tests/RunCMake/project/CodeInjection-stdout.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE
-(-- )?Included CMAKE_TOOLCHAIN_FILE
-.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file
-(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file
-(-- )?Included CMAKE_PROJECT_INCLUDE
-(-- )?Calling sub-project
-(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE
-(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE
-(-- )?Included CMAKE_PROJECT_INCLUDE
-(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake
deleted file mode 100644
index f3f0a7e..0000000
--- a/Tests/RunCMake/project/CodeInjection/cmake_project_include.cmake
+++ /dev/null
@@ -1 +0,0 @@
-message(STATUS "Included CMAKE_PROJECT_INCLUDE")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake
deleted file mode 100644
index 01d53c9..0000000
--- a/Tests/RunCMake/project/CodeInjection/cmake_project_include_before.cmake
+++ /dev/null
@@ -1 +0,0 @@
-message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake
new file mode 100644
index 0000000..2bc65cf
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_1.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_INCLUDE first file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake
new file mode 100644
index 0000000..df7240c
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_2.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_INCLUDE second file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake
new file mode 100644
index 0000000..20bea78
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_1.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE first file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake
new file mode 100644
index 0000000..91b59d1
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_includes_before_2.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_INCLUDE_BEFORE second file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake
deleted file mode 100644
index d68de6a..0000000
--- a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include.cmake
+++ /dev/null
@@ -1 +0,0 @@
-message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake
deleted file mode 100644
index ef3bfc0..0000000
--- a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_include_before.cmake
+++ /dev/null
@@ -1 +0,0 @@
-message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake
new file mode 100644
index 0000000..fe0fe4a
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_1.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE first file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake
new file mode 100644
index 0000000..c36fb52
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_2.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE second file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake
new file mode 100644
index 0000000..23ae05a
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_1.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file")
diff --git a/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake
new file mode 100644
index 0000000..17f1d29
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/cmake_project_subproj_includes_before_2.cmake
@@ -0,0 +1 @@
+message(STATUS "Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file")
diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache.cmake
deleted file mode 100644
index 6c8995b..0000000
--- a/Tests/RunCMake/project/CodeInjection/initial_cache.cmake
+++ /dev/null
@@ -1,10 +0,0 @@
-set(CMAKE_TOOLCHAIN_FILE                 "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "")
-set(CMAKE_PROJECT_INCLUDE                "${CMAKE_CURRENT_LIST_DIR}/cmake_project_include.cmake" CACHE FILEPATH "")
-set(CMAKE_PROJECT_INCLUDE_BEFORE         "${CMAKE_CURRENT_LIST_DIR}/cmake_project_include_before.cmake" CACHE FILEPATH "")
-set(CMAKE_PROJECT_SubProj_INCLUDE        "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_include.cmake" CACHE FILEPATH "")
-set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_include_before.cmake" CACHE FILEPATH "")
-set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
-  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake"
-  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_2.cmake"
-  CACHE STRING ""
-)
diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache_1.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_1.cmake
new file mode 100644
index 0000000..43bb817
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/initial_cache_1.cmake
@@ -0,0 +1,9 @@
+set(CMAKE_TOOLCHAIN_FILE                 "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_INCLUDE                "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_INCLUDE_BEFORE         "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_SubProj_INCLUDE        "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake"
+  CACHE FILEPATH ""
+)
diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake
new file mode 100644
index 0000000..09fcbfd
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/initial_cache_2.cmake
@@ -0,0 +1,27 @@
+set(CMAKE_TOOLCHAIN_FILE
+  "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_INCLUDE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake"
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_2.cmake"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_INCLUDE_BEFORE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake"
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_2.cmake"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_SubProj_INCLUDE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake"
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_2.cmake"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake"
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_2.cmake"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake"
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_2.cmake"
+  CACHE STRING ""
+)
diff --git a/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake b/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake
new file mode 100644
index 0000000..dd299bc
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection/initial_cache_3.cmake
@@ -0,0 +1,28 @@
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}" CACHE STRING "")
+set(CMAKE_TOOLCHAIN_FILE
+  "${CMAKE_CURRENT_LIST_DIR}/passthrough_toolchain_file.cmake" CACHE FILEPATH "")
+set(CMAKE_PROJECT_INCLUDE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_1.cmake"
+  "cmake_project_includes_2"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_INCLUDE_BEFORE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_includes_before_1.cmake"
+  "cmake_project_includes_before_2"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_SubProj_INCLUDE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_1.cmake"
+  "cmake_project_subproj_includes_2"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_SubProj_INCLUDE_BEFORE
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_subproj_includes_before_1.cmake"
+  "cmake_project_subproj_includes_before_2"
+  CACHE STRING ""
+)
+set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
+  "${CMAKE_CURRENT_LIST_DIR}/cmake_project_top_level_includes_1.cmake"
+  "cmake_project_top_level_includes_2"
+  CACHE STRING ""
+)
diff --git a/Tests/RunCMake/project/CodeInjection1-stdout.txt b/Tests/RunCMake/project/CodeInjection1-stdout.txt
new file mode 100644
index 0000000..7a780b7
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection1-stdout.txt
@@ -0,0 +1,9 @@
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_TOOLCHAIN_FILE
+.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Calling sub-project
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file
diff --git a/Tests/RunCMake/project/CodeInjection.cmake b/Tests/RunCMake/project/CodeInjection1.cmake
similarity index 100%
rename from Tests/RunCMake/project/CodeInjection.cmake
rename to Tests/RunCMake/project/CodeInjection1.cmake
diff --git a/Tests/RunCMake/project/CodeInjection2-stdout.txt b/Tests/RunCMake/project/CodeInjection2-stdout.txt
new file mode 100644
index 0000000..5c18cdf
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection2-stdout.txt
@@ -0,0 +1,16 @@
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_TOOLCHAIN_FILE
+.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file
+(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE second file
+(-- )?Calling sub-project
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE second file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE second file
diff --git a/Tests/RunCMake/project/CodeInjection.cmake b/Tests/RunCMake/project/CodeInjection2.cmake
similarity index 100%
copy from Tests/RunCMake/project/CodeInjection.cmake
copy to Tests/RunCMake/project/CodeInjection2.cmake
diff --git a/Tests/RunCMake/project/CodeInjection3-stdout.txt b/Tests/RunCMake/project/CodeInjection3-stdout.txt
new file mode 100644
index 0000000..5c18cdf
--- /dev/null
+++ b/Tests/RunCMake/project/CodeInjection3-stdout.txt
@@ -0,0 +1,16 @@
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_TOOLCHAIN_FILE
+.*Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES first file
+(-- )?Included CMAKE_PROJECT_TOP_LEVEL_INCLUDES second file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE second file
+(-- )?Calling sub-project
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE_BEFORE second file
+(-- )?Included CMAKE_PROJECT_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_INCLUDE second file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE first file
+(-- )?Included CMAKE_PROJECT_SubProj_INCLUDE second file
diff --git a/Tests/RunCMake/project/CodeInjection.cmake b/Tests/RunCMake/project/CodeInjection3.cmake
similarity index 100%
copy from Tests/RunCMake/project/CodeInjection.cmake
copy to Tests/RunCMake/project/CodeInjection3.cmake
diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake
index 0f3716f..16f10be 100644
--- a/Tests/RunCMake/project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/project/RunCMakeTest.cmake
@@ -5,8 +5,16 @@
 # which tests some of the individual variables one at a time.
 # Here, we are focused on testing that the variables are all injected
 # at the expected points in the expected order.
-run_cmake_with_options(CodeInjection
-  -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache.cmake"
+run_cmake_with_options(CodeInjection1
+  -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_1.cmake"
+)
+# This checks that List variables are allowed.
+run_cmake_with_options(CodeInjection2
+        -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_2.cmake"
+)
+# This checks that module names are also allowed.
+run_cmake_with_options(CodeInjection3
+        -C "${CMAKE_CURRENT_LIST_DIR}/CodeInjection/initial_cache_3.cmake"
 )
 
 if(CMake_TEST_RESOURCES)
diff --git a/Tests/RunCMake/property_init/Executable.cmake b/Tests/RunCMake/property_init/Executable.cmake
index ede0e4b..a5e4fb8 100644
--- a/Tests/RunCMake/property_init/Executable.cmake
+++ b/Tests/RunCMake/property_init/Executable.cmake
@@ -17,6 +17,7 @@
 
   # Metadata
   "CROSSCOMPILING_EMULATOR"       "emu"     "<SAME>"
+  "TEST_LAUNCHER"                 "test"    "<SAME>"
   )
 
 prepare_target_types(executable
diff --git a/Tests/RunCMake/pseudo_emulator.c b/Tests/RunCMake/pseudo_emulator.c
index 15f64dc..cc921f0 100644
--- a/Tests/RunCMake/pseudo_emulator.c
+++ b/Tests/RunCMake/pseudo_emulator.c
@@ -1,8 +1,14 @@
+#ifndef _CRT_SECURE_NO_WARNINGS
+#  define _CRT_SECURE_NO_WARNINGS
+#endif
+
 #include <stdio.h>
+#include <stdlib.h>
 
 int main(int argc, char* argv[])
 {
   int ii;
+  const char* fail = getenv("PSEUDO_EMULATOR_FAIL");
 
   printf("Command:");
   for (ii = 1; ii < argc; ++ii) {
@@ -10,5 +16,9 @@
   }
   printf("\n");
 
-  return 42;
+  if (fail && *fail) {
+    return 42;
+  }
+
+  return 0;
 }
diff --git a/Tests/RunCMake/set_property/IMPORTED_GLOBAL-stderr.txt b/Tests/RunCMake/set_property/IMPORTED_GLOBAL-stderr.txt
index e45fc64..8f575b5 100644
--- a/Tests/RunCMake/set_property/IMPORTED_GLOBAL-stderr.txt
+++ b/Tests/RunCMake/set_property/IMPORTED_GLOBAL-stderr.txt
@@ -23,8 +23,8 @@
 
 
 CMake Error at IMPORTED_GLOBAL.cmake:32 \(set_property\):
-  IMPORTED_GLOBAL property can't be set on non-imported targets
-  \(\"NonImportedTarget\"\)
+  IMPORTED_GLOBAL property can't be set on non-imported
+  targets\(\"NonImportedTarget\"\)
 
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/showIncludes.c b/Tests/RunCMake/showIncludes.c
index cfc8572..3859049 100644
--- a/Tests/RunCMake/showIncludes.c
+++ b/Tests/RunCMake/showIncludes.c
@@ -10,7 +10,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-int main()
+int main(void)
 {
   /* 'cl /showIncludes' encodes output in the console output code page.  */
   unsigned int cp = GetConsoleOutputCP();
diff --git a/Tests/RunCMake/target_compile_definitions/foo.c b/Tests/RunCMake/target_compile_definitions/foo.c
index 74a86e1..7d75e37 100644
--- a/Tests/RunCMake/target_compile_definitions/foo.c
+++ b/Tests/RunCMake/target_compile_definitions/foo.c
@@ -1,4 +1,4 @@
 
-void foo()
+void foo(void)
 {
 }
diff --git a/Tests/RunCMake/target_compile_features/empty.c b/Tests/RunCMake/target_compile_features/empty.c
index 11ec041..8d91e77 100644
--- a/Tests/RunCMake/target_compile_features/empty.c
+++ b/Tests/RunCMake/target_compile_features/empty.c
@@ -1,7 +1,7 @@
 #ifdef _WIN32
 __declspec(dllexport)
 #endif
-  int empty()
+  int empty(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/target_compile_options/CMP0101.c b/Tests/RunCMake/target_compile_options/CMP0101.c
index 250869a..7ef6117 100644
--- a/Tests/RunCMake/target_compile_options/CMP0101.c
+++ b/Tests/RunCMake/target_compile_options/CMP0101.c
@@ -3,7 +3,7 @@
 #  error "BEFORE not honored"
 #endif
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/func.c b/Tests/RunCMake/target_link_libraries-ALIAS/func.c
index 415a9bf..ebc6a5f 100644
--- a/Tests/RunCMake/target_link_libraries-ALIAS/func.c
+++ b/Tests/RunCMake/target_link_libraries-ALIAS/func.c
@@ -2,6 +2,6 @@
 #if defined(_WIN32)
 __declspec(dllexport)
 #endif
-  void func_c()
+  void func_c(void)
 {
 }
diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/lib.c b/Tests/RunCMake/target_link_libraries-ALIAS/lib.c
index b2d1b66..95c0e91 100644
--- a/Tests/RunCMake/target_link_libraries-ALIAS/lib.c
+++ b/Tests/RunCMake/target_link_libraries-ALIAS/lib.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-void lib()
+void lib(void)
 {
   func_c();
 }
diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/main.c b/Tests/RunCMake/target_link_libraries-ALIAS/main.c
index a908dea..72a3ddb 100644
--- a/Tests/RunCMake/target_link_libraries-ALIAS/main.c
+++ b/Tests/RunCMake/target_link_libraries-ALIAS/main.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-int main()
+int main(void)
 {
   func_c();
 
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c
index a5075d4..ed769a0 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/base.c
@@ -4,6 +4,6 @@
 __declspec(dllexport)
 #  endif
 #endif
-  void base()
+  void base(void)
 {
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c
index 3399e00..ef11bc2 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func1.c
@@ -1,7 +1,7 @@
 
-extern void func2();
+extern void func2(void);
 
-void func1()
+void func1(void)
 {
   func2();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c
index 0f9aa64..3eab38e 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func2.c
@@ -1,7 +1,7 @@
 
-extern void func3();
+extern void func3(void);
 
-void func2()
+void func2(void)
 {
   func3();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c
index 0b7df64..c109e09 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/func3.c
@@ -1,6 +1,6 @@
 
-extern void func3();
+extern void func3(void);
 
-void func3()
+void func3(void)
 {
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c
index 35ab367..21f559c 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/lib.c
@@ -4,12 +4,12 @@
 __declspec(dllimport)
 #  endif
 #endif
-  void base();
+  void base(void);
 
 #if defined(_WIN32)
 __declspec(dllexport)
 #endif
-  void lib()
+  void lib(void)
 {
   base();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c b/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c
index 403583d..d78b0f6 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_GROUP/main.c
@@ -1,7 +1,7 @@
 
-extern void func1();
+extern void func1(void);
 
-int main()
+int main(void)
 {
   func1();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c
index 415a9bf..ebc6a5f 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/func.c
@@ -2,6 +2,6 @@
 #if defined(_WIN32)
 __declspec(dllexport)
 #endif
-  void func_c()
+  void func_c(void)
 {
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c
index b2d1b66..95c0e91 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/lib.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-void lib()
+void lib(void)
 {
   func_c();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c
index 689dbd7..891c4ad 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/main.c
@@ -1,14 +1,14 @@
 
 #if defined(C_USE_CXX)
-void func_c_cxx();
+void func_c_cxx(void);
 #else
 #  if defined(_WIN32)
 __declspec(dllimport)
 #  endif
-  void func_c();
+  void func_c(void);
 #endif
 
-int main()
+int main(void)
 {
 #if defined(C_USE_CXX)
   func_c_cxx();
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c
index 415a9bf..ebc6a5f 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/func.c
@@ -2,6 +2,6 @@
 #if defined(_WIN32)
 __declspec(dllexport)
 #endif
-  void func_c()
+  void func_c(void)
 {
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c
index b2d1b66..95c0e91 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/lib.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-void lib()
+void lib(void)
 {
   func_c();
 }
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c
index a908dea..72a3ddb 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/main.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-int main()
+int main(void)
 {
   func_c();
 
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake
new file mode 100644
index 0000000..7003ade
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-check.cmake
@@ -0,0 +1,4 @@
+
+if (actual_stdout MATCHES "(/|-)-LIBFLAG${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}")
+  set (RunCMake_TEST_FAILED "Found unexpected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-consuming_LINK_LIBRARIES_DIRECT-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-group2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-link-items4-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake
new file mode 100644
index 0000000..eb1f755
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake
new file mode 100644
index 0000000..eb1f755
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base3> --LIBGROUP<base1> --SUFFIXGROUP'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake
new file mode 100644
index 0000000..783bad9
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}")
+    set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP <other2> <other1>'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base2${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other2${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}")
+    set (RunCMake_TEST_FAILED "Not found expected '<base2> --PREFIXGROUP --LIBGROUP<base3> --SUFFIXGROUP <other2> --PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP <other1>'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-mix-features3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-nested-feature2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake
new file mode 100644
index 0000000..ce72570
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base3> <other1>'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake
new file mode 100644
index 0000000..817b6e2
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}other1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP --LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<other1> --SUFFIXGROUP'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake
new file mode 100644
index 0000000..ce72570
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base1> --LIBFLAG<base3> <other1>'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --LIBFLAG<base1> <other1>'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake
new file mode 100644
index 0000000..700bcf2
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP\"?")
+    set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --SUFFIXGROUP --LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<other1> --SUFFIXGROUP'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUPother1${LINK_EXTERN_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> --PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<other1> --SUFFIXGROUP'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-features4-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake
new file mode 100644
index 0000000..3a44bc0
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-check.cmake
@@ -0,0 +1,12 @@
+
+include("${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-NEW-build/LIBRARIES_PROCESSING.cmake")
+
+if (CMAKE_C_LINK_LIBRARIES_PROCESSING MATCHES "ORDER=FORWARD")
+  if (NOT actual_stdout MATCHES "${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}\"?")
+    set (RunCMake_TEST_FAILED "Not found expected '<base1> --LIBFLAG<base3> <other1>'.")
+  endif()
+else()
+  if (NOT actual_stdout MATCHES "(/|-)-LIBFLAG.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?${CMAKE_LINK_LIBRARY_FLAG}other1${LINK_EXTERN_LIBRARY_SUFFIX}\"?")
+    set (RunCMake_TEST_FAILED "Not found expected '--LIBFLAG<base3> <base1> <other1>'.")
+  endif()
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-override-with-DEFAULT-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-result.txt
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-NEW-simple2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake
new file mode 100644
index 0000000..e04526a
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-check.cmake
@@ -0,0 +1,3 @@
+if (actual_stdout MATCHES "(/|-)-LIBFLAG${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}")
+  set (RunCMake_TEST_FAILED "Found unexpected '--LIBFLAG<base1>'.")
+endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-consuming_LINK_LIBRARIES_DIRECT-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-group2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-group2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items3-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-link-items4-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-link-items4-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features3-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-mix-features3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-nested-feature2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-nested-feature2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features3-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features3-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-features4-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-features4-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-check.cmake
similarity index 100%
rename from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-check.cmake
rename to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-override-with-DEFAULT-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-override-with-DEFAULT-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple1-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple1-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-check.cmake
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-check.cmake
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-check.cmake
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-result.txt
similarity index 100%
copy from Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-simple2-result.txt
copy to Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-CMP0156-OLD-simple2-result.txt
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
deleted file mode 100644
index 858dcfe..0000000
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY-mix-features1-check.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-
-if (NOT actual_stdout MATCHES "(/|-)-PREFIXGROUP\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base3${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-LIBGROUP.*${LINK_SHARED_LIBRARY_PREFIX}base1${LINK_SHARED_LIBRARY_SUFFIX}\"? +\"?(/|-)-SUFFIXGROUP")
-  set (RunCMake_TEST_FAILED "Not found expected '--PREFIXGROUP --LIBGROUP<base1> --LIBGROUP<base3> --SUFFIXGROUP'.")
-endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
index f19112a..0aa11be 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/LINK_LIBRARY.cmake
@@ -1,5 +1,12 @@
 enable_language(C)
 
+if(CMP0156 STREQUAL "NEW")
+  cmake_policy(SET CMP0156 NEW)
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/LIBRARIES_PROCESSING.cmake" "set(CMAKE_C_LINK_LIBRARIES_PROCESSING \"${CMAKE_C_LINK_LIBRARIES_PROCESSING}\")\n")
+else()
+  cmake_policy(SET CMP0156 OLD)
+endif()
+
 # ensure command line is always displayed and do not use any response file
 set(CMAKE_VERBOSE_MAKEFILE TRUE)
 set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
@@ -101,3 +108,10 @@
 set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE "feat3,base1,other1")
 set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_base1 feat2)
 set_property(TARGET LinkLibrary_override_features4 PROPERTY LINK_LIBRARY_OVERRIDE_other1 feat2)
+
+# testing NTERFACE_LINK_LIBRARIES_DIRECT property
+add_library(lib_with_LINK_LIBRARIES_DIRECT SHARED base.c)
+set_property(TARGET lib_with_LINK_LIBRARIES_DIRECT PROPERTY INTERFACE_LINK_LIBRARIES_DIRECT base1)
+
+add_library(LinkLibrary_consuming_LINK_LIBRARIES_DIRECT SHARED lib.c)
+target_link_libraries(LinkLibrary_consuming_LINK_LIBRARIES_DIRECT PRIVATE $<LINK_LIBRARY:feat1,lib_with_LINK_LIBRARIES_DIRECT>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
index 0f3a6b7..88a7e63 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -37,29 +37,35 @@
     set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
   endif()
 
-  run_cmake(LINK_LIBRARY)
+  foreach(policy IN ITEMS OLD NEW)
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/LINK_LIBRARY-CMP0156-${policy}-build)
+    run_cmake_with_options(LINK_LIBRARY -DCMP0156=${policy})
 
-  run_cmake_target(LINK_LIBRARY simple1 LinkLibrary_simple1)
-  run_cmake_target(LINK_LIBRARY simple2 LinkLibrary_simple2)
-  run_cmake_target(LINK_LIBRARY group1 LinkLibrary_group1)
-  run_cmake_target(LINK_LIBRARY group2 LinkLibrary_group2)
-  run_cmake_target(LINK_LIBRARY nested-feature1 LinkLibrary_nested_feature1)
-  run_cmake_target(LINK_LIBRARY nested-feature2 LinkLibrary_nested_feature2)
-  run_cmake_target(LINK_LIBRARY link-items1 LinkLibrary_link_items1)
-  run_cmake_target(LINK_LIBRARY link-items2 LinkLibrary_link_items2)
-  run_cmake_target(LINK_LIBRARY link-items3 LinkLibrary_link_items3)
-  run_cmake_target(LINK_LIBRARY link-items4 LinkLibrary_link_items4)
-  run_cmake_target(LINK_LIBRARY mix-features1 LinkLibrary_mix_features1)
-  run_cmake_target(LINK_LIBRARY mix-features2 LinkLibrary_mix_features2)
-  run_cmake_target(LINK_LIBRARY mix-features3 LinkLibrary_mix_features3)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} simple1 LinkLibrary_simple1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} simple2 LinkLibrary_simple2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} group1 LinkLibrary_group1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} group2 LinkLibrary_group2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} nested-feature1 LinkLibrary_nested_feature1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} nested-feature2 LinkLibrary_nested_feature2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items1 LinkLibrary_link_items1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items2 LinkLibrary_link_items2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items3 LinkLibrary_link_items3)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} link-items4 LinkLibrary_link_items4)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features1 LinkLibrary_mix_features1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features2 LinkLibrary_mix_features2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} mix-features3 LinkLibrary_mix_features3)
 
-  # testing target property LINK_LIBRARY_OVERRIDE
-  run_cmake_target(LINK_LIBRARY override-features1 LinkLibrary_override_features1)
-  run_cmake_target(LINK_LIBRARY override-features2 LinkLibrary_override_features2)
-  run_cmake_target(LINK_LIBRARY override-with-DEFAULT LinkLibrary_override_with_default)
-  # testing target property LINK_LIBRARY_OVERRIDE_<LIBRARY>
-  run_cmake_target(LINK_LIBRARY override-features3 LinkLibrary_override_features3)
-  run_cmake_target(LINK_LIBRARY override-features4 LinkLibrary_override_features4)
+    # testing target property LINK_LIBRARY_OVERRIDE
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features1 LinkLibrary_override_features1)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features2 LinkLibrary_override_features2)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-with-DEFAULT LinkLibrary_override_with_default)
+    # testing target property LINK_LIBRARY_OVERRIDE_<LIBRARY>
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features3 LinkLibrary_override_features3)
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} override-features4 LinkLibrary_override_features4)
+
+    # testing target property INTERFACE_LINK_LIBRARIES_DIRECT
+    run_cmake_target(LINK_LIBRARY-CMP0156-${policy} consuming_LINK_LIBRARIES_DIRECT LinkLibrary_consuming_LINK_LIBRARIES_DIRECT)
+  endforeach()
 
   run_cmake(imported-target)
 
diff --git a/Tests/RunCMake/target_link_libraries/lib.c b/Tests/RunCMake/target_link_libraries/lib.c
index b2d1b66..95c0e91 100644
--- a/Tests/RunCMake/target_link_libraries/lib.c
+++ b/Tests/RunCMake/target_link_libraries/lib.c
@@ -2,9 +2,9 @@
 #if defined(_WIN32)
 __declspec(dllimport)
 #endif
-  void func_c();
+  void func_c(void);
 
-void lib()
+void lib(void)
 {
   func_c();
 }
diff --git a/Tests/RunCMake/target_sources/FileSetReadOnlyInterface-stderr.txt b/Tests/RunCMake/target_sources/FileSetReadOnlyInterface-stderr.txt
index 2307d13..50901fe 100644
--- a/Tests/RunCMake/target_sources/FileSetReadOnlyInterface-stderr.txt
+++ b/Tests/RunCMake/target_sources/FileSetReadOnlyInterface-stderr.txt
@@ -1,5 +1,5 @@
 ^CMake Error at FileSetReadOnlyInterface\.cmake:[0-9]+ \(set_property\):
-  INTERFACE_HEADER_SETS property is read-only
+  INTERFACE_HEADER_SETS property is read-only for target\("lib1"\)
 
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-stderr.txt b/Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-stderr.txt
index 5f955da..4767894 100644
--- a/Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-stderr.txt
+++ b/Tests/RunCMake/target_sources/FileSetReadOnlyPrivate-stderr.txt
@@ -1,5 +1,5 @@
 ^CMake Error at FileSetReadOnlyPrivate\.cmake:[0-9]+ \(set_property\):
-  HEADER_SETS property is read-only
+  HEADER_SETS property is read-only for target\("lib1"\)
 
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/LinkOptions.cmake b/Tests/RunCMake/try_compile/LinkOptions.cmake
index 7fae35c..45cbedf 100644
--- a/Tests/RunCMake/try_compile/LinkOptions.cmake
+++ b/Tests/RunCMake/try_compile/LinkOptions.cmake
@@ -1,8 +1,5 @@
-
 enable_language(C)
 
-cmake_policy(SET CMP0054 NEW)
-
 set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}")
 if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
   if (RunCMake_C_COMPILER_ID STREQUAL "MSVC"
diff --git a/Tests/RunCMake/try_compile/lib.c b/Tests/RunCMake/try_compile/lib.c
index b00c576..ce64a13 100644
--- a/Tests/RunCMake/try_compile/lib.c
+++ b/Tests/RunCMake/try_compile/lib.c
@@ -1,4 +1,4 @@
 
-void func()
+void func(void)
 {
 }
diff --git a/Tests/RunCMake/try_compile/main.c b/Tests/RunCMake/try_compile/main.c
index 2128ead..6b1e682 100644
--- a/Tests/RunCMake/try_compile/main.c
+++ b/Tests/RunCMake/try_compile/main.c
@@ -1,4 +1,4 @@
-extern void func();
+extern void func(void);
 
 int main(void)
 {
diff --git a/Tests/RunCMake/try_run/ConfigureLog-test.c b/Tests/RunCMake/try_run/ConfigureLog-test.c
index 6a8f125..465069d 100644
--- a/Tests/RunCMake/try_run/ConfigureLog-test.c
+++ b/Tests/RunCMake/try_run/ConfigureLog-test.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   fprintf(stderr, "Output, with backslash '\\', on stderr!\n");
   fflush(stderr); /* make output deterministic even if stderr is buffered */
diff --git a/Tests/RunCMake/try_run/Inspect.cmake b/Tests/RunCMake/try_run/Inspect.cmake
new file mode 100644
index 0000000..66698d6
--- /dev/null
+++ b/Tests/RunCMake/try_run/Inspect.cmake
@@ -0,0 +1,22 @@
+enable_language(C)
+enable_language(CXX)
+if(CMake_TEST_Fortran)
+  enable_language(Fortran)
+endif()
+
+set(info "")
+foreach(var
+    CMAKE_SYSTEM_NAME
+    CMAKE_C_COMPILER_ID
+    CMAKE_C_COMPILER_VERSION
+    CMAKE_CXX_COMPILER_ID
+    CMAKE_CXX_COMPILER_VERSION
+    CMAKE_Fortran_COMPILER_ID
+    CMAKE_Fortran_COMPILER_VERSION
+    )
+  if(DEFINED ${var})
+    string(APPEND info "set(${var} \"${${var}}\")\n")
+  endif()
+endforeach()
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/try_run/LinkOptions.cmake b/Tests/RunCMake/try_run/LinkOptions.cmake
index b9a87f3..b19141c 100644
--- a/Tests/RunCMake/try_run/LinkOptions.cmake
+++ b/Tests/RunCMake/try_run/LinkOptions.cmake
@@ -1,8 +1,5 @@
-
 enable_language(C)
 
-cmake_policy(SET CMP0054 NEW)
-
 set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}")
 if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
   if (RunCMake_C_COMPILER_ID STREQUAL "MSVC"
diff --git a/Tests/RunCMake/try_run/LinkerLanguage.cmake b/Tests/RunCMake/try_run/LinkerLanguage.cmake
new file mode 100644
index 0000000..137e198
--- /dev/null
+++ b/Tests/RunCMake/try_run/LinkerLanguage.cmake
@@ -0,0 +1,29 @@
+enable_language(CXX)
+enable_language(Fortran)
+
+set (lib_name "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}lib${CMAKE_STATIC_LIBRARY_SUFFIX}")
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+  if (CMAKE_SIZEOF_VOID_P EQUAL 4)
+    set (undef_flag -u _func)
+  else()
+    set (undef_flag -u func)
+  endif()
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+  set (undef_flag -u _func)
+else()
+  set (undef_flag -u func)
+endif()
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
+try_run(run_result compile_result
+  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/lib.cxx ${CMAKE_CURRENT_SOURCE_DIR}/main.f90
+  COMPILE_OUTPUT_VARIABLE compile_out
+  RUN_OUTPUT_VARIABLE run_out
+  LINKER_LANGUAGE Fortran)
+
+if(NOT compile_result)
+  message(FATAL_ERROR "try_run(... LINKER_LANGUAGE Fortran) compilation failed:\n${compile_out}")
+endif()
+if(run_result STREQUAL "FAILED_TO_RUN")
+  message(FATAL_ERROR "try_run(... LINKER_LANGUAGE Fortran) execution failed:\n${run_out}")
+endif()
diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake
index 62e3caf..7e9b2d1 100644
--- a/Tests/RunCMake/try_run/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake
@@ -1,5 +1,16 @@
 include(RunCMake)
 
+# Detect information from the toolchain:
+# - CMAKE_SYSTEM_NAME
+# - CMAKE_C_COMPILER_ID
+# - CMAKE_C_COMPILER_VERSION
+# - CMAKE_CXX_COMPILER_ID
+# - CMAKE_CXX_COMPILER_VERSION
+run_cmake_with_options(Inspect
+  -DCMake_TEST_Fortran=${CMake_TEST_Fortran}
+  )
+include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
+
 run_cmake(BinDirEmpty)
 run_cmake(BinDirRelative)
 run_cmake(NoOutputVariable)
@@ -19,3 +30,9 @@
   run_cmake(LinkOptions)
   unset (RunCMake_TEST_OPTIONS)
 endif()
+
+if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
+    CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$" AND
+    (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3))
+  run_cmake(LinkerLanguage)
+endif()
diff --git a/Tests/RunCMake/try_run/lib.c b/Tests/RunCMake/try_run/lib.c
index b00c576..ce64a13 100644
--- a/Tests/RunCMake/try_run/lib.c
+++ b/Tests/RunCMake/try_run/lib.c
@@ -1,4 +1,4 @@
 
-void func()
+void func(void)
 {
 }
diff --git a/Tests/RunCMake/try_run/lib.cxx b/Tests/RunCMake/try_run/lib.cxx
new file mode 100644
index 0000000..b01a075
--- /dev/null
+++ b/Tests/RunCMake/try_run/lib.cxx
@@ -0,0 +1,4 @@
+
+extern "C" void func()
+{
+}
diff --git a/Tests/RunCMake/try_run/main.c b/Tests/RunCMake/try_run/main.c
index 2128ead..6b1e682 100644
--- a/Tests/RunCMake/try_run/main.c
+++ b/Tests/RunCMake/try_run/main.c
@@ -1,4 +1,4 @@
-extern void func();
+extern void func(void);
 
 int main(void)
 {
diff --git a/Tests/RunCMake/try_run/main.f90 b/Tests/RunCMake/try_run/main.f90
new file mode 100644
index 0000000..29b933e
--- /dev/null
+++ b/Tests/RunCMake/try_run/main.f90
@@ -0,0 +1,12 @@
+program main
+
+implicit none
+
+interface
+subroutine func() bind(C)
+end subroutine
+end interface
+
+call func()
+
+end program
diff --git a/Tests/RuntimePath/bar1.c b/Tests/RuntimePath/bar1.c
index 5dc5ec9..11b3a74 100644
--- a/Tests/RuntimePath/bar1.c
+++ b/Tests/RuntimePath/bar1.c
@@ -1,5 +1,5 @@
-extern int foo1();
-int bar1()
+extern int foo1(void);
+int bar1(void)
 {
   return foo1();
 }
diff --git a/Tests/RuntimePath/bar2.c b/Tests/RuntimePath/bar2.c
index 9035489..a117f6f 100644
--- a/Tests/RuntimePath/bar2.c
+++ b/Tests/RuntimePath/bar2.c
@@ -1,5 +1,5 @@
-extern int foo2();
-int bar2()
+extern int foo2(void);
+int bar2(void)
 {
   return foo2();
 }
diff --git a/Tests/RuntimePath/foo1.c b/Tests/RuntimePath/foo1.c
index dfcb0b7..14370ac 100644
--- a/Tests/RuntimePath/foo1.c
+++ b/Tests/RuntimePath/foo1.c
@@ -1,4 +1,4 @@
-int foo1()
+int foo1(void)
 {
   return 0;
 }
diff --git a/Tests/RuntimePath/foo2.c b/Tests/RuntimePath/foo2.c
index 12a3e77..a105df9 100644
--- a/Tests/RuntimePath/foo2.c
+++ b/Tests/RuntimePath/foo2.c
@@ -1,4 +1,4 @@
-int foo2()
+int foo2(void)
 {
   return 0;
 }
diff --git a/Tests/RuntimePath/main.c b/Tests/RuntimePath/main.c
index c71ee06..f25b493 100644
--- a/Tests/RuntimePath/main.c
+++ b/Tests/RuntimePath/main.c
@@ -1,5 +1,5 @@
-extern int bar1();
-int main()
+extern int bar1(void);
+int main(void)
 {
   return bar1();
 }
diff --git a/Tests/SetLang/bar.c b/Tests/SetLang/bar.c
index 515e8c2..3a299dc 100644
--- a/Tests/SetLang/bar.c
+++ b/Tests/SetLang/bar.c
@@ -20,7 +20,7 @@
   int i;
 };
 
-int main()
+int main(void)
 {
   A a;
   if (a.i == 21) {
diff --git a/Tests/Simple/simpleCLib.c b/Tests/Simple/simpleCLib.c
index 6509865..90c4440 100644
--- a/Tests/Simple/simpleCLib.c
+++ b/Tests/Simple/simpleCLib.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int FooBar()
+int FooBar(void)
 {
   int class;
   int private = 10;
diff --git a/Tests/SimpleCOnly/bar.c b/Tests/SimpleCOnly/bar.c
index 570fee9..6c7605a 100644
--- a/Tests/SimpleCOnly/bar.c
+++ b/Tests/SimpleCOnly/bar.c
@@ -1,4 +1,4 @@
-int bar()
+int bar(void)
 {
   return 5;
 }
diff --git a/Tests/SimpleCOnly/foo.c b/Tests/SimpleCOnly/foo.c
index c61d212..c12b865 100644
--- a/Tests/SimpleCOnly/foo.c
+++ b/Tests/SimpleCOnly/foo.c
@@ -1,4 +1,4 @@
-int foo()
+int foo(void)
 {
   return 12;
 }
diff --git a/Tests/SimpleCOnly/main.c b/Tests/SimpleCOnly/main.c
index 54a7312..ad4c75c 100644
--- a/Tests/SimpleCOnly/main.c
+++ b/Tests/SimpleCOnly/main.c
@@ -3,7 +3,7 @@
 extern int foo();
 extern int bar();
 
-int main()
+int main(void)
 {
   int i = foo();
   int k = bar();
diff --git a/Tests/SourceFileIncludeDirProperty/main.c b/Tests/SourceFileIncludeDirProperty/main.c
index 36144ca..d32e2ad 100644
--- a/Tests/SourceFileIncludeDirProperty/main.c
+++ b/Tests/SourceFileIncludeDirProperty/main.c
@@ -1,7 +1,7 @@
 
 #include "header.h"
 
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/SourceFileProperty/ICaseTest.c b/Tests/SourceFileProperty/ICaseTest.c
index 454c721..70ae53e 100644
--- a/Tests/SourceFileProperty/ICaseTest.c
+++ b/Tests/SourceFileProperty/ICaseTest.c
@@ -1,6 +1,6 @@
 
 #ifdef NEEDED_TO_WORK
-int icasetest()
+int icasetest(void)
 {
   return 0;
 }
diff --git a/Tests/SourceGroups/main.c b/Tests/SourceGroups/main.c
index f646b49..85a6c8b 100644
--- a/Tests/SourceGroups/main.c
+++ b/Tests/SourceGroups/main.c
@@ -14,7 +14,7 @@
 extern int tree_baz(void);
 extern int nested(void);
 
-int main()
+int main(void)
 {
   printf("foo: %d bar: %d foobar: %d barbar: %d baz: %d\n", foo(), bar(),
          foobar(), barbar(), baz());
diff --git a/Tests/SourceGroups/sub2/main.c b/Tests/SourceGroups/sub2/main.c
index 4cd8ae0..6a195bc 100644
--- a/Tests/SourceGroups/sub2/main.c
+++ b/Tests/SourceGroups/sub2/main.c
@@ -3,7 +3,7 @@
 extern int qax(void);
 extern int qux(void);
 
-int main()
+int main(void)
 {
   printf("qux: %d qax: %d\n", qux(), qax());
 
diff --git a/Tests/SubDir/AnotherSubdir/pair+int.int.c b/Tests/SubDir/AnotherSubdir/pair+int.int.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDir/AnotherSubdir/pair+int.int.c
+++ b/Tests/SubDir/AnotherSubdir/pair+int.int.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDir/AnotherSubdir/pair_int.int.c b/Tests/SubDir/AnotherSubdir/pair_int.int.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDir/AnotherSubdir/pair_int.int.c
+++ b/Tests/SubDir/AnotherSubdir/pair_int.int.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDir/AnotherSubdir/secondone.c b/Tests/SubDir/AnotherSubdir/secondone.c
index 3e9e5af..0dd9c6b 100644
--- a/Tests/SubDir/AnotherSubdir/secondone.c
+++ b/Tests/SubDir/AnotherSubdir/secondone.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void secondone()
+void secondone(void)
 {
   printf("Hello again\n");
 }
diff --git a/Tests/SubDir/AnotherSubdir/testfromsubdir.c b/Tests/SubDir/AnotherSubdir/testfromsubdir.c
index 34b6e7a..5e72140 100644
--- a/Tests/SubDir/AnotherSubdir/testfromsubdir.c
+++ b/Tests/SubDir/AnotherSubdir/testfromsubdir.c
@@ -4,7 +4,7 @@
 void pair_stuff();
 void vcl_stuff();
 
-int main()
+int main(void)
 {
   printf("Hello from subdirectory\n");
   secondone();
diff --git a/Tests/SubDir/ThirdSubDir/pair+int.int1.c b/Tests/SubDir/ThirdSubDir/pair+int.int1.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDir/ThirdSubDir/pair+int.int1.c
+++ b/Tests/SubDir/ThirdSubDir/pair+int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDir/ThirdSubDir/pair_int.int1.c b/Tests/SubDir/ThirdSubDir/pair_int.int1.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDir/ThirdSubDir/pair_int.int1.c
+++ b/Tests/SubDir/ThirdSubDir/pair_int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c b/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c
index 95a66ee..3cf9442 100644
--- a/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c
+++ b/Tests/SubDir/ThirdSubDir/pair_p_int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_p_stuff()
+void pair_p_stuff(void)
 {
   printf("Placeholder for another strange file in subdirectory\n");
 }
diff --git a/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c b/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c
index d162084..448ef52 100644
--- a/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c
+++ b/Tests/SubDir/ThirdSubDir/testfromauxsubdir.c
@@ -5,7 +5,7 @@
 void pair_p_stuff();
 void vcl_stuff();
 
-int main()
+int main(void)
 {
   printf("Hello from subdirectory\n");
   secondone();
diff --git a/Tests/SubDir/ThirdSubDir/thirdone.c b/Tests/SubDir/ThirdSubDir/thirdone.c
index 3e9e5af..0dd9c6b 100644
--- a/Tests/SubDir/ThirdSubDir/thirdone.c
+++ b/Tests/SubDir/ThirdSubDir/thirdone.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void secondone()
+void secondone(void)
 {
   printf("Hello again\n");
 }
diff --git a/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c b/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c
index a0c60f7..689c827 100644
--- a/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c
+++ b/Tests/SubDir/vcl_algorithm+vcl_pair+double.foo.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void vcl_stuff()
+void vcl_stuff(void)
 {
   printf("Placeholder for a file with strange name\n");
 }
diff --git a/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c b/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c
index a0c60f7..689c827 100644
--- a/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c
+++ b/Tests/SubDir/vcl_algorithm_vcl_pair_double.foo.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void vcl_stuff()
+void vcl_stuff(void)
 {
   printf("Placeholder for a file with strange name\n");
 }
diff --git a/Tests/SubDirSpaces/Another Subdir/pair+int.int.c b/Tests/SubDirSpaces/Another Subdir/pair+int.int.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDirSpaces/Another Subdir/pair+int.int.c
+++ b/Tests/SubDirSpaces/Another Subdir/pair+int.int.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDirSpaces/Another Subdir/pair_int.int.c b/Tests/SubDirSpaces/Another Subdir/pair_int.int.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDirSpaces/Another Subdir/pair_int.int.c
+++ b/Tests/SubDirSpaces/Another Subdir/pair_int.int.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDirSpaces/Another Subdir/secondone.c b/Tests/SubDirSpaces/Another Subdir/secondone.c
index 3e9e5af..0dd9c6b 100644
--- a/Tests/SubDirSpaces/Another Subdir/secondone.c
+++ b/Tests/SubDirSpaces/Another Subdir/secondone.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void secondone()
+void secondone(void)
 {
   printf("Hello again\n");
 }
diff --git a/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c b/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c
index 34b6e7a..5e72140 100644
--- a/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c
+++ b/Tests/SubDirSpaces/Another Subdir/testfromsubdir.c
@@ -4,7 +4,7 @@
 void pair_stuff();
 void vcl_stuff();
 
-int main()
+int main(void)
 {
   printf("Hello from subdirectory\n");
   secondone();
diff --git "a/Tests/SubDirSpaces/Some\050x86\051 Sources/test.c" "b/Tests/SubDirSpaces/Some\050x86\051 Sources/test.c"
index 66568d4..5baf240 100644
--- "a/Tests/SubDirSpaces/Some\050x86\051 Sources/test.c"
+++ "b/Tests/SubDirSpaces/Some\050x86\051 Sources/test.c"
@@ -1,3 +1,3 @@
-void testOdd()
+void testOdd(void)
 {
 }
diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c
+++ b/Tests/SubDirSpaces/ThirdSubDir/pair+int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c
index b7a6237..6e719fe 100644
--- a/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c
+++ b/Tests/SubDirSpaces/ThirdSubDir/pair_int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_stuff()
+void pair_stuff(void)
 {
   printf("Placeholder for a strange file in subdirectory\n");
 }
diff --git a/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c b/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c
index 95a66ee..3cf9442 100644
--- a/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c
+++ b/Tests/SubDirSpaces/ThirdSubDir/pair_p_int.int1.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void pair_p_stuff()
+void pair_p_stuff(void)
 {
   printf("Placeholder for another strange file in subdirectory\n");
 }
diff --git a/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c b/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c
index fa6c33c..2bff654 100644
--- a/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c
+++ b/Tests/SubDirSpaces/ThirdSubDir/testfromauxsubdir.c
@@ -7,7 +7,7 @@
 #ifdef CMAKE_PAREN
 void testOdd();
 #endif
-int main()
+int main(void)
 {
   printf("Hello from subdirectory\n");
   secondone();
diff --git a/Tests/SubDirSpaces/ThirdSubDir/thirdone.c b/Tests/SubDirSpaces/ThirdSubDir/thirdone.c
index 3e9e5af..0dd9c6b 100644
--- a/Tests/SubDirSpaces/ThirdSubDir/thirdone.c
+++ b/Tests/SubDirSpaces/ThirdSubDir/thirdone.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void secondone()
+void secondone(void)
 {
   printf("Hello again\n");
 }
diff --git a/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c b/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c
index a0c60f7..689c827 100644
--- a/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c
+++ b/Tests/SubDirSpaces/vcl_algorithm+vcl_pair+double.foo.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void vcl_stuff()
+void vcl_stuff(void)
 {
   printf("Placeholder for a file with strange name\n");
 }
diff --git a/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c b/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c
index a0c60f7..689c827 100644
--- a/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c
+++ b/Tests/SubDirSpaces/vcl_algorithm_vcl_pair_double.foo.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-void vcl_stuff()
+void vcl_stuff(void)
 {
   printf("Placeholder for a file with strange name\n");
 }
diff --git a/Tests/SwiftMix/CMakeLists.txt b/Tests/SwiftMix/CMakeLists.txt
index e8b6521..a4bb19b 100644
--- a/Tests/SwiftMix/CMakeLists.txt
+++ b/Tests/SwiftMix/CMakeLists.txt
@@ -1,6 +1,9 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.15)
 project(SwiftMix C Swift)
 
+# Swift on Windows only provides a release runtime.
+set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
+
 add_executable(SwiftMix CMain.c ObjCMain.m SwiftMain.swift ObjC-Swift.h)
 set_property(TARGET SwiftMix PROPERTY XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "ObjC-Swift.h")
 target_compile_options(SwiftMix PRIVATE "$<$<COMPILE_LANGUAGE:C>:-Werror=objc-method-access>")
diff --git a/Tests/SwiftMixLib/CMakeLists.txt b/Tests/SwiftMixLib/CMakeLists.txt
index a52fc94..d23c6ba 100644
--- a/Tests/SwiftMixLib/CMakeLists.txt
+++ b/Tests/SwiftMixLib/CMakeLists.txt
@@ -1,6 +1,9 @@
 cmake_minimum_required(VERSION 3.24)
 project(SwiftMixLib C CXX Swift)
 
+# Swift on Windows only provides a release runtime.
+set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
+
 add_library(SwiftMixedLib lib.c lib.cpp lib.swift)
 add_executable(Swifty main.swift)
 target_link_libraries(Swifty PUBLIC SwiftMixedLib)
diff --git a/Tests/SwiftMixPCH/CMain.c b/Tests/SwiftMixPCH/CMain.c
new file mode 100644
index 0000000..4cd78e6
--- /dev/null
+++ b/Tests/SwiftMixPCH/CMain.c
@@ -0,0 +1,9 @@
+#ifndef PCH_VALUE
+#  error "PCH_VALUE not defined"
+#endif
+
+int main(void)
+{
+  const int value = PCH_VALUE;
+  return value == 42 ? 0 : 1;
+}
diff --git a/Tests/SwiftMixPCH/CMakeLists.txt b/Tests/SwiftMixPCH/CMakeLists.txt
new file mode 100644
index 0000000..d5704f1
--- /dev/null
+++ b/Tests/SwiftMixPCH/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.15)
+if(POLICY CMP0157)
+  cmake_policy(SET CMP0157 NEW)
+endif()
+
+project(SwiftMixPCH C Swift)
+
+# Swift on Windows only provides a release runtime.
+set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
+
+add_executable(SwiftMixPCH CMain.c SwiftFunc.swift)
+target_precompile_headers(SwiftMixPCH PRIVATE pch.h)
+
+# We don't want swift to emit main()
+target_compile_options(SwiftMixPCH PRIVATE "$<$<COMPILE_LANGUAGE:Swift>:-parse-as-library>")
diff --git a/Tests/SwiftMixPCH/SwiftFunc.swift b/Tests/SwiftMixPCH/SwiftFunc.swift
new file mode 100644
index 0000000..d49c36f
--- /dev/null
+++ b/Tests/SwiftMixPCH/SwiftFunc.swift
@@ -0,0 +1,4 @@
+
+func SwiftFunc() -> Int32 {
+    return 0;
+}
diff --git a/Tests/SwiftMixPCH/pch.h b/Tests/SwiftMixPCH/pch.h
new file mode 100644
index 0000000..6de0669
--- /dev/null
+++ b/Tests/SwiftMixPCH/pch.h
@@ -0,0 +1 @@
+#define PCH_VALUE 42
diff --git a/Tests/SwiftOnly/CMakeLists.txt b/Tests/SwiftOnly/CMakeLists.txt
index 13cf2b1..3d2fe73 100644
--- a/Tests/SwiftOnly/CMakeLists.txt
+++ b/Tests/SwiftOnly/CMakeLists.txt
@@ -1,7 +1,10 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
 if(POLICY CMP0126)
   cmake_policy(SET CMP0126 NEW)
 endif()
+if(POLICY CMP0157)
+  cmake_policy(SET CMP0157 NEW)
+endif()
 
 # NOTE: Force the Release mode configuration as there are some issues with the
 # debug information handling on macOS on certain Xcode builds.
@@ -40,6 +43,13 @@
 target_link_libraries(N PUBLIC
   M)
 
+if(NOT XCODE_VERSION OR XCODE_VERSION VERSION_GREATER_EQUAL 9.0)
+  # TODO: Add a wholemodule object-library test once that is working
+  add_library(O OBJECT O.swift L.swift)
+  target_link_libraries(N PUBLIC O)
+  set_target_properties(O PROPERTIES Swift_COMPILATION_MODE "incremental")
+endif()
+
 # Dummy to make sure generation works with such targets.
 add_library(SwiftIface INTERFACE)
 target_link_libraries(SwiftOnly PRIVATE SwiftIface)
@@ -54,3 +64,21 @@
 if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2)
   add_subdirectory("SwiftPlugin")
 endif()
+
+function(test_cmp0157_default mode)
+  if(POLICY CMP0157)
+    cmake_policy(GET CMP0157 cmp0157_wmo)
+    if(cmp0157_wmo STREQUAL "NEW")
+      set(CMAKE_Swift_COMPILATION_MODE "${mode}")
+      add_executable(hi_${mode} main.swift)
+      get_target_property(${mode}_swift_comp_mode hi_${mode} "Swift_COMPILATION_MODE")
+      if(NOT ${mode}_swift_comp_mode STREQUAL ${mode})
+        message(SEND_ERROR "expected ${mode} -- found ${${mode}_swift_comp_mode}")
+      endif()
+    endif()
+  endif()
+endfunction()
+
+test_cmp0157_default("wholemodule")
+test_cmp0157_default("incremental")
+test_cmp0157_default("singlefile")
diff --git a/Tests/SwiftOnly/O.swift b/Tests/SwiftOnly/O.swift
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/SwiftOnly/O.swift
diff --git a/Tests/SwiftOnly/SubA/SubA.swift b/Tests/SwiftOnly/SubA/SubA.swift
index e69de29..09ddd88 100644
--- a/Tests/SwiftOnly/SubA/SubA.swift
+++ b/Tests/SwiftOnly/SubA/SubA.swift
@@ -0,0 +1 @@
+public func hi() { print("hi") }
diff --git a/Tests/SwiftOnly/SubB/SubB.swift b/Tests/SwiftOnly/SubB/SubB.swift
index d593c4c..142fdaa 100644
--- a/Tests/SwiftOnly/SubB/SubB.swift
+++ b/Tests/SwiftOnly/SubB/SubB.swift
@@ -1 +1,3 @@
 import SubA
+
+public var number = 42
diff --git a/Tests/TryCompile/Inner/innerexe.c b/Tests/TryCompile/Inner/innerexe.c
index e329c5f..6d1e3da 100644
--- a/Tests/TryCompile/Inner/innerexe.c
+++ b/Tests/TryCompile/Inner/innerexe.c
@@ -1,5 +1,5 @@
 extern int innerlib(void);
-int main()
+int main(void)
 {
   return innerlib();
 }
diff --git a/Tests/TryCompile/check_a_b.c b/Tests/TryCompile/check_a_b.c
index 05fba0f..65535ac 100644
--- a/Tests/TryCompile/check_a_b.c
+++ b/Tests/TryCompile/check_a_b.c
@@ -4,7 +4,7 @@
 #ifndef DEF_B
 #  error DEF_B not defined
 #endif
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/TryCompile/exit_success.c b/Tests/TryCompile/exit_success.c
index 82f5b5f..302d926 100644
--- a/Tests/TryCompile/exit_success.c
+++ b/Tests/TryCompile/exit_success.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   printf("hello world\n");
   return 0;
diff --git a/Tests/TryCompile/exit_with_error.c b/Tests/TryCompile/exit_with_error.c
index dbddcf5..dfa36fc 100644
--- a/Tests/TryCompile/exit_with_error.c
+++ b/Tests/TryCompile/exit_with_error.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   printf("hello world\n");
   return 1;
diff --git a/Tests/TryCompile/pass.c b/Tests/TryCompile/pass.c
index f8b643a..8488f4e 100644
--- a/Tests/TryCompile/pass.c
+++ b/Tests/TryCompile/pass.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/TryCompile/pass2a.c b/Tests/TryCompile/pass2a.c
index 27c377b..8375a8e 100644
--- a/Tests/TryCompile/pass2a.c
+++ b/Tests/TryCompile/pass2a.c
@@ -1,5 +1,5 @@
 extern int pass2b(void);
-int main()
+int main(void)
 {
   return pass2b();
 }
diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c
index 84ded1f..c25b7d9 100644
--- a/Tests/TryCompile/stdout_and_stderr.c
+++ b/Tests/TryCompile/stdout_and_stderr.c
@@ -1,6 +1,6 @@
 #include <stdio.h>
 
-int main()
+int main(void)
 {
   fputs("error\n", stderr);
   puts("hello world\n");
diff --git a/Tests/Unset/unset.c b/Tests/Unset/unset.c
index f8b643a..8488f4e 100644
--- a/Tests/Unset/unset.c
+++ b/Tests/Unset/unset.c
@@ -1,4 +1,4 @@
-int main()
+int main(void)
 {
   return 0;
 }
diff --git a/Tests/UseSWIG/CMakeLists.txt b/Tests/UseSWIG/CMakeLists.txt
index 3d80270..0971025 100644
--- a/Tests/UseSWIG/CMakeLists.txt
+++ b/Tests/UseSWIG/CMakeLists.txt
@@ -134,6 +134,7 @@
       --build-options ${build_options}
       --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
       )
+    set_property(TEST UseSWIG.BasicFortran APPEND PROPERTY LABELS "Fortran")
   endif()
 endif()
 
diff --git a/Tests/VSGNUFortran/c_code/main.c b/Tests/VSGNUFortran/c_code/main.c
index 60c1120..be868c3 100644
--- a/Tests/VSGNUFortran/c_code/main.c
+++ b/Tests/VSGNUFortran/c_code/main.c
@@ -1,6 +1,6 @@
 #include <HelloWorldFCMangle.h> /* created by FortranCInterface */
 extern void FC_hello(void);
-int main()
+int main(void)
 {
   FC_hello();
   return 0;
diff --git a/Tests/Visibility/bar.c b/Tests/Visibility/bar.c
index b72a1a5..e1f4df6 100644
--- a/Tests/Visibility/bar.c
+++ b/Tests/Visibility/bar.c
@@ -1,3 +1,3 @@
-void bar()
+void bar(void)
 {
 }
diff --git a/Tests/X11/X11.c b/Tests/X11/X11.c
index 3a6f9f0..9f002f6 100644
--- a/Tests/X11/X11.c
+++ b/Tests/X11/X11.c
@@ -4,7 +4,7 @@
 #  include <X11/Xlib.h>
 #  include <X11/Xutil.h>
 
-int main()
+int main(void)
 {
   printf("There is X on this computer\n");
   return 0;
@@ -12,7 +12,7 @@
 
 #else
 
-int main()
+int main(void)
 {
   printf("No X on this computer\n");
   return 0;
diff --git a/Tests/XCTest/FrameworkExample/FrameworkExample.c b/Tests/XCTest/FrameworkExample/FrameworkExample.c
index 77361c8..9da1bf2 100644
--- a/Tests/XCTest/FrameworkExample/FrameworkExample.c
+++ b/Tests/XCTest/FrameworkExample/FrameworkExample.c
@@ -1,6 +1,6 @@
 #include "FrameworkExample.h"
 
-int FourtyTwo()
+int FourtyTwo(void)
 {
   return 42;
 }
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index 12b5407..8bf591b 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index e45970d..6056030 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -75,6 +75,10 @@
   { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
   { include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] },
 
+  { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] },
+  { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] },
+  { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] },
+
   { include: [ "<filesystem>", public, "<cm/filesystem>", public ] },
   { include: [ "<optional>", public, "<cm/optional>", public ] },
   { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },
diff --git a/Utilities/Release/WiX/WIX.template.in b/Utilities/Release/WiX/WIX.template.in
index 8abf9d8..1f8953b 100644
--- a/Utilities/Release/WiX/WIX.template.in
+++ b/Utilities/Release/WiX/WIX.template.in
@@ -52,6 +52,7 @@
         </FeatureRef>
 
         <UIRef Id="$(var.CPACK_WIX_UI_REF)" />
+        <UIRef Id="WixUI_ErrorProgressText" />
 
         <?include "properties.wxi"?>
         <?include "product_fragment.wxi"?>
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index a944daf..09bb65b 100755
--- a/Utilities/Scripts/update-curl.bash
+++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@
 readonly ownership="Curl Upstream <curl-library@lists.haxx.se>"
 readonly subtree="Utilities/cmcurl"
 readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-8_4_0"
+readonly tag="curl-8_6_0"
 readonly shortlog=false
 readonly paths="
   CMake/*
@@ -35,15 +35,6 @@
     git_archive
     pushd "${extractdir}/${name}-reduced"
     rm lib/config-*.h
-    chmod a-x lib/connect.c
-    for f in \
-      lib/cookie.c \
-      lib/krb5.c \
-      lib/security.c \
-      ; do
-        iconv -f LATIN1 -t UTF8 $f -o $f.utf-8
-        mv $f.utf-8 $f
-    done
     echo "* -whitespace" > .gitattributes
     popd
 }
diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash
index 5a4f11a..724303e 100755
--- a/Utilities/Scripts/update-libarchive.bash
+++ b/Utilities/Scripts/update-libarchive.bash
@@ -8,7 +8,7 @@
 readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>"
 readonly subtree="Utilities/cmlibarchive"
 readonly repo="https://github.com/libarchive/libarchive.git"
-readonly tag="v3.6.2"
+readonly tag="v3.7.2"
 readonly shortlog=false
 readonly paths="
   CMakeLists.txt
diff --git a/Utilities/Scripts/update-librhash.bash b/Utilities/Scripts/update-librhash.bash
index ea7e655..b3d078b 100755
--- a/Utilities/Scripts/update-librhash.bash
+++ b/Utilities/Scripts/update-librhash.bash
@@ -8,7 +8,7 @@
 readonly ownership="librhash upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmlibrhash"
 readonly repo="https://github.com/rhash/rhash.git"
-readonly tag="v1.3.9"
+readonly tag="v1.4.4"
 readonly shortlog=false
 readonly paths="
   COPYING
@@ -31,6 +31,7 @@
   librhash/sha512.c
   librhash/sha512.h
   librhash/ustd.h
+  librhash/util.c
   librhash/util.h
 "
 
diff --git a/Utilities/Scripts/update-zstd.bash b/Utilities/Scripts/update-zstd.bash
index 48ca5a1..7bfb3dc 100755
--- a/Utilities/Scripts/update-zstd.bash
+++ b/Utilities/Scripts/update-zstd.bash
@@ -8,7 +8,7 @@
 readonly ownership="zstd upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmzstd"
 readonly repo="https://github.com/facebook/zstd.git"
-readonly tag="v1.5.0"
+readonly tag="v1.5.5"
 readonly shortlog=false
 readonly paths="
   LICENSE
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index 694ba3c..746c872 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.28 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in
index f8651e1..20e1340 100644
--- a/Utilities/Sphinx/conf.py.in
+++ b/Utilities/Sphinx/conf.py.in
@@ -88,7 +88,11 @@
 # qthelp_namespace = "org.cmake"
 # qthelp_qch_name = "CMake.qch"
 
-linkcheck_ignore = [r'about:|https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack']
+linkcheck_ignore = [
+    r'about:',
+    r'https://gitlab\.kitware\.com/cmake/community/-/wikis/doc/cpack',
+    r'https://www.intel.com/',
+]
 
 linkcheck_allowed_redirects = {
     r'https://cdash\.org': r'https://www\.cdash\.org/',
diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake
index 8f68777..311d58e 100644
--- a/Utilities/cmThirdPartyChecks.cmake
+++ b/Utilities/cmThirdPartyChecks.cmake
@@ -43,8 +43,8 @@
   set(HAVE_CHROOT 0)
   set(HAVE_COPYFILE_H 0)
   set(HAVE_CRYPTO_H 0)
-  set(HAVE__CTIME64_S 1)
   set(HAVE_CTIME_R 0)
+  set(HAVE_CTIME_S 1)
   set(HAVE_CYGWIN_CONV_PATH 0)
   set(HAVE_DES_H 0)
   set(HAVE_DIRECT_H 1)
@@ -64,6 +64,8 @@
   set(HAVE_FCNTL_H 1)
   set(HAVE_FCNTL_O_NONBLOCK 0)
   set(HAVE_FDOPENDIR 0)
+  set(HAVE_FNMATCH 0)
+  set(HAVE_FNMATCH_H 0)
   set(HAVE_FORK 0)
   set(HAVE_FREEADDRINFO 1)
   set(HAVE_FREEIFADDRS 0)
@@ -82,6 +84,7 @@
   set(HAVE_GETGRGID_R 0)
   set(HAVE_GETGRNAM_R 0)
   set(HAVE_GETHOSTBYNAME 1)
+  set(HAVE_GETLINE 0)
   set(HAVE_GETPAGESIZE 0)
   set(HAVE_GETPEERNAME 1)
   set(HAVE_GETPID 1)
@@ -94,8 +97,8 @@
   set(HAVE_GETSOCKNAME 1)
   set(HAVE_GETVFSBYNAME 0)
   set(HAVE_GLIBC_STRERROR_R 0)
-  set(HAVE__GMTIME64_S 1)
   set(HAVE_GMTIME_R 0)
+  set(HAVE_GMTIME_S 1)
   set(HAVE_GRP_H 0)
   set(HAVE_IDN2_H 0)
   set(HAVE_IFADDRS_H 0)
@@ -126,8 +129,8 @@
   set(HAVE_LINUX_FS_H 0)
   set(HAVE_LINUX_MAGIC_H 0)
   set(HAVE_LINUX_TYPES_H 0)
-  set(HAVE__LOCALTIME64_S 1)
   set(HAVE_LOCALTIME_R 0)
+  set(HAVE_LOCALTIME_S 0)
   set(HAVE_LSTAT 0)
   set(HAVE_LUTIMES 0)
   set(HAVE_MACH_ABSOLUTE_TIME 0)
@@ -136,7 +139,7 @@
   set(HAVE_MEMORY_H 1)
   set(HAVE_MKDIR 1)
   set(HAVE_MKFIFO 0)
-  set(HAVE__MKGMTIME64 1)
+  set(HAVE__MKGMTIME 1)
   set(HAVE_MKNOD 0)
   set(HAVE_MMAP 0)
   set(HAVE_MSG_NOSIGNAL 0)
@@ -216,6 +219,7 @@
   set(HAVE_SYS_MKDEV_H 0)
   set(HAVE_SYS_MOUNT_H 0)
   set(HAVE_SYS_POLL_H 0)
+  set(HAVE_SYS_QUEUE_H 0)
   set(HAVE_SYS_RESOURCE_H 0)
   set(HAVE_SYS_RICHACL_H 0)
   set(HAVE_SYS_SELECT_H 0)
diff --git a/Utilities/cmcppdap/include/dap/any.h b/Utilities/cmcppdap/include/dap/any.h
index b05f03d..6060546 100644
--- a/Utilities/cmcppdap/include/dap/any.h
+++ b/Utilities/cmcppdap/include/dap/any.h
@@ -159,7 +159,7 @@
 
 template <typename T>
 T& any::get() const {
-  static_assert(!std::is_same<T, std::nullptr_t>(),
+  static_assert(!std::is_same<T, std::nullptr_t>::value,
                 "Cannot get nullptr from 'any'.");
   assert(is<T>());
   return *reinterpret_cast<T*>(value);
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index ea80ec8..83d743d 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -23,7 +23,6 @@
  ***************************************************************************/
 
 #ifdef HAVE_FCNTL_O_NONBLOCK
-
 /* headers for FCNTL_O_NONBLOCK test */
 #include <sys/types.h>
 #include <unistd.h>
@@ -45,14 +44,13 @@
 #error "O_NONBLOCK does not work on this platform"
 #endif
 
-int
-main ()
+int main(void)
 {
-      /* O_NONBLOCK source test */
-      int flags = 0;
-      if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK))
-          return 1;
-      return 0;
+  /* O_NONBLOCK source test */
+  int flags = 0;
+  if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK))
+    return 1;
+  return 0;
 }
 #endif
 
@@ -108,36 +106,16 @@
 }
 #endif
 
-#ifdef HAVE_SOCKLEN_T
-#ifdef _WIN32
-#include <ws2tcpip.h>
-#else
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-int
-main ()
-{
-if ((socklen_t *) 0)
-  return 0;
-if (sizeof (socklen_t))
-  return 0;
-  ;
-  return 0;
-}
-#endif
 #ifdef HAVE_IN_ADDR_T
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
-
-int
-main ()
+int main(void)
 {
-if ((in_addr_t *) 0)
-  return 0;
-if (sizeof (in_addr_t))
-  return 0;
+  if((in_addr_t *) 0)
+    return 0;
+  if(sizeof(in_addr_t))
+    return 0;
   ;
   return 0;
 }
@@ -150,11 +128,10 @@
 #ifdef HAVE_STDBOOL_H
 #include <stdbool.h>
 #endif
-int
-main ()
+int main(void)
 {
-if (sizeof (bool *) )
-  return 0;
+  if(sizeof(bool *))
+    return 0;
   ;
   return 0;
 }
@@ -165,8 +142,9 @@
 #include <stdarg.h>
 #include <string.h>
 #include <float.h>
-int main() { return 0; }
+int main(void) { return 0; }
 #endif
+
 #ifdef HAVE_FILE_OFFSET_BITS
 #ifdef _FILE_OFFSET_BITS
 #undef _FILE_OFFSET_BITS
@@ -181,104 +159,83 @@
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                        && LARGE_OFF_T % 2147483647 == 1)
                       ? 1 : -1];
-int main () { ; return 0; }
+int main(void) { ; return 0; }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET
 /* includes start */
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#  endif
+#  include <winsock2.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* ioctlsocket source code */
- int socket;
- unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
-
+  /* ioctlsocket source code */
+  int socket;
+  unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
   ;
   return 0;
 }
 
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_CAMEL
 /* includes start */
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#  endif
+#  include <winsock2.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* IoctlSocket source code */
-    if(0 != IoctlSocket(0, 0, 0))
-      return 1;
+  /* IoctlSocket source code */
+  if(0 != IoctlSocket(0, 0, 0))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO
 /* includes start */
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#  endif
+#  include <winsock2.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* IoctlSocket source code */
-        long flags = 0;
-        if(0 != IoctlSocket(0, FIONBIO, &flags))
-          return 1;
+  /* IoctlSocket source code */
+  long flags = 0;
+  if(0 != IoctlSocket(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_FIONBIO
 /* includes start */
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#  endif
+#  include <winsock2.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-        int flags = 0;
-        if(0 != ioctlsocket(0, FIONBIO, &flags))
-          return 1;
-
+  int flags = 0;
+  if(0 != ioctlsocket(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTL_FIONBIO
 /* headers for FIONBIO test */
 /* includes start */
@@ -297,19 +254,16 @@
 #ifdef HAVE_STROPTS_H
 #  include <stropts.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-        int flags = 0;
-        if(0 != ioctl(0, FIONBIO, &flags))
-          return 1;
-
+  int flags = 0;
+  if(0 != ioctl(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTL_SIOCGIFADDR
 /* headers for FIONBIO test */
 /* includes start */
@@ -329,28 +283,23 @@
 #  include <stropts.h>
 #endif
 #include <net/if.h>
-
-int
-main ()
+int main(void)
 {
-        struct ifreq ifr;
-        if(0 != ioctl(0, SIOCGIFADDR, &ifr))
-          return 1;
-
+  struct ifreq ifr;
+  if(0 != ioctl(0, SIOCGIFADDR, &ifr))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK
 /* includes start */
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#  endif
+#  include <winsock2.h>
 #endif
 /* includes start */
 #ifdef HAVE_SYS_TYPES_H
@@ -360,30 +309,30 @@
 #  include <sys/socket.h>
 #endif
 /* includes end */
-
-int
-main ()
+int main(void)
 {
-        if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
-          return 1;
+  if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_GLIBC_STRERROR_R
 #include <string.h>
 #include <errno.h>
 
 void check(char c) {}
 
-int
-main () {
+int main(void)
+{
   char buffer[1024];
   /* This will not compile if strerror_r does not return a char* */
   check(strerror_r(EACCES, buffer, sizeof(buffer))[0]);
   return 0;
 }
 #endif
+
 #ifdef HAVE_POSIX_STRERROR_R
 #include <string.h>
 #include <errno.h>
@@ -391,92 +340,51 @@
 /* float, because a pointer can't be implicitly cast to float */
 void check(float f) {}
 
-int
-main () {
+int main(void)
+{
   char buffer[1024];
   /* This will not compile if strerror_r does not return an int */
   check(strerror_r(EACCES, buffer, sizeof(buffer)));
   return 0;
 }
 #endif
+
 #ifdef HAVE_FSETXATTR_6
 #include <sys/xattr.h> /* header from libc, not from libattr */
-int
-main() {
+int main(void)
+{
   fsetxattr(0, 0, 0, 0, 0, 0);
   return 0;
 }
 #endif
+
 #ifdef HAVE_FSETXATTR_5
 #include <sys/xattr.h> /* header from libc, not from libattr */
-int
-main() {
+int main(void)
+{
   fsetxattr(0, 0, 0, 0, 0);
   return 0;
 }
 #endif
+
 #ifdef HAVE_CLOCK_GETTIME_MONOTONIC
 #include <time.h>
-int
-main() {
+int main(void)
+{
   struct timespec ts = {0, 0};
   clock_gettime(CLOCK_MONOTONIC, &ts);
   return 0;
 }
 #endif
+
 #ifdef HAVE_BUILTIN_AVAILABLE
-int
-main() {
+int main(void)
+{
   if(__builtin_available(macOS 10.12, *)) {}
   return 0;
 }
 #endif
-#ifdef HAVE_VARIADIC_MACROS_C99
-#define c99_vmacro3(first, ...) fun3(first, __VA_ARGS__)
-#define c99_vmacro2(first, ...) fun2(first, __VA_ARGS__)
 
-int fun3(int arg1, int arg2, int arg3);
-int fun2(int arg1, int arg2);
-
-int fun3(int arg1, int arg2, int arg3) {
-  return arg1 + arg2 + arg3;
-}
-int fun2(int arg1, int arg2) {
-  return arg1 + arg2;
-}
-
-int
-main() {
-  int res3 = c99_vmacro3(1, 2, 3);
-  int res2 = c99_vmacro2(1, 2);
-  (void)res3;
-  (void)res2;
-  return 0;
-}
-#endif
-#ifdef HAVE_VARIADIC_MACROS_GCC
-#define gcc_vmacro3(first, args...) fun3(first, args)
-#define gcc_vmacro2(first, args...) fun2(first, args)
-
-int fun3(int arg1, int arg2, int arg3);
-int fun2(int arg1, int arg2);
-
-int fun3(int arg1, int arg2, int arg3) {
-  return arg1 + arg2 + arg3;
-}
-int fun2(int arg1, int arg2) {
-  return arg1 + arg2;
-}
-
-int
-main() {
-  int res3 = gcc_vmacro3(1, 2, 3);
-  int res2 = gcc_vmacro2(1, 2);
-  (void)res3;
-  (void)res2;
-  return 0;
-}
-#endif
 #ifdef HAVE_ATOMIC
 /* includes start */
 #ifdef HAVE_SYS_TYPES_H
@@ -490,17 +398,24 @@
 #endif
 /* includes end */
 
-int
-main() {
+int main(void)
+{
   _Atomic int i = 1;
   i = 0;  /* Force an atomic-write operation. */
   return i;
 }
 #endif
+
 #ifdef HAVE_WIN32_WINNT
 /* includes start */
-#ifdef WIN32
-#  include "../lib/setup-win32.h"
+#ifdef _WIN32
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#  ifndef NOGDI
+#    define NOGDI
+#  endif
+#  include <windows.h>
 #endif
 /* includes end */
 
@@ -508,8 +423,8 @@
 #define expand(x) enquote(x)
 #pragma message("_WIN32_WINNT=" expand(_WIN32_WINNT))
 
-int
-main() {
+int main(void)
+{
   return 0;
 }
 #endif
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake
index 973e6ad..0ea9e0c 100644
--- a/Utilities/cmcurl/CMake/FindZstd.cmake
+++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -56,11 +56,18 @@
     ${PC_Zstd_LIBRARY_DIRS}
 )
 
+if(Zstd_INCLUDE_DIR)
+  file(READ "${Zstd_INCLUDE_DIR}/zstd.h" _zstd_header)
+  string(REGEX MATCH ".*define ZSTD_VERSION_MAJOR *([0-9]+).*define ZSTD_VERSION_MINOR *([0-9]+).*define ZSTD_VERSION_RELEASE *([0-9]+)" _zstd_ver "${_zstd_header}")
+  set(Zstd_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+endif()
+
 include(FindPackageHandleStandardArgs)
 find_package_handle_standard_args(Zstd
   REQUIRED_VARS
     Zstd_LIBRARY
     Zstd_INCLUDE_DIR
+  VERSION_VAR Zstd_VERSION
 )
 
 if(Zstd_FOUND)
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake
index e12bf30..9ff62ea 100644
--- a/Utilities/cmcurl/CMake/Macros.cmake
+++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -23,19 +23,6 @@
 ###########################################################################
 #File defines convenience macros for available feature testing
 
-# This macro checks if the symbol exists in the library and if it
-# does, it prepends library to the list.  It is intended to be called
-# multiple times with a sequence of possibly dependent libraries in
-# order of least-to-most-dependent.  Some libraries depend on others
-# to link correctly.
-macro(check_library_exists_concat LIBRARY SYMBOL VARIABLE)
-  check_library_exists("${LIBRARY};${CURL_LIBS}" ${SYMBOL} "${CMAKE_LIBRARY_PATH}"
-    ${VARIABLE})
-  if(${VARIABLE})
-    set(CURL_LIBS ${LIBRARY} ${CURL_LIBS})
-  endif()
-endmacro()
-
 # Check if header file exists and add it to the list.
 # This macro is intended to be called multiple times with a sequence of
 # possibly dependent header files.  Some headers depend on others to be
@@ -58,7 +45,7 @@
         "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
     endif()
 
-    message(STATUS "Performing Curl Test ${CURL_TEST}")
+    message(STATUS "Performing Test ${CURL_TEST}")
     try_compile(${CURL_TEST}
       ${CMAKE_BINARY_DIR}
       ${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c
@@ -67,15 +54,15 @@
       OUTPUT_VARIABLE OUTPUT)
     if(${CURL_TEST})
       set(${CURL_TEST} 1 CACHE INTERNAL "Curl test ${FUNCTION}")
-      message(STATUS "Performing Curl Test ${CURL_TEST} - Success")
+      message(STATUS "Performing Test ${CURL_TEST} - Success")
       file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
-        "Performing Curl Test ${CURL_TEST} passed with the following output:\n"
+        "Performing Test ${CURL_TEST} passed with the following output:\n"
         "${OUTPUT}\n")
     else()
-      message(STATUS "Performing Curl Test ${CURL_TEST} - Failed")
+      message(STATUS "Performing Test ${CURL_TEST} - Failed")
       set(${CURL_TEST} "" CACHE INTERNAL "Curl test ${FUNCTION}")
       file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-        "Performing Curl Test ${CURL_TEST} failed with the following output:\n"
+        "Performing Test ${CURL_TEST} failed with the following output:\n"
         "${OUTPUT}\n")
     endif()
   endif()
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index d67a905..7701c0e 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -23,115 +23,89 @@
 ###########################################################################
 include(CheckCSourceCompiles)
 include(CheckCSourceRuns)
-
-# The begin of the sources (macros and includes)
-set(_source_epilogue "#undef inline")
+include(CheckTypeSize)
 
 macro(add_header_include check header)
   if(${check})
-    set(_source_epilogue "${_source_epilogue}\n#include <${header}>")
+    set(_source_epilogue "${_source_epilogue}
+      #include <${header}>")
   endif()
 endmacro()
 
-set(signature_call_conv)
-if(HAVE_WINDOWS_H)
-  add_header_include(HAVE_WINSOCK2_H "winsock2.h")
-  add_header_include(HAVE_WINDOWS_H "windows.h")
-  set(_source_epilogue
-      "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif")
-  set(signature_call_conv "PASCAL")
-  if(WIN32)
-    set(CMAKE_REQUIRED_LIBRARIES ws2_32)
-  endif()
-else()
-  add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
-  add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
-endif()
-
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
 
-check_c_source_compiles("${_source_epilogue}
-  int main(void) {
-    int flag = MSG_NOSIGNAL;
-    (void)flag;
-    return 0;
-  }" HAVE_MSG_NOSIGNAL)
-
-if(NOT HAVE_WINDOWS_H)
-  add_header_include(HAVE_SYS_TIME_H "sys/time.h")
-endif()
-check_c_source_compiles("${_source_epilogue}
-#include <time.h>
-int main(void) {
-  struct timeval ts;
-  ts.tv_sec  = 0;
-  ts.tv_usec = 0;
-  (void)ts;
-  return 0;
-}" HAVE_STRUCT_TIMEVAL)
-
-if(HAVE_WINDOWS_H)
-  set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h)
-else()
+if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE)
   set(CMAKE_EXTRA_INCLUDE_FILES)
-  if(HAVE_SYS_SOCKET_H)
-    set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
+  if(WIN32)
+    set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
+    set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN")
+    set(CMAKE_REQUIRED_LIBRARIES "ws2_32")
+  elseif(HAVE_SYS_SOCKET_H)
+    set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   endif()
+  check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
+  set(HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE})
 endif()
 
-check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
-if(HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE)
-  set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
+if(NOT WIN32)
+  set(_source_epilogue "#undef inline")
+  add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
+  add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
+  check_c_source_compiles("${_source_epilogue}
+    int main(void)
+    {
+      int flag = MSG_NOSIGNAL;
+      (void)flag;
+      return 0;
+    }" HAVE_MSG_NOSIGNAL)
 endif()
 
+set(_source_epilogue "#undef inline")
+add_header_include(HAVE_SYS_TIME_H "sys/time.h")
+check_c_source_compiles("${_source_epilogue}
+  #include <time.h>
+  int main(void)
+  {
+    struct timeval ts;
+    ts.tv_sec  = 0;
+    ts.tv_usec = 0;
+    (void)ts;
+    return 0;
+  }" HAVE_STRUCT_TIMEVAL)
+
 unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
 
-if(NOT CMAKE_CROSSCOMPILING)
-  if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "iOS")
-    # only try this on non-apple platforms
+if(NOT CMAKE_CROSSCOMPILING AND NOT APPLE)
+  set(_source_epilogue "#undef inline")
+  add_header_include(HAVE_SYS_POLL_H "sys/poll.h")
+  add_header_include(HAVE_POLL_H "poll.h")
+  check_c_source_runs("${_source_epilogue}
+    #include <stdlib.h>
+    #include <sys/time.h>
+    int main(void)
+    {
+      if(0 != poll(0, 0, 10)) {
+        return 1; /* fail */
+      }
+      else {
+        /* detect the 10.12 poll() breakage */
+        struct timeval before, after;
+        int rc;
+        size_t us;
 
-    # if not cross-compilation...
-    set(CMAKE_REQUIRED_FLAGS "")
-    if(HAVE_SYS_POLL_H)
-      set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H")
-    elseif(HAVE_POLL_H)
-      set(CMAKE_REQUIRED_FLAGS "-DHAVE_POLL_H")
-    endif()
-    check_c_source_runs("
-      #include <stdlib.h>
-      #include <sys/time.h>
+        gettimeofday(&before, NULL);
+        rc = poll(NULL, 0, 500);
+        gettimeofday(&after, NULL);
 
-      #ifdef HAVE_SYS_POLL_H
-      #  include <sys/poll.h>
-      #elif  HAVE_POLL_H
-      #  include <poll.h>
-      #endif
+        us = (after.tv_sec - before.tv_sec) * 1000000 +
+          (after.tv_usec - before.tv_usec);
 
-      int main(void)
-      {
-          if(0 != poll(0, 0, 10)) {
-            return 1; /* fail */
-          }
-          else {
-            /* detect the 10.12 poll() breakage */
-            struct timeval before, after;
-            int rc;
-            size_t us;
-
-            gettimeofday(&before, NULL);
-            rc = poll(NULL, 0, 500);
-            gettimeofday(&after, NULL);
-
-            us = (after.tv_sec - before.tv_sec) * 1000000 +
-              (after.tv_usec - before.tv_usec);
-
-            if(us < 400000) {
-              return 1;
-            }
-          }
-          return 0;
+        if(us < 400000) {
+          return 1;
+        }
+      }
+      return 0;
     }" HAVE_POLL_FINE)
-  endif()
 endif()
 
 # Detect HAVE_GETADDRINFO_THREADSAFE
@@ -140,8 +114,8 @@
   set(HAVE_GETADDRINFO_THREADSAFE ${HAVE_GETADDRINFO})
 elseif(NOT HAVE_GETADDRINFO)
   set(HAVE_GETADDRINFO_THREADSAFE FALSE)
-elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
-       CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
+elseif(APPLE OR
+       CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
        CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
        CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR
        CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
@@ -153,14 +127,10 @@
 endif()
 
 if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
-
-  set(_save_epilogue "${_source_epilogue}")
   set(_source_epilogue "#undef inline")
-
   add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
   add_header_include(HAVE_SYS_TIME_H "sys/time.h")
   add_header_include(HAVE_NETDB_H "netdb.h")
-
   check_c_source_compiles("${_source_epilogue}
     int main(void)
     {
@@ -172,7 +142,7 @@
     }" HAVE_H_ERRNO)
 
   if(NOT HAVE_H_ERRNO)
-    check_c_source_runs("${_source_epilogue}
+    check_c_source_compiles("${_source_epilogue}
       int main(void)
       {
         h_errno = 2;
@@ -197,17 +167,12 @@
   if(HAVE_H_ERRNO OR HAVE_H_ERRNO_ASSIGNABLE OR HAVE_H_ERRNO_SBS_ISSUE_7)
     set(HAVE_GETADDRINFO_THREADSAFE TRUE)
   endif()
-
-  set(_source_epilogue "${_save_epilogue}")
 endif()
 
-if(NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
-  set(_save_epilogue "${_source_epilogue}")
+if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
   set(_source_epilogue "#undef inline")
-
   add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
   add_header_include(HAVE_SYS_TIME_H "sys/time.h")
-
   check_c_source_compiles("${_source_epilogue}
     #include <time.h>
     int main(void)
@@ -216,6 +181,4 @@
       (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
       return 0;
     }" HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
-
-  set(_source_epilogue "${_save_epilogue}")
 endif()
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake
index 1310cb4..d82bbb1 100644
--- a/Utilities/cmcurl/CMake/PickyWarnings.cmake
+++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -23,6 +23,12 @@
 ###########################################################################
 include(CheckCCompilerFlag)
 
+unset(WPICKY)
+
+if(CURL_WERROR AND CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0)
+  set(WPICKY "${WPICKY} -pedantic-errors")
+endif()
+
 if(PICKY_COMPILER)
   if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
 
@@ -52,8 +58,8 @@
     # Assume these options always exist with both clang and gcc.
     # Require clang 3.0 / gcc 2.95 or later.
     list(APPEND WPICKY_ENABLE
-      -Wbad-function-cast                  # clang  3.0  gcc  2.95
-      -Wconversion                         # clang  3.0  gcc  2.95
+      -Wbad-function-cast                  # clang  2.7  gcc  2.95
+      -Wconversion                         # clang  2.7  gcc  2.95
       -Winline                             # clang  1.0  gcc  1.0
       -Wmissing-declarations               # clang  1.0  gcc  2.7
       -Wmissing-prototypes                 # clang  1.0  gcc  1.0
@@ -70,23 +76,38 @@
 
     # Always enable with clang, version dependent with gcc
     set(WPICKY_COMMON_OLD
+      -Waddress                            # clang  2.7  gcc  4.3
+      -Wattributes                         # clang  2.7  gcc  4.1
       -Wcast-align                         # clang  1.0  gcc  4.2
       -Wdeclaration-after-statement        # clang  1.0  gcc  3.4
-      -Wempty-body                         # clang  3.0  gcc  4.3
+      -Wdiv-by-zero                        # clang  2.7  gcc  4.1
+      -Wempty-body                         # clang  2.7  gcc  4.3
       -Wendif-labels                       # clang  1.0  gcc  3.3
       -Wfloat-equal                        # clang  1.0  gcc  2.96 (3.0)
-      -Wignored-qualifiers                 # clang  3.0  gcc  4.3
+      -Wformat-security                    # clang  2.7  gcc  4.1
+      -Wignored-qualifiers                 # clang  2.8  gcc  4.3
+      -Wmissing-field-initializers         # clang  2.7  gcc  4.1
+      -Wmissing-noreturn                   # clang  2.7  gcc  4.1
       -Wno-format-nonliteral               # clang  1.0  gcc  2.96 (3.0)
-      -Wno-sign-conversion                 # clang  3.0  gcc  4.3
       -Wno-system-headers                  # clang  1.0  gcc  3.0
+    # -Wpadded                             # clang  2.9  gcc  4.1               # Not used because we cannot change public structs
+      -Wold-style-definition               # clang  2.7  gcc  3.4
+      -Wredundant-decls                    # clang  2.7  gcc  4.1
+      -Wsign-conversion                    # clang  2.9  gcc  4.3
+        -Wno-error=sign-conversion                                              # FIXME
       -Wstrict-prototypes                  # clang  1.0  gcc  3.3
-      -Wtype-limits                        # clang  3.0  gcc  4.3
+    # -Wswitch-enum                        # clang  2.7  gcc  4.1               # Not used because this basically disallows default case
+      -Wtype-limits                        # clang  2.7  gcc  4.3
+      -Wunreachable-code                   # clang  2.7  gcc  4.1
+    # -Wunused-macros                      # clang  2.7  gcc  4.1               # Not practical
+      -Wunused-parameter                   # clang  2.7  gcc  4.1
       -Wvla                                # clang  2.8  gcc  4.3
     )
 
     set(WPICKY_COMMON
       -Wdouble-promotion                   # clang  3.6  gcc  4.6  appleclang  6.3
       -Wenum-conversion                    # clang  3.2  gcc 10.0  appleclang  4.6  g++ 11.0
+      -Wpragmas                            # clang  3.5  gcc  4.1  appleclang  6.0
       -Wunused-const-variable              # clang  3.4  gcc  6.0  appleclang  5.1
     )
 
@@ -95,12 +116,17 @@
         ${WPICKY_COMMON_OLD}
         -Wshift-sign-overflow              # clang  2.9
         -Wshorten-64-to-32                 # clang  1.0
+        -Wlanguage-extension-token         # clang  3.0
+        -Wformat=2                         # clang  3.0  gcc  4.8
       )
       # Enable based on compiler version
       if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR
          (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3))
         list(APPEND WPICKY_ENABLE
           ${WPICKY_COMMON}
+          -Wunreachable-code-break         # clang  3.5            appleclang  6.0
+          -Wheader-guard                   # clang  3.4            appleclang  5.1
+          -Wsometimes-uninitialized        # clang  3.2            appleclang  4.6
         )
       endif()
       if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR
@@ -117,6 +143,12 @@
           -Wextra-semi-stmt                # clang  7.0            appleclang 10.3
         )
       endif()
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.4))
+        list(APPEND WPICKY_ENABLE
+          -Wimplicit-fallthrough           # clang  4.0  gcc  7.0  appleclang 12.4  # we have silencing markup for clang 10.0 and above only
+        )
+      endif()
     else()  # gcc
       list(APPEND WPICKY_DETECT
         ${WPICKY_COMMON}
@@ -125,9 +157,11 @@
       if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3)
         list(APPEND WPICKY_ENABLE
           ${WPICKY_COMMON_OLD}
+          -Wclobbered                      #             gcc  4.3
           -Wmissing-parameter-type         #             gcc  4.3
           -Wold-style-declaration          #             gcc  4.3
           -Wstrict-aliasing=3              #             gcc  4.0
+          -Wtrampolines                    #             gcc  4.3
         )
       endif()
       if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW)
@@ -137,7 +171,7 @@
       endif()
       if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)
         list(APPEND WPICKY_ENABLE
-          -Wformat=2                       # clang  3.0  gcc  4.8 (clang part-default, enabling it fully causes -Wformat-nonliteral warnings)
+          -Wformat=2                       # clang  3.0  gcc  4.8
         )
       endif()
       if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0)
@@ -159,7 +193,8 @@
           -Walloc-zero                     #             gcc  7.0
           -Wduplicated-branches            #             gcc  7.0
           -Wformat-overflow=2              #             gcc  7.0
-          -Wformat-truncation=1            #             gcc  7.0
+          -Wformat-truncation=2            #             gcc  7.0
+          -Wimplicit-fallthrough           # clang  4.0  gcc  7.0
           -Wrestrict                       #             gcc  7.0
         )
       endif()
@@ -172,13 +207,11 @@
 
     #
 
-    unset(WPICKY)
-
-    foreach(_CCOPT ${WPICKY_ENABLE})
+    foreach(_CCOPT IN LISTS WPICKY_ENABLE)
       set(WPICKY "${WPICKY} ${_CCOPT}")
     endforeach()
 
-    foreach(_CCOPT ${WPICKY_DETECT})
+    foreach(_CCOPT IN LISTS WPICKY_DETECT)
       # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new
       # test result in.
       string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname)
@@ -190,8 +223,10 @@
         set(WPICKY "${WPICKY} ${_CCOPT}")
       endif()
     endforeach()
-
-    message(STATUS "Picky compiler options:${WPICKY}")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WPICKY}")
   endif()
 endif()
+
+if(WPICKY)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WPICKY}")
+  message(STATUS "Picky compiler options:${WPICKY}")
+endif()
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
index 5daec0e..d3391d9 100644
--- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
+++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -21,113 +21,168 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
-if(NOT UNIX)
-  if(WIN32)
+if(NOT WIN32)
+  message(FATAL_ERROR "This file should be included on Windows platform only")
+endif()
 
-    set(HAVE_WINDOWS_H 1)
-    set(HAVE_WS2TCPIP_H 1)
-    set(HAVE_WINSOCK2_H 1)
+set(HAVE_LOCALE_H 1)
 
-    if(MINGW)
-      set(HAVE_SNPRINTF 1)
-      set(HAVE_UNISTD_H 1)
-      set(HAVE_INTTYPES_H 1)
+if(MINGW)
+  set(HAVE_SNPRINTF 1)
+  set(HAVE_UNISTD_H 1)
+  set(HAVE_LIBGEN_H 1)
+  set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
+  set(HAVE_STDBOOL_H 1)
+  set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
+  set(HAVE_STRTOLL 1)
+  set(HAVE_BASENAME 1)
+  set(HAVE_STRCASECMP 1)
+  set(HAVE_FTRUNCATE 1)
+  set(HAVE_SYS_PARAM_H 1)
+  set(HAVE_SYS_TIME_H 1)
+  set(HAVE_GETTIMEOFDAY 1)
+else()
+  set(HAVE_LIBGEN_H 0)
+  set(HAVE_STRCASECMP 0)
+  set(HAVE_FTRUNCATE 0)
+  set(HAVE_SYS_PARAM_H 0)
+  set(HAVE_SYS_TIME_H 0)
+  set(HAVE_GETTIMEOFDAY 0)
+  if(MSVC)
+    set(HAVE_UNISTD_H 0)
+    set(HAVE_LOCALE_H 1)
+    set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
+    set(HAVE_STDATOMIC_H 0)
+    if(NOT MSVC_VERSION LESS 1800)
+      set(HAVE_STDBOOL_H 1)
       set(HAVE_STRTOLL 1)
-      set(HAVE_BASENAME 1)
-    elseif(MSVC)
-      if(NOT MSVC_VERSION LESS 1800)
-        set(HAVE_INTTYPES_H 1)
-        set(HAVE_STRTOLL 1)
-      else()
-        set(HAVE_INTTYPES_H 0)
-        set(HAVE_STRTOLL 0)
-      endif()
-      if(NOT MSVC_VERSION LESS 1900)
-        set(HAVE_SNPRINTF 1)
-      else()
-        set(HAVE_SNPRINTF 0)
-      endif()
-      set(HAVE_BASENAME 0)
+    else()
+      set(HAVE_STDBOOL_H 0)
+      set(HAVE_STRTOLL 0)
     endif()
-
-    set(HAVE_LIBSOCKET 0)
-    set(HAVE_GETHOSTNAME 1)
-    set(HAVE_LIBZ 0)
-
-    set(HAVE_ARC4RANDOM 0)
-    set(HAVE_FNMATCH 0)
-    set(HAVE_SCHED_YIELD 0)
-    set(HAVE_ARPA_INET_H 0)
-    set(HAVE_FCNTL_H 1)
-    set(HAVE_IFADDRS_H 0)
-    set(HAVE_IO_H 1)
-    set(HAVE_NETDB_H 0)
-    set(HAVE_NETINET_IN_H 0)
-    set(HAVE_NETINET_TCP_H 0)
-    set(HAVE_NETINET_UDP_H 0)
-    set(HAVE_NET_IF_H 0)
-    set(HAVE_IOCTL_SIOCGIFADDR 0)
-    set(HAVE_POLL_H 0)
-    set(HAVE_POLL_FINE 0)
-    set(HAVE_PWD_H 0)
-    set(HAVE_STRINGS_H 0)
-    set(HAVE_SYS_FILIO_H 0)
-    set(HAVE_SYS_WAIT_H 0)
-    set(HAVE_SYS_IOCTL_H 0)
-    set(HAVE_SYS_PARAM_H 0)
-    set(HAVE_SYS_POLL_H 0)
-    set(HAVE_SYS_RESOURCE_H 0)
-    set(HAVE_SYS_SELECT_H 0)
-    set(HAVE_SYS_SOCKET_H 0)
-    set(HAVE_SYS_SOCKIO_H 0)
-    set(HAVE_SYS_STAT_H 1)
-    set(HAVE_SYS_TIME_H 0)
-    set(HAVE_SYS_TYPES_H 1)
-    set(HAVE_SYS_UN_H 0)
-    set(HAVE_SYS_UTIME_H 1)
-    set(HAVE_TERMIOS_H 0)
-    set(HAVE_TERMIO_H 0)
-    set(HAVE_UTIME_H 0)
-
-    set(HAVE_FSEEKO 0)
-    set(HAVE__FSEEKI64 1)
-    set(HAVE_SOCKET 1)
-    set(HAVE_SELECT 1)
-    set(HAVE_STRDUP 1)
-    set(HAVE_STRICMP 1)
-    set(HAVE_STRCMPI 1)
-    set(HAVE_MEMRCHR 0)
-    set(HAVE_GETTIMEOFDAY 0)
-    set(HAVE_CLOSESOCKET 1)
-    set(HAVE_SIGSETJMP 0)
-    set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
-    set(HAVE_GETPASS_R 0)
-    set(HAVE_GETPWUID 0)
-    set(HAVE_GETEUID 0)
-    set(HAVE_UTIME 1)
-    set(HAVE_GMTIME_R 0)
-    set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 0)
-    set(HAVE_GETHOSTBYNAME_R 0)
-    set(HAVE_SIGNAL 1)
-    set(HAVE_LINUX_TCP_H 0)
-    set(HAVE_GLIBC_STRERROR_R 0)
-    set(HAVE_MACH_ABSOLUTE_TIME 0)
-    set(HAVE_GETIFADDRS 0)
-
-    set(HAVE_GETHOSTBYNAME_R_3 0)
-    set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
-    set(HAVE_GETHOSTBYNAME_R_5 0)
-    set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
-    set(HAVE_GETHOSTBYNAME_R_6 0)
-    set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
-
-    set(HAVE_O_NONBLOCK 0)
-    set(HAVE_IN_ADDR_T 0)
-    set(STDC_HEADERS 1)
-
-    set(HAVE_SIGACTION 0)
-    set(HAVE_MACRO_SIGSETJMP 0)
-  else()
-    message("This file should be included on Windows platform only")
+    set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
+    if(NOT MSVC_VERSION LESS 1900)
+      set(HAVE_SNPRINTF 1)
+    else()
+      set(HAVE_SNPRINTF 0)
+    endif()
+    set(HAVE_BASENAME 0)
+    set(HAVE_STRTOK_R 0)
+    set(HAVE_FILE_OFFSET_BITS 0)
+    set(HAVE_ATOMIC 0)
   endif()
 endif()
+
+# Available in Windows XP and newer
+set(HAVE_GETADDRINFO 1)
+set(HAVE_FREEADDRINFO 1)
+
+set(HAVE_FCHMOD 0)
+set(HAVE_SOCKETPAIR 0)
+set(HAVE_SENDMSG 0)
+set(HAVE_ALARM 0)
+set(HAVE_FCNTL 0)
+set(HAVE_GETPPID 0)
+set(HAVE_UTIMES 0)
+set(HAVE_GETPWUID_R 0)
+set(HAVE_STRERROR_R 0)
+set(HAVE_SIGINTERRUPT 0)
+set(HAVE_PIPE 0)
+set(HAVE_IF_NAMETOINDEX 0)
+set(HAVE_GETRLIMIT 0)
+set(HAVE_SETRLIMIT 0)
+set(HAVE_FSETXATTR 0)
+set(HAVE_LIBSOCKET 0)
+set(HAVE_SETLOCALE 1)
+set(HAVE_SETMODE 1)
+set(HAVE_GETPEERNAME 1)
+set(HAVE_GETSOCKNAME 1)
+set(HAVE_GETHOSTNAME 1)
+set(HAVE_LIBZ 0)
+
+set(HAVE_RECV 1)
+set(HAVE_SEND 1)
+set(HAVE_STROPTS_H 0)
+set(HAVE_SYS_XATTR_H 0)
+set(HAVE_ARC4RANDOM 0)
+set(HAVE_FNMATCH 0)
+set(HAVE_SCHED_YIELD 0)
+set(HAVE_ARPA_INET_H 0)
+set(HAVE_FCNTL_H 1)
+set(HAVE_IFADDRS_H 0)
+set(HAVE_IO_H 1)
+set(HAVE_NETDB_H 0)
+set(HAVE_NETINET_IN_H 0)
+set(HAVE_NETINET_TCP_H 0)
+set(HAVE_NETINET_UDP_H 0)
+set(HAVE_NET_IF_H 0)
+set(HAVE_IOCTL_SIOCGIFADDR 0)
+set(HAVE_POLL_H 0)
+set(HAVE_POLL_FINE 0)
+set(HAVE_PWD_H 0)
+set(HAVE_STRINGS_H 0)  # mingw-w64 has it (wrapper to string.h)
+set(HAVE_SYS_FILIO_H 0)
+set(HAVE_SYS_WAIT_H 0)
+set(HAVE_SYS_IOCTL_H 0)
+set(HAVE_SYS_POLL_H 0)
+set(HAVE_SYS_RESOURCE_H 0)
+set(HAVE_SYS_SELECT_H 0)
+set(HAVE_SYS_SOCKET_H 0)
+set(HAVE_SYS_SOCKIO_H 0)
+set(HAVE_SYS_STAT_H 1)
+set(HAVE_SYS_TYPES_H 1)
+set(HAVE_SYS_UN_H 0)
+set(HAVE_SYS_UTIME_H 1)
+set(HAVE_TERMIOS_H 0)
+set(HAVE_TERMIO_H 0)
+set(HAVE_UTIME_H 0)  # mingw-w64 has it (wrapper to sys/utime.h)
+
+set(HAVE_FSEEKO 0)
+set(HAVE__FSEEKI64 1)
+set(HAVE_SOCKET 1)
+set(HAVE_SELECT 1)
+set(HAVE_STRDUP 1)
+set(HAVE_STRICMP 1)
+set(HAVE_STRCMPI 1)
+set(HAVE_MEMRCHR 0)
+set(HAVE_CLOSESOCKET 1)
+set(HAVE_SIGSETJMP 0)
+set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
+set(HAVE_GETPASS_R 0)
+set(HAVE_GETPWUID 0)
+set(HAVE_GETEUID 0)
+set(HAVE_UTIME 1)
+set(HAVE_GMTIME_R 0)
+set(HAVE_GETHOSTBYNAME_R 0)
+set(HAVE_SIGNAL 1)
+set(HAVE_SIGACTION 0)
+set(HAVE_LINUX_TCP_H 0)
+set(HAVE_GLIBC_STRERROR_R 0)
+set(HAVE_MACH_ABSOLUTE_TIME 0)
+set(HAVE_GETIFADDRS 0)
+set(HAVE_FCNTL_O_NONBLOCK 0)
+set(HAVE_IOCTLSOCKET 1)
+set(HAVE_IOCTLSOCKET_CAMEL 0)
+set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0)
+set(HAVE_IOCTLSOCKET_FIONBIO 1)
+set(HAVE_IOCTL_FIONBIO 0)
+set(HAVE_SETSOCKOPT_SO_NONBLOCK 0)
+set(HAVE_POSIX_STRERROR_R 0)
+set(HAVE_BUILTIN_AVAILABLE 0)
+set(HAVE_MSG_NOSIGNAL 0)
+set(HAVE_STRUCT_TIMEVAL 1)
+set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
+
+set(HAVE_GETHOSTBYNAME_R_3 0)
+set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_5 0)
+set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_6 0)
+set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+
+set(HAVE_O_NONBLOCK 0)
+set(HAVE_IN_ADDR_T 0)
+set(STDC_HEADERS 1)
+
+set(HAVE_SIZEOF_SUSECONDS_T 0)
+set(HAVE_SIZEOF_SA_FAMILY_T 0)
diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in
index 056907c..9adb96e 100644
--- a/Utilities/cmcurl/CMake/curl-config.cmake.in
+++ b/Utilities/cmcurl/CMake/curl-config.cmake.in
@@ -35,4 +35,6 @@
 check_required_components("@PROJECT_NAME@")
 
 # Alias for either shared or static library
-add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
+if(NOT TARGET @PROJECT_NAME@::libcurl)
+  add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
+endif()
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 9387247..3dd24f1 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -22,6 +22,7 @@
 set(CURL_DISABLE_AWS OFF)
 set(CURL_DISABLE_BASIC_AUTH OFF)
 set(CURL_DISABLE_BEARER_AUTH OFF)
+set(CURL_DISABLE_BINDLOCAL OFF)
 set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support")
 set(CURL_DISABLE_DICT ON CACHE INTERNAL "Disable curl dict protocol?")
 set(CURL_DISABLE_DIGEST_AUTH OFF)
@@ -31,10 +32,12 @@
 set(CURL_DISABLE_FTP OFF CACHE INTERNAL "Disable curl ftp protocol?")
 set(CURL_DISABLE_GETOPTIONS OFF)
 set(CURL_DISABLE_GOPHER ON CACHE INTERNAL "Disable curl gopher protocol?")
+set(CURL_DISABLE_HEADERS_API OFF)
 set(CURL_DISABLE_HSTS OFF)
 set(CURL_DISABLE_HTTP_AUTH OFF)
 set(CURL_DISABLE_HTTP OFF CACHE INTERNAL "Disable curl http protocol?")
 set(CURL_DISABLE_IMAP ON CACHE INTERNAL "Disable curl imap protocol?")
+set(CURL_DISABLE_INSTALL ON)
 set(CURL_DISABLE_KERBEROS_AUTH OFF)
 set(CURL_DISABLE_LDAP ON CACHE INTERNAL "Disable curl ldap protocol?")
 set(CURL_DISABLE_LDAPS ON CACHE INTERNAL "Disable curl ldaps protocol?")
@@ -176,25 +179,8 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
-# curl/libcurl CMake script
 # by Tetetest and Sukender (Benoit Neil)
 
-# TODO:
-# The output .so file lacks the soname number which we currently have within the lib/Makefile.am file
-# Add full (4 or 5 libs) SSL support
-# Add INSTALL target (EXTRA_DIST variables in Makefile.am may be moved to Makefile.inc so that CMake/CPack is aware of what's to include).
-# Check on all possible platforms
-# Test with as many configurations possible (With or without any option)
-# Create scripts that help keeping the CMake build system up to date (to reduce maintenance). According to Tetetest:
-#  - lists of headers that 'configure' checks for;
-#  - curl-specific tests (the ones that are in m4/curl-*.m4 files);
-#  - (most obvious thing:) curl version numbers.
-# Add documentation subproject
-#
-# To check:
-# (From Daniel Stenberg) The cmake build selected to run gcc with -fPIC on my box while the plain configure script did not.
-# (From Daniel Stenberg) The gcc command line use neither -g nor any -O options. As a developer, I also treasure our configure scripts's --enable-debug option that sets a long range of "picky" compiler options.
-
 # Note: By default this CMake build script detects the version of some
 # dependencies using `check_symbol_exists`.  Those checks do not work
 # in the case that both CURL and its dependency are included as
@@ -209,7 +195,6 @@
 #   HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
 #   HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
 #   HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
-#   HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd
 #
 # For each of the above variables, if the variable is DEFINED (either
 # to ON or OFF), the symbol detection will be skipped.  If the
@@ -260,6 +245,8 @@
 option(BUILD_STATIC_LIBS "Build static libraries" OFF)
 option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF)
 option(ENABLE_ARES "Set to ON to enable c-ares support" OFF)
+option(CURL_DISABLE_INSTALL "Set to ON to disable installation targets" OFF)
+
 if(WIN32)
   option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF)
   option(ENABLE_UNICODE "Set to ON to use the Unicode version of the Windows API functions" OFF)
@@ -385,6 +372,8 @@
 mark_as_advanced(CURL_DISABLE_GETOPTIONS)
 option(CURL_DISABLE_GOPHER "disables Gopher" OFF)
 mark_as_advanced(CURL_DISABLE_GOPHER)
+option(CURL_DISABLE_HEADERS_API "disables headers-api support" OFF)
+mark_as_advanced(CURL_DISABLE_HEADERS_API)
 option(CURL_DISABLE_HSTS "disables HSTS support" OFF)
 mark_as_advanced(CURL_DISABLE_HSTS)
 option(CURL_DISABLE_HTTP "disables HTTP" OFF)
@@ -402,6 +391,8 @@
 option(CURL_DISABLE_MIME "disables MIME support" OFF)
 mark_as_advanced(CURL_DISABLE_MIME)
 option(CURL_DISABLE_MQTT "disables MQTT" OFF)
+mark_as_advanced(CURL_DISABLE_BINDLOCAL)
+option(CURL_DISABLE_BINDLOCAL "disables local binding support" OFF)
 mark_as_advanced(CURL_DISABLE_MQTT)
 option(CURL_DISABLE_NETRC "disables netrc parser" OFF)
 mark_as_advanced(CURL_DISABLE_NETRC)
@@ -481,18 +472,22 @@
 endif()
 
 if(0) # This code not needed for building within CMake.
-if(USE_MANUAL)
-    #nroff is currently only used when USE_MANUAL is set, so we can prevent the warning of no *NROFF if USE_MANUAL is OFF (or not defined), by not even looking for NROFF..
-    curl_nroff_check()
-endif()
 find_package(Perl)
 
-cmake_dependent_option(ENABLE_MANUAL "to provide the built-in manual"
-    ON "NROFF_USEFUL;PERL_FOUND"
-    OFF)
+option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON)
+# curl source release tarballs come with the curl man page pre-built.
+option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" OFF)
 
-if(ENABLE_MANUAL)
-  set(USE_MANUAL ON)
+if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
+  if(PERL_FOUND)
+    curl_nroff_check()
+    if(NROFF_USEFUL)
+      set(HAVE_MANUAL_TOOLS ON)
+    endif()
+  endif()
+  if(NOT HAVE_MANUAL_TOOLS)
+    message(WARNING "Perl not found, or nroff not useful. Will not build manuals.")
+  endif()
 endif()
 endif()
 
@@ -529,28 +524,30 @@
 
 # On windows preload settings
 if(WIN32)
-  list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=)
   include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake)
 endif()
 
 if(ENABLE_THREADED_RESOLVER)
-  find_package(Threads REQUIRED)
   if(WIN32)
     set(USE_THREADS_WIN32 ON)
   else()
+    find_package(Threads REQUIRED)
     set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT})
     set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT})
+    set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
   endif()
-  set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
 endif()
 
 # Check for all needed libraries
-check_library_exists_concat("socket" connect      HAVE_LIBSOCKET)
+check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
+if(HAVE_LIBSOCKET)
+  set(CURL_LIBS "socket;${CURL_LIBS}")
+endif()
 
 check_function_exists(gethostname HAVE_GETHOSTNAME)
 
 if(WIN32)
-  list(APPEND CURL_LIBS "ws2_32")
+  list(APPEND CURL_LIBS "ws2_32" "bcrypt")
   if(USE_LIBRTMP)
     list(APPEND CURL_LIBS "winmm")
   endif()
@@ -581,7 +578,7 @@
 if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL)
   set(openssl_default OFF)
 endif()
-cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${openssl_default} CURL_ENABLE_SSL OFF)
 option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF)
 endif()
 
@@ -653,11 +650,6 @@
   list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES})
   include_directories(${OPENSSL_INCLUDE_DIR})
 
-  if(WIN32)
-    list(APPEND CURL_LIBS "ws2_32")
-    list(APPEND CURL_LIBS "bcrypt")  # for OpenSSL/LibreSSL
-  endif()
-
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl")
     set(valid_default_ssl_backend TRUE)
   endif()
@@ -783,17 +775,12 @@
 set(HAVE_ZSTD OFF)
 if(CURL_ZSTD)
   find_package(Zstd REQUIRED)
-  if(NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
-    cmake_push_check_state()
-    set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS})
-    set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES})
-    check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM)
-    cmake_pop_check_state()
-  endif()
-  if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM)
+  if(Zstd_FOUND AND NOT Zstd_VERSION VERSION_LESS "1.0.0")
     set(HAVE_ZSTD ON)
     list(APPEND CURL_LIBS ${Zstd_LIBRARIES})
     include_directories(${Zstd_INCLUDE_DIRS})
+  else()
+    message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.")
   endif()
 endif()
 
@@ -826,6 +813,20 @@
   cmake_pop_check_state()
 endmacro()
 
+# Ensure that the OpenSSL fork actually supports QUIC.
+macro(openssl_check_quic)
+  if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
+    if(USE_OPENSSL)
+      openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+    elseif(USE_WOLFSSL)
+      openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+    endif()
+  endif()
+  if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
+    message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR")
+  endif()
+endmacro()
+
 if(USE_OPENSSL OR USE_WOLFSSL)
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
     openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO)
@@ -852,18 +853,7 @@
     else()
       find_package(NGTCP2 REQUIRED quictls)
     endif()
-
-    # Be sure that the OpenSSL/wolfSSL library actually supports QUIC.
-    if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
-      if(USE_OPENSSL)
-        openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-      elseif(USE_WOLFSSL)
-        openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-      endif()
-    endif()
-    if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
-      message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
-    endif()
+    openssl_check_quic()
   elseif(USE_GNUTLS)
     find_package(NGTCP2 REQUIRED GnuTLS)
   else()
@@ -885,7 +875,10 @@
     message(FATAL_ERROR "Only one HTTP/3 backend can be selected!")
   endif()
   find_package(QUICHE REQUIRED)
-  CheckQuicSupportInOpenSSL()
+  if(NOT HAVE_BORINGSSL)
+    message(FATAL_ERROR "quiche requires BoringSSL")
+  endif()
+  openssl_check_quic()
   set(USE_QUICHE ON)
   include_directories(${QUICHE_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${QUICHE_LIBRARIES})
@@ -908,6 +901,10 @@
   list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
 endif()
 
+if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3))
+  message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
+endif()
+
 if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
   set(USE_TLS_SRP 1)
 endif()
@@ -930,8 +927,12 @@
   if(NOT USE_WIN32_LDAP)
     # Check for LDAP
     set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
-    check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP)
-    check_library_exists_concat(${CMAKE_LBER_LIB} ber_init HAVE_LIBLBER)
+    check_library_exists("${CMAKE_LDAP_LIB}" "ldap_init" "" HAVE_LIBLDAP)
+    if(HAVE_LIBLDAP)
+      check_library_exists("${CMAKE_LDAP_LIB};${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER)
+    else()
+      check_library_exists("${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER)
+    endif()
 
     set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES})
     set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory")
@@ -955,7 +956,7 @@
       endif()
       set(NEED_LBER_H ON)
       set(_HEADER_LIST)
-      if(HAVE_WINDOWS_H)
+      if(WIN32)
         list(APPEND _HEADER_LIST "windows.h")
       endif()
       if(HAVE_SYS_TYPES_H)
@@ -970,8 +971,10 @@
 
       list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
       list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
+      set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}")
       if(HAVE_LIBLBER)
         list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
+        set(CURL_LIBS "${CMAKE_LBER_LIB};${CURL_LIBS}")
       endif()
 
       check_c_source_compiles("
@@ -1018,7 +1021,11 @@
 # Check for idn2
 option(USE_LIBIDN2 "Use libidn2 for IDN support" ON)
 if(USE_LIBIDN2)
-  check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2)
+  check_library_exists("idn2" "idn2_lookup_ul" "" HAVE_LIBIDN2)
+  if(HAVE_LIBIDN2)
+    set(CURL_LIBS "idn2;${CURL_LIBS}")
+    check_include_file_concat("idn2.h" HAVE_IDN2_H)
+  endif()
 else()
   set(HAVE_LIBIDN2 OFF)
 endif()
@@ -1089,10 +1096,8 @@
     check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H)
     check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H)
 
-    if(GSS_FLAVOUR STREQUAL "Heimdal")
-      set(HAVE_GSSHEIMDAL ON)
-    else() # MIT
-      set(HAVE_GSSMIT ON)
+    if(NOT GSS_FLAVOUR STREQUAL "Heimdal")
+      # MIT
       set(_INCLUDE_LIST "")
       if(HAVE_GSSAPI_GSSAPI_H)
         list(APPEND _INCLUDE_LIST "gssapi/gssapi.h")
@@ -1233,17 +1238,46 @@
 endif()
 
 # Check for header files
-if(NOT UNIX)
-  check_include_file_concat("windows.h"      HAVE_WINDOWS_H)
-  check_include_file_concat("ws2tcpip.h"     HAVE_WS2TCPIP_H)
-  check_include_file_concat("winsock2.h"     HAVE_WINSOCK2_H)
-else()
-  set(HAVE_WINDOWS_H 0)
-  set(HAVE_WS2TCPIP_H 0)
-  set(HAVE_WINSOCK2_H 0)
+if(WIN32)
+  set(CURL_INCLUDES ${CURL_INCLUDES} "winsock2.h")
+  set(CURL_INCLUDES ${CURL_INCLUDES} "ws2tcpip.h")
+  set(CURL_INCLUDES ${CURL_INCLUDES} "windows.h")
 endif()
 
-check_include_file_concat("inttypes.h"       HAVE_INTTYPES_H)
+if(WIN32)
+  # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT
+  curl_internal_test(HAVE_WIN32_WINNT)
+  if(HAVE_WIN32_WINNT)
+    string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}")
+    string(REGEX REPLACE ".*_WIN32_WINNT=" "" OUTPUT "${OUTPUT}")
+    string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" OUTPUT "${OUTPUT}")  # pad to 4 digits
+    string(TOLOWER "${OUTPUT}" HAVE_WIN32_WINNT)
+    message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}")
+  endif()
+  # avoid storing HAVE_WIN32_WINNT in CMake cache
+  unset(HAVE_WIN32_WINNT CACHE)
+
+  if(HAVE_WIN32_WINNT)
+    if(HAVE_WIN32_WINNT STRLESS "0x0501")
+      # Windows XP is required for freeaddrinfo, getaddrinfo
+      message(FATAL_ERROR "Building for Windows XP or newer is required.")
+    endif()
+
+    # pre-fill detection results based on target OS version
+    if(MINGW OR MSVC)
+      if(HAVE_WIN32_WINNT STRLESS "0x0600")
+        set(HAVE_INET_NTOP 0)
+        set(HAVE_INET_PTON 0)
+      else()  # Windows Vista or newer
+        set(HAVE_INET_NTOP 1)
+        set(HAVE_INET_PTON 1)
+      endif()
+      unset(HAVE_INET_NTOP CACHE)
+      unset(HAVE_INET_PTON CACHE)
+    endif()
+  endif()
+endif()
+
 check_include_file_concat("sys/filio.h"      HAVE_SYS_FILIO_H)
 check_include_file_concat("sys/wait.h"       HAVE_SYS_WAIT_H)
 check_include_file_concat("sys/ioctl.h"      HAVE_SYS_IOCTL_H)
@@ -1261,7 +1295,6 @@
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
-check_include_file_concat("idn2.h"           HAVE_IDN2_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
 check_include_file_concat("libgen.h"         HAVE_LIBGEN_H)
@@ -1277,7 +1310,6 @@
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
 check_include_file_concat("stdatomic.h"      HAVE_STDATOMIC_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
-check_include_file_concat("stdint.h"         HAVE_STDINT_H)
 check_include_file_concat("strings.h"        HAVE_STRINGS_H)
 check_include_file_concat("stropts.h"        HAVE_STROPTS_H)
 check_include_file_concat("termio.h"         HAVE_TERMIO_H)
@@ -1308,7 +1340,6 @@
   set(CMAKE_REQUIRED_LIBRARIES network)
 endif()
 
-check_symbol_exists(fchmod        "${CURL_INCLUDES}" HAVE_FCHMOD)
 check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
 check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
@@ -1345,6 +1376,7 @@
 check_symbol_exists(signal         "${CURL_INCLUDES};signal.h" HAVE_SIGNAL)
 check_symbol_exists(strtoll        "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL)
 check_symbol_exists(strerror_r     "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R)
+check_symbol_exists(sigaction      "signal.h" HAVE_SIGACTION)
 check_symbol_exists(siginterrupt   "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT)
 check_symbol_exists(getaddrinfo    "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO)
 check_symbol_exists(getifaddrs     "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)
@@ -1361,6 +1393,10 @@
 check_symbol_exists(setmode        "${CURL_INCLUDES}" HAVE_SETMODE)
 check_symbol_exists(setrlimit      "${CURL_INCLUDES}" HAVE_SETRLIMIT)
 
+if(HAVE_FSEEKO)
+  set(HAVE_DECL_FSEEKO 1)
+endif()
+
 if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
   # earlier MSVC compilers had faulty snprintf implementations
   check_symbol_exists(snprintf       "stdio.h" HAVE_SNPRINTF)
@@ -1384,20 +1420,11 @@
 set(HAVE_SA_FAMILY_T            ${HAVE_SIZEOF_SA_FAMILY_T})
 set(CMAKE_EXTRA_INCLUDE_FILES   "")
 
-set(CMAKE_EXTRA_INCLUDE_FILES   "ws2def.h")
-check_type_size("ADDRESS_FAMILY"    SIZEOF_ADDRESS_FAMILY)
-set(HAVE_ADDRESS_FAMILY         ${HAVE_SIZEOF_ADDRESS_FAMILY})
-set(CMAKE_EXTRA_INCLUDE_FILES   "")
-
-# sigaction and sigsetjmp are special. Use special mechanism for
-# detecting those, but only if previous attempt failed.
-check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
-
-if(NOT HAVE_SIGSETJMP)
-  check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
-  if(HAVE_MACRO_SIGSETJMP)
-    set(HAVE_SIGSETJMP 1)
-  endif()
+if(WIN32)
+  set(CMAKE_EXTRA_INCLUDE_FILES   "winsock2.h")
+  check_type_size("ADDRESS_FAMILY"    SIZEOF_ADDRESS_FAMILY)
+  set(HAVE_ADDRESS_FAMILY         ${HAVE_SIZEOF_ADDRESS_FAMILY})
+  set(CMAKE_EXTRA_INCLUDE_FILES   "")
 endif()
 
 # Do curl specific tests
@@ -1421,8 +1448,6 @@
     HAVE_BOOL_T
     STDC_HEADERS
     HAVE_FILE_OFFSET_BITS
-    HAVE_VARIADIC_MACROS_C99
-    HAVE_VARIADIC_MACROS_GCC
     HAVE_ATOMIC
     )
   curl_internal_test(${CURL_TEST})
@@ -1442,18 +1467,6 @@
 check_type_size("curl_socket_t"  SIZEOF_CURL_SOCKET_T)
 set(CMAKE_EXTRA_INCLUDE_FILES "")
 
-if(WIN32)
-  # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT
-  curl_internal_test(HAVE_WIN32_WINNT)
-  if(HAVE_WIN32_WINNT)
-    string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}")
-    string(REGEX REPLACE ".*_WIN32_WINNT=" "" HAVE_WIN32_WINNT "${OUTPUT}")
-    message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}")
-  endif()
-  # avoid storing HAVE_WIN32_WINNT in CMake cache
-  unset(HAVE_WIN32_WINNT CACHE)
-endif()
-
 if(0) # This code not needed for building within CMake.
 if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
   # on not-Windows and not-crosscompiling, check for writable argv[]
@@ -1511,8 +1524,10 @@
   endforeach()
 endif()
 
-# Check clock_gettime(CLOCK_MONOTONIC, x) support
-curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
+if(NOT WIN32)
+  # Check clock_gettime(CLOCK_MONOTONIC, x) support
+  curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
+endif()
 
 # Check compiler support of __builtin_available()
 curl_internal_test(HAVE_BUILTIN_AVAILABLE)
@@ -1548,15 +1563,6 @@
   endif()
 endif()
 
-# TODO test which of these headers are required
-if(WIN32)
-  set(CURL_PULL_WS2TCPIP_H ${HAVE_WS2TCPIP_H})
-else()
-  set(CURL_PULL_SYS_TYPES_H ${HAVE_SYS_TYPES_H})
-  set(CURL_PULL_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H})
-  set(CURL_PULL_SYS_POLL_H ${HAVE_SYS_POLL_H})
-endif()
-
 include(CMake/OtherTests.cmake)
 
 add_definitions(-DHAVE_CONFIG_H)
@@ -1577,8 +1583,6 @@
   if(USE_WIN32_CRYPTO OR USE_SCHANNEL)
     list(APPEND CURL_LIBS "advapi32" "crypt32")
   endif()
-
-  list(APPEND CURL_LIBS "bcrypt")
 endif()
 
 if(MSVC)
@@ -1666,7 +1670,7 @@
 set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
 set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
 
-if(USE_MANUAL)
+if(HAVE_MANUAL_TOOLS)
   add_subdirectory(docs)
 endif()
 
@@ -1683,258 +1687,257 @@
   add_subdirectory(tests)
 endif()
 
-# Helper to populate a list (_items) with a label when conditions (the remaining
-# args) are satisfied
-macro(_add_if label)
-  # needs to be a macro to allow this indirection
-  if(${ARGN})
-    set(_items ${_items} "${label}")
-  endif()
-endmacro()
+if(NOT CURL_DISABLE_INSTALL)
 
-# NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
-if(NOT (CURL_DISABLE_NTLM) AND
-    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS))
-  set(use_curl_ntlm_core ON)
-endif()
-
-# Clear list and try to detect available features
-set(_items)
-_add_if("SSL"           SSL_ENABLED)
-_add_if("IPv6"          ENABLE_IPV6)
-_add_if("unixsockets"   USE_UNIX_SOCKETS)
-_add_if("libz"          HAVE_LIBZ)
-_add_if("brotli"        HAVE_BROTLI)
-_add_if("zstd"          HAVE_ZSTD)
-_add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
-_add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN)
-_add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
-                        ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
-# TODO SSP1 (Schannel) check is missing
-_add_if("SSPI"          USE_WINDOWS_SSPI)
-_add_if("GSS-API"       HAVE_GSSAPI)
-_add_if("alt-svc"       NOT CURL_DISABLE_ALTSVC)
-_add_if("HSTS"          NOT CURL_DISABLE_HSTS)
-# TODO SSP1 missing for SPNEGO
-_add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
-                        (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-_add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
-                        (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-# NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
-_add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
-                        (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
-# TODO missing option (autoconf: --enable-ntlm-wb)
-_add_if("NTLM_WB"       NOT (CURL_DISABLE_NTLM) AND
-                        (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
-                        NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
-_add_if("TLS-SRP"       USE_TLS_SRP)
-# TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header
-_add_if("HTTP2"         USE_NGHTTP2)
-_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
-_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
-# TODO wolfSSL only support this from v5.0.0 onwards
-_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
-                        OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
-                        USE_MBEDTLS OR USE_SECTRANSP))
-_add_if("unicode"       ENABLE_UNICODE)
-_add_if("threadsafe"    HAVE_ATOMIC OR (WIN32 AND
-                        HAVE_WIN32_WINNT GREATER_EQUAL 0x600))
-_add_if("PSL"           USE_LIBPSL)
-string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
-message(STATUS "Enabled features: ${SUPPORT_FEATURES}")
-
-# Clear list and try to detect available protocols
-set(_items)
-_add_if("HTTP"          NOT CURL_DISABLE_HTTP)
-_add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
-_add_if("FTP"           NOT CURL_DISABLE_FTP)
-_add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
-_add_if("FILE"          NOT CURL_DISABLE_FILE)
-_add_if("TELNET"        NOT CURL_DISABLE_TELNET)
-_add_if("LDAP"          NOT CURL_DISABLE_LDAP)
-# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS
-_add_if("LDAPS"         NOT CURL_DISABLE_LDAPS AND
-                        ((USE_OPENLDAP AND SSL_ENABLED) OR
-                        (NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
-_add_if("DICT"          NOT CURL_DISABLE_DICT)
-_add_if("TFTP"          NOT CURL_DISABLE_TFTP)
-_add_if("GOPHER"        NOT CURL_DISABLE_GOPHER)
-_add_if("GOPHERS"       NOT CURL_DISABLE_GOPHER AND SSL_ENABLED)
-_add_if("POP3"          NOT CURL_DISABLE_POP3)
-_add_if("POP3S"         NOT CURL_DISABLE_POP3 AND SSL_ENABLED)
-_add_if("IMAP"          NOT CURL_DISABLE_IMAP)
-_add_if("IMAPS"         NOT CURL_DISABLE_IMAP AND SSL_ENABLED)
-_add_if("SMB"           NOT CURL_DISABLE_SMB AND
-                        use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
-_add_if("SMBS"          NOT CURL_DISABLE_SMB AND SSL_ENABLED AND
-                        use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
-_add_if("SMTP"          NOT CURL_DISABLE_SMTP)
-_add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND SSL_ENABLED)
-_add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH)
-_add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
-_add_if("RTSP"          NOT CURL_DISABLE_RTSP)
-_add_if("RTMP"          USE_LIBRTMP)
-_add_if("MQTT"          NOT CURL_DISABLE_MQTT)
-_add_if("WS"            USE_WEBSOCKETS)
-_add_if("WSS"           USE_WEBSOCKETS)
-if(_items)
-  list(SORT _items)
-endif()
-string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}")
-message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}")
-
-# Clear list and collect SSL backends
-set(_items)
-_add_if("Schannel"         SSL_ENABLED AND USE_SCHANNEL)
-_add_if("OpenSSL"          SSL_ENABLED AND USE_OPENSSL)
-_add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
-_add_if("mbedTLS"          SSL_ENABLED AND USE_MBEDTLS)
-_add_if("BearSSL"          SSL_ENABLED AND USE_BEARSSL)
-_add_if("wolfSSL"          SSL_ENABLED AND USE_WOLFSSL)
-_add_if("GnuTLS"           SSL_ENABLED AND USE_GNUTLS)
-
-if(_items)
-  list(SORT _items)
-endif()
-string(REPLACE ";" " " SSL_BACKENDS "${_items}")
-message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
-if(CURL_DEFAULT_SSL_BACKEND)
-  message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}")
-endif()
-
-# curl-config needs the following options to be set.
-set(CC                      "${CMAKE_C_COMPILER}")
-# TODO probably put a -D... options here?
-set(CONFIGURE_OPTIONS       "")
-set(CURLVERSION             "${CURL_VERSION}")
-set(exec_prefix             "\${prefix}")
-set(includedir              "\${prefix}/include")
-set(LDFLAGS                 "${CMAKE_SHARED_LINKER_FLAGS}")
-set(LIBCURL_LIBS            "")
-set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
-foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
-  if(TARGET "${_lib}")
-    set(_libname "${_lib}")
-    get_target_property(_imported "${_libname}" IMPORTED)
-    if(NOT _imported)
-      # Reading the LOCATION property on non-imported target will error out.
-      # Assume the user won't need this information in the .pc file.
-      continue()
+  # Helper to populate a list (_items) with a label when conditions (the remaining
+  # args) are satisfied
+  macro(_add_if label)
+    # needs to be a macro to allow this indirection
+    if(${ARGN})
+      set(_items ${_items} "${label}")
     endif()
-    get_target_property(_lib "${_libname}" LOCATION)
-    if(NOT _lib)
-      message(WARNING "Bad lib in library list: ${_libname}")
-      continue()
-    endif()
+  endmacro()
+
+  # NTLM support requires crypto function adaptions from various SSL libs
+  if(NOT (CURL_DISABLE_NTLM) AND
+      (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS))
+    set(use_curl_ntlm_core ON)
   endif()
-  if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
-    set(LIBCURL_LIBS          "${LIBCURL_LIBS} ${_lib}")
+
+  # Clear list and try to detect available features
+  set(_items)
+  _add_if("SSL"           SSL_ENABLED)
+  _add_if("IPv6"          ENABLE_IPV6)
+  _add_if("UnixSockets"   USE_UNIX_SOCKETS)
+  _add_if("libz"          HAVE_LIBZ)
+  _add_if("brotli"        HAVE_BROTLI)
+  _add_if("zstd"          HAVE_ZSTD)
+  _add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
+  _add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN)
+  _add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
+                          ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
+  _add_if("SSPI"          USE_WINDOWS_SSPI)
+  _add_if("GSS-API"       HAVE_GSSAPI)
+  _add_if("alt-svc"       NOT CURL_DISABLE_ALTSVC)
+  _add_if("HSTS"          NOT CURL_DISABLE_HSTS)
+  _add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
+                          (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
+  _add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
+                          (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
+  _add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
+                          (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
+  _add_if("NTLM_WB"       NOT (CURL_DISABLE_NTLM) AND
+                          (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
+                          NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
+  _add_if("TLS-SRP"       USE_TLS_SRP)
+  _add_if("HTTP2"         USE_NGHTTP2)
+  _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
+  _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
+  # TODO wolfSSL only support this from v5.0.0 onwards
+  _add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
+                          OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
+                          USE_MBEDTLS OR USE_SECTRANSP))
+  _add_if("unicode"       ENABLE_UNICODE)
+  _add_if("threadsafe"    HAVE_ATOMIC OR
+                          (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR
+                          (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x600))
+  _add_if("PSL"           USE_LIBPSL)
+  string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
+  message(STATUS "Enabled features: ${SUPPORT_FEATURES}")
+
+  # Clear list and try to detect available protocols
+  set(_items)
+  _add_if("HTTP"          NOT CURL_DISABLE_HTTP)
+  _add_if("IPFS"          NOT CURL_DISABLE_HTTP)
+  _add_if("IPNS"          NOT CURL_DISABLE_HTTP)
+  _add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
+  _add_if("FTP"           NOT CURL_DISABLE_FTP)
+  _add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
+  _add_if("FILE"          NOT CURL_DISABLE_FILE)
+  _add_if("TELNET"        NOT CURL_DISABLE_TELNET)
+  _add_if("LDAP"          NOT CURL_DISABLE_LDAP)
+  # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS
+  _add_if("LDAPS"         NOT CURL_DISABLE_LDAPS AND
+                          ((USE_OPENLDAP AND SSL_ENABLED) OR
+                          (NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
+  _add_if("DICT"          NOT CURL_DISABLE_DICT)
+  _add_if("TFTP"          NOT CURL_DISABLE_TFTP)
+  _add_if("GOPHER"        NOT CURL_DISABLE_GOPHER)
+  _add_if("GOPHERS"       NOT CURL_DISABLE_GOPHER AND SSL_ENABLED)
+  _add_if("POP3"          NOT CURL_DISABLE_POP3)
+  _add_if("POP3S"         NOT CURL_DISABLE_POP3 AND SSL_ENABLED)
+  _add_if("IMAP"          NOT CURL_DISABLE_IMAP)
+  _add_if("IMAPS"         NOT CURL_DISABLE_IMAP AND SSL_ENABLED)
+  _add_if("SMB"           NOT CURL_DISABLE_SMB AND
+                          use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
+  _add_if("SMBS"          NOT CURL_DISABLE_SMB AND SSL_ENABLED AND
+                          use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
+  _add_if("SMTP"          NOT CURL_DISABLE_SMTP)
+  _add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND SSL_ENABLED)
+  _add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH)
+  _add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
+  _add_if("RTSP"          NOT CURL_DISABLE_RTSP)
+  _add_if("RTMP"          USE_LIBRTMP)
+  _add_if("MQTT"          NOT CURL_DISABLE_MQTT)
+  _add_if("WS"            USE_WEBSOCKETS)
+  _add_if("WSS"           USE_WEBSOCKETS)
+  if(_items)
+    list(SORT _items)
+  endif()
+  string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}")
+  message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}")
+
+  # Clear list and collect SSL backends
+  set(_items)
+  _add_if("Schannel"         SSL_ENABLED AND USE_SCHANNEL)
+  _add_if("OpenSSL"          SSL_ENABLED AND USE_OPENSSL)
+  _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
+  _add_if("mbedTLS"          SSL_ENABLED AND USE_MBEDTLS)
+  _add_if("BearSSL"          SSL_ENABLED AND USE_BEARSSL)
+  _add_if("wolfSSL"          SSL_ENABLED AND USE_WOLFSSL)
+  _add_if("GnuTLS"           SSL_ENABLED AND USE_GNUTLS)
+
+  if(_items)
+    list(SORT _items)
+  endif()
+  string(REPLACE ";" " " SSL_BACKENDS "${_items}")
+  message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
+  if(CURL_DEFAULT_SSL_BACKEND)
+    message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}")
+  endif()
+
+  # curl-config needs the following options to be set.
+  set(CC                      "${CMAKE_C_COMPILER}")
+  # TODO probably put a -D... options here?
+  set(CONFIGURE_OPTIONS       "")
+  set(CURLVERSION             "${CURL_VERSION}")
+  set(exec_prefix             "\${prefix}")
+  set(includedir              "\${prefix}/include")
+  set(LDFLAGS                 "${CMAKE_SHARED_LINKER_FLAGS}")
+  set(LIBCURL_LIBS            "")
+  set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
+  foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
+    if(TARGET "${_lib}")
+      set(_libname "${_lib}")
+      get_target_property(_imported "${_libname}" IMPORTED)
+      if(NOT _imported)
+        # Reading the LOCATION property on non-imported target will error out.
+        # Assume the user won't need this information in the .pc file.
+        continue()
+      endif()
+      get_target_property(_lib "${_libname}" LOCATION)
+      if(NOT _lib)
+        message(WARNING "Bad lib in library list: ${_libname}")
+        continue()
+      endif()
+    endif()
+    if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
+      set(LIBCURL_LIBS          "${LIBCURL_LIBS} ${_lib}")
+    else()
+      set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
+    endif()
+  endforeach()
+  if(BUILD_SHARED_LIBS)
+    set(ENABLE_SHARED         "yes")
+    set(LIBCURL_NO_SHARED     "")
+    set(CPPFLAG_CURL_STATICLIB "")
   else()
-    set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
+    set(ENABLE_SHARED         "no")
+    set(LIBCURL_NO_SHARED     "${LIBCURL_LIBS}")
+    set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB")
   endif()
-endforeach()
-if(BUILD_SHARED_LIBS)
-  set(ENABLE_SHARED         "yes")
-  set(LIBCURL_NO_SHARED     "")
-  set(CPPFLAG_CURL_STATICLIB "")
-else()
-  set(ENABLE_SHARED         "no")
-  set(LIBCURL_NO_SHARED     "${LIBCURL_LIBS}")
-  set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB")
-endif()
-if(BUILD_STATIC_LIBS)
-  set(ENABLE_STATIC         "yes")
-else()
-  set(ENABLE_STATIC         "no")
-endif()
-# "a" (Linux) or "lib" (Windows)
-string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}")
-set(prefix                  "${CMAKE_INSTALL_PREFIX}")
-# Set this to "yes" to append all libraries on which -lcurl is dependent
-set(REQUIRE_LIB_DEPS        "no")
-# SUPPORT_FEATURES
-# SUPPORT_PROTOCOLS
-set(VERSIONNUM              "${CURL_VERSION_NUM}")
+  if(BUILD_STATIC_LIBS)
+    set(ENABLE_STATIC         "yes")
+  else()
+    set(ENABLE_STATIC         "no")
+  endif()
+  # "a" (Linux) or "lib" (Windows)
+  string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}")
+  set(prefix                  "${CMAKE_INSTALL_PREFIX}")
+  # Set this to "yes" to append all libraries on which -lcurl is dependent
+  set(REQUIRE_LIB_DEPS        "no")
+  # SUPPORT_FEATURES
+  # SUPPORT_PROTOCOLS
+  set(VERSIONNUM              "${CURL_VERSION_NUM}")
 
-# Finally generate a "curl-config" matching this config
-# Use:
-# * ENABLE_SHARED
-# * ENABLE_STATIC
-configure_file("${CURL_SOURCE_DIR}/curl-config.in"
-               "${CURL_BINARY_DIR}/curl-config" @ONLY)
-install(FILES "${CURL_BINARY_DIR}/curl-config"
-        DESTINATION ${CMAKE_INSTALL_BINDIR}
-        PERMISSIONS
-          OWNER_READ OWNER_WRITE OWNER_EXECUTE
-          GROUP_READ GROUP_EXECUTE
-          WORLD_READ WORLD_EXECUTE)
+  # Finally generate a "curl-config" matching this config
+  # Use:
+  # * ENABLE_SHARED
+  # * ENABLE_STATIC
+  configure_file("${CURL_SOURCE_DIR}/curl-config.in"
+                "${CURL_BINARY_DIR}/curl-config" @ONLY)
+  install(FILES "${CURL_BINARY_DIR}/curl-config"
+          DESTINATION ${CMAKE_INSTALL_BINDIR}
+          PERMISSIONS
+            OWNER_READ OWNER_WRITE OWNER_EXECUTE
+            GROUP_READ GROUP_EXECUTE
+            WORLD_READ WORLD_EXECUTE)
 
-# Finally generate a pkg-config file matching this config
-configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
-               "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
-install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
-        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+  # Finally generate a pkg-config file matching this config
+  configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
+                "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
+  install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
+          DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
 
-# install headers
-install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
-    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-    FILES_MATCHING PATTERN "*.h")
+  # install headers
+  install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
+      DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+      FILES_MATCHING PATTERN "*.h")
 
-include(CMakePackageConfigHelpers)
-write_basic_package_version_file(
-    "${version_config}"
-    VERSION ${CURL_VERSION}
-    COMPATIBILITY SameMajorVersion
-)
-file(READ "${version_config}" generated_version_config)
-file(WRITE "${version_config}"
-"if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\")
-    # Version 8 satisfies version 7... requirements
-    set(PACKAGE_FIND_VERSION_MAJOR 8)
-    set(PACKAGE_FIND_VERSION_COUNT 1)
-endif()
-${generated_version_config}"
-)
+  include(CMakePackageConfigHelpers)
+  write_basic_package_version_file(
+      "${version_config}"
+      VERSION ${CURL_VERSION}
+      COMPATIBILITY SameMajorVersion
+  )
+  file(READ "${version_config}" generated_version_config)
+  file(WRITE "${version_config}"
+  "if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\")
+      # Version 8 satisfies version 7... requirements
+      set(PACKAGE_FIND_VERSION_MAJOR 8)
+      set(PACKAGE_FIND_VERSION_COUNT 1)
+  endif()
+  ${generated_version_config}"
+  )
 
-# Use:
-# * TARGETS_EXPORT_NAME
-# * PROJECT_NAME
-configure_package_config_file(CMake/curl-config.cmake.in
-        "${project_config}"
-        INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR}
-)
+  # Use:
+  # * TARGETS_EXPORT_NAME
+  # * PROJECT_NAME
+  configure_package_config_file(CMake/curl-config.cmake.in
+          "${project_config}"
+          INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR}
+  )
 
-if(CURL_ENABLE_EXPORT_TARGET)
+  if(CURL_ENABLE_EXPORT_TARGET)
+    install(
+            EXPORT "${TARGETS_EXPORT_NAME}"
+            NAMESPACE "${PROJECT_NAME}::"
+            DESTINATION ${CURL_INSTALL_CMAKE_DIR}
+    )
+  endif()
+
   install(
-          EXPORT "${TARGETS_EXPORT_NAME}"
-          NAMESPACE "${PROJECT_NAME}::"
+          FILES ${version_config} ${project_config}
           DESTINATION ${CURL_INSTALL_CMAKE_DIR}
   )
-endif()
 
-install(
-        FILES ${version_config} ${project_config}
-        DESTINATION ${CURL_INSTALL_CMAKE_DIR}
-)
-
-# Workaround for MSVS10 to avoid the Dialog Hell
-# FIXME: This could be removed with future version of CMake.
-if(MSVC_VERSION EQUAL 1600)
-  set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln")
-  if(EXISTS "${CURL_SLN_FILENAME}")
-    file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n")
+  # Workaround for MSVS10 to avoid the Dialog Hell
+  # FIXME: This could be removed with future version of CMake.
+  if(MSVC_VERSION EQUAL 1600)
+    set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln")
+    if(EXISTS "${CURL_SLN_FILENAME}")
+      file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n")
+    endif()
   endif()
-endif()
 
-if(NOT TARGET curl_uninstall)
-  configure_file(
-      ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in
-      ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake
-      IMMEDIATE @ONLY)
+  if(NOT TARGET curl_uninstall)
+    configure_file(
+        ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in
+        ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake
+        IMMEDIATE @ONLY)
 
-  add_custom_target(curl_uninstall
-      COMMAND ${CMAKE_COMMAND} -P
-      ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
+    add_custom_target(curl_uninstall
+        COMMAND ${CMAKE_COMMAND} -P
+        ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
+  endif()
 endif()
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING
index d1eab3e..d9e7e0b 100644
--- a/Utilities/cmcurl/COPYING
+++ b/Utilities/cmcurl/COPYING
@@ -1,6 +1,6 @@
 COPYRIGHT AND PERMISSION NOTICE
 
-Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2024, Daniel Stenberg, <daniel@haxx.se>, and many
 contributors, see the THANKS file.
 
 All rights reserved.
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index 0496570..5b9bcc6 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -53,28 +53,19 @@
 #include "curlver.h"         /* libcurl version defines   */
 #include "system.h"          /* determine things run-time */
 
-/*
- * Define CURL_WIN32 when build target is Win32 API
- */
-
-#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) &&        \
-  !defined(__SYMBIAN32__)
-#define CURL_WIN32
-#endif
-
 #include <stdio.h>
 #include <limits.h>
 
-#if (defined(__FreeBSD__) && (__FreeBSD__ >= 2)) || defined(__MidnightBSD__)
+#if defined(__FreeBSD__) || defined(__MidnightBSD__)
 /* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */
-#include <osreldate.h>
+#include <sys/param.h>
 #endif
 
 /* The include stuff here below is mainly for time_t! */
 #include <sys/types.h>
 #include <time.h>
 
-#if defined(CURL_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
 #if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \
       defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H))
 /* The check above prevents the winsock2 inclusion if winsock.h already was
@@ -88,7 +79,7 @@
    libc5-based Linux systems. Only include it on systems that are known to
    require it! */
 #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
-    defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
+    defined(__minix) || defined(__INTEGRITY) || \
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
@@ -97,11 +88,11 @@
 #include <sys/select.h>
 #endif
 
-#if !defined(CURL_WIN32) && !defined(_WIN32_WCE)
+#if !defined(_WIN32) && !defined(_WIN32_WCE)
 #include <sys/socket.h>
 #endif
 
-#if !defined(CURL_WIN32)
+#if !defined(_WIN32)
 #include <sys/time.h>
 #endif
 
@@ -128,7 +119,7 @@
 
 #ifdef CURL_STATICLIB
 #  define CURL_EXTERN
-#elif defined(CURL_WIN32) || defined(__SYMBIAN32__) || \
+#elif defined(_WIN32) || \
      (__has_declspec_attribute(dllexport) && \
       __has_declspec_attribute(dllimport))
 #  if defined(BUILDING_LIBCURL)
@@ -144,7 +135,7 @@
 
 #ifndef curl_socket_typedef
 /* socket typedef */
-#if defined(CURL_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
+#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
 typedef SOCKET curl_socket_t;
 #define CURL_SOCKET_BAD INVALID_SOCKET
 #else
@@ -640,6 +631,7 @@
   CURLE_PROXY,                   /* 97 - proxy handshake error */
   CURLE_SSL_CLIENTCERT,          /* 98 - client-side certificate required */
   CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
+  CURLE_TOO_LARGE,               /* 100 - a value/data met its maximum */
   CURL_LAST /* never use! */
 } CURLcode;
 
@@ -1854,7 +1846,8 @@
   /* allow GSSAPI credential delegation */
   CURLOPT(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_VALUES, 210),
 
-  /* Set the name servers to use for DNS resolution */
+  /* Set the name servers to use for DNS resolution.
+   * Only supported by the c-ares DNS backend */
   CURLOPT(CURLOPT_DNS_SERVERS, CURLOPTTYPE_STRINGPOINT, 211),
 
   /* Time-out accept operations (currently for FTP only) after this amount
@@ -2210,6 +2203,9 @@
   /* set a specific client IP for HAProxy PROXY protocol header? */
   CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323),
 
+  /* millisecond version */
+  CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2941,7 +2937,8 @@
   CURLINFO_CAPATH           = CURLINFO_STRING + 62,
   CURLINFO_XFER_ID          = CURLINFO_OFF_T + 63,
   CURLINFO_CONN_ID          = CURLINFO_OFF_T + 64,
-  CURLINFO_LASTONE          = 64
+  CURLINFO_QUEUE_TIME_T     = CURLINFO_OFF_T + 65,
+  CURLINFO_LASTONE          = 65
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -3220,6 +3217,7 @@
 #include "options.h"
 #include "header.h"
 #include "websockets.h"
+#include "mprintf.h"
 
 /* the typechecker doesn't work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 3c3f992..85008e1 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,12 +32,12 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.4.0"
+#define LIBCURL_VERSION "8.6.0"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 4
+#define LIBCURL_VERSION_MINOR 6
 #define LIBCURL_VERSION_PATCH 0
 
 /* This is the numeric version of the libcurl version number, meant for easier
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080400
+#define LIBCURL_VERSION_NUM 0x080600
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h
index dc5664b..4f70454 100644
--- a/Utilities/cmcurl/include/curl/mprintf.h
+++ b/Utilities/cmcurl/include/curl/mprintf.h
@@ -34,19 +34,27 @@
 
 #if (defined(__GNUC__) || defined(__clang__)) &&                        \
   defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
-  !defined(__MINGW32__) && !defined(CURL_NO_FMT_CHECKS)
-#define CURL_TEMP_PRINTF(a,b) __attribute__ ((format(printf, a, b)))
+  !defined(CURL_NO_FMT_CHECKS)
+#if defined(__MINGW32__) && !defined(__clang__)
+#define CURL_TEMP_PRINTF(fmt, arg) \
+  __attribute__((format(gnu_printf, fmt, arg)))
 #else
-#define CURL_TEMP_PRINTF(a,b)
+#define CURL_TEMP_PRINTF(fmt, arg) \
+  __attribute__((format(printf, fmt, arg)))
+#endif
+#else
+#define CURL_TEMP_PRINTF(fmt, arg)
 #endif
 
-CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2);
+CURL_EXTERN int curl_mprintf(const char *format, ...)
+  CURL_TEMP_PRINTF(1, 2);
 CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...)
   CURL_TEMP_PRINTF(2, 3);
 CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...)
   CURL_TEMP_PRINTF(2, 3);
 CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength,
-                               const char *format, ...) CURL_TEMP_PRINTF(3, 4);
+                               const char *format, ...)
+  CURL_TEMP_PRINTF(3, 4);
 CURL_EXTERN int curl_mvprintf(const char *format, va_list args)
   CURL_TEMP_PRINTF(1, 0);
 CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args)
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h
index 97e0d03..81a1b81 100644
--- a/Utilities/cmcurl/include/curl/system.h
+++ b/Utilities/cmcurl/include/curl/system.h
@@ -141,29 +141,6 @@
 #    define CURL_TYPEOF_CURL_SOCKLEN_T int
 #  endif
 
-#elif defined(__SYMBIAN32__)
-#  if defined(__EABI__) /* Treat all ARM compilers equally */
-#    define CURL_TYPEOF_CURL_OFF_T     long long
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  elif defined(__CW32__)
-#    pragma longlong on
-#    define CURL_TYPEOF_CURL_OFF_T     long long
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  elif defined(__VC32__)
-#    define CURL_TYPEOF_CURL_OFF_T     __int64
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  endif
-#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
-
 #elif defined(macintosh)
 #  include <ConditionalMacros.h>
 #  if TYPE_LONGLONG
@@ -201,14 +178,14 @@
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 
 #elif defined(__MINGW32__)
+#  include <inttypes.h>
 #  define CURL_TYPEOF_CURL_OFF_T     long long
-#  define CURL_FORMAT_CURL_OFF_T     "I64d"
-#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_FORMAT_CURL_OFF_T     PRId64
+#  define CURL_FORMAT_CURL_OFF_TU    PRIu64
 #  define CURL_SUFFIX_CURL_OFF_T     LL
 #  define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
 #  define CURL_PULL_SYS_TYPES_H      1
-#  define CURL_PULL_WS2TCPIP_H       1
 
 #elif defined(__VMS)
 #  if defined(__VAX)
@@ -370,7 +347,14 @@
 /* ===================================== */
 
 #elif defined(_MSC_VER)
-#  if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+#  if (_MSC_VER >= 1800)
+#    include <inttypes.h>
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     PRId64
+#    define CURL_FORMAT_CURL_OFF_TU    PRIu64
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
 #    define CURL_TYPEOF_CURL_OFF_T     __int64
 #    define CURL_FORMAT_CURL_OFF_T     "I64d"
 #    define CURL_FORMAT_CURL_OFF_TU    "I64u"
@@ -432,15 +416,6 @@
 #define CURL_PULL_SYS_POLL_H
 #endif
 
-
-/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file  */
-/* ws2tcpip.h is required here to properly make type definitions below. */
-#ifdef CURL_PULL_WS2TCPIP_H
-#  include <winsock2.h>
-#  include <windows.h>
-#  include <ws2tcpip.h>
-#endif
-
 /* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file  */
 /* sys/types.h is required here to properly make type definitions below. */
 #ifdef CURL_PULL_SYS_TYPES_H
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index 88cdeb3..91f8c45 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -63,6 +63,7 @@
   CURLUE_BAD_SLASHES,         /* 28 */
   CURLUE_BAD_USER,            /* 29 */
   CURLUE_LACKS_IDN,           /* 30 */
+  CURLUE_TOO_LARGE,           /* 31 */
   CURLUE_LAST
 } CURLUcode;
 
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index 9899b9d..bf25d89 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -85,20 +85,25 @@
 return() # The rest of this file is not needed for building within CMake.
 #-----------------------------------------------------------------------------
 
-add_library(
-  curlu # special libcurlu library just for unittests
-  STATIC
-  EXCLUDE_FROM_ALL
-  ${HHEADERS} ${CSOURCES}
-)
-target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+if(BUILD_TESTING)
+  add_library(
+    curlu # special libcurlu library just for unittests
+    STATIC
+    EXCLUDE_FROM_ALL
+    ${HHEADERS} ${CSOURCES}
+  )
+  target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+endif()
 
 if(ENABLE_CURLDEBUG)
   # We must compile these sources separately to avoid memdebug.h redefinitions
   # applying to them.
   set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
 endif()
-target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+
+if(BUILD_TESTING)
+  target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+endif()
 
 transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
 include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index e568ef9..627148a 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -78,15 +78,19 @@
 LIB_VQUIC_CFILES = \
   vquic/curl_msh3.c   \
   vquic/curl_ngtcp2.c   \
+  vquic/curl_osslq.c   \
   vquic/curl_quiche.c   \
-  vquic/vquic.c
+  vquic/vquic.c \
+  vquic/vquic-tls.c
 
 LIB_VQUIC_HFILES = \
   vquic/curl_msh3.h   \
   vquic/curl_ngtcp2.h   \
+  vquic/curl_osslq.h   \
   vquic/curl_quiche.h   \
   vquic/vquic.h    \
-  vquic/vquic_int.h
+  vquic/vquic_int.h \
+  vquic/vquic-tls.h
 
 LIB_VSSH_CFILES =  \
   vssh/libssh.c    \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index 22b0b69..e9f62bf 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -97,7 +97,7 @@
                                       unsigned int srcport,
                                       unsigned int dstport)
 {
-  struct altsvc *as = calloc(sizeof(struct altsvc), 1);
+  struct altsvc *as = calloc(1, sizeof(struct altsvc));
   size_t hlen;
   size_t dlen;
   if(!as)
@@ -106,9 +106,11 @@
   dlen = strlen(dsthost);
   DEBUGASSERT(hlen);
   DEBUGASSERT(dlen);
-  if(!hlen || !dlen)
+  if(!hlen || !dlen) {
     /* bad input */
+    free(as);
     return NULL;
+  }
   if((hlen > 2) && srchost[0] == '[') {
     /* IPv6 address, strip off brackets */
     srchost++;
@@ -123,15 +125,13 @@
     dlen -= 2;
   }
 
-  as->src.host = Curl_memdup(srchost, hlen + 1);
+  as->src.host = Curl_memdup0(srchost, hlen);
   if(!as->src.host)
     goto error;
-  as->src.host[hlen] = 0;
 
-  as->dst.host = Curl_memdup(dsthost, dlen + 1);
+  as->dst.host = Curl_memdup0(dsthost, dlen);
   if(!as->dst.host)
     goto error;
-  as->dst.host[dlen] = 0;
 
   as->src.alpnid = srcalpnid;
   as->dst.alpnid = dstalpnid;
@@ -301,7 +301,7 @@
  */
 struct altsvcinfo *Curl_altsvc_init(void)
 {
-  struct altsvcinfo *asi = calloc(sizeof(struct altsvcinfo), 1);
+  struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
   if(!asi)
     return NULL;
   Curl_llist_init(&asi->list, NULL);
@@ -335,9 +335,6 @@
 CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
 {
   DEBUGASSERT(asi);
-  if(!ctrl)
-    /* unexpected */
-    return CURLE_BAD_FUNCTION_ARGUMENT;
   asi->flags = ctrl;
   return CURLE_OK;
 }
diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h
index de13738..228b446 100644
--- a/Utilities/cmcurl/lib/arpa_telnet.h
+++ b/Utilities/cmcurl/lib/arpa_telnet.h
@@ -56,12 +56,14 @@
   "TERM SPEED",  "LFLOW",          "LINEMODE",      "XDISPLOC",
   "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT",       "NEW-ENVIRON"
 };
+#define CURL_TELOPT(x)    telnetoptions[x]
+#else
+#define CURL_TELOPT(x)    ""
 #endif
 
 #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
 
 #define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM)
-#define CURL_TELOPT(x)    telnetoptions[x]
 
 #define CURL_NTELOPTS 40
 
@@ -103,7 +105,12 @@
 
 #define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \
                        ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) )
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
 #define CURL_TELCMD(x)    telnetcmds[(x)-CURL_TELCMD_MINIMUM]
+#else
+#define CURL_TELCMD(x)    ""
+#endif
 
 #endif /* CURL_DISABLE_TELNET */
 
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index e73e41d..76efba7 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -60,13 +60,13 @@
 #include "progress.h"
 #include "timediff.h"
 
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
-  defined(WIN32)
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#  include <ares_version.h> /* really old c-ares didn't include this by
-                               itself */
+#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
+  defined(_WIN32)
+#  define CARES_STATICLIB
+#endif
+#include <ares.h>
+#include <ares_version.h> /* really old c-ares didn't include this by
+                             itself */
 
 #if ARES_VERSION >= 0x010500
 /* c-ares 1.5.0 or later, the callback proto is modified */
@@ -173,10 +173,26 @@
   int status;
   struct ares_options options;
   int optmask = ARES_OPT_SOCK_STATE_CB;
+  static int ares_ver = 0;
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb_data = easy;
-  options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
-  optmask |= ARES_OPT_TIMEOUTMS;
+  if(ares_ver == 0)
+    ares_version(&ares_ver);
+
+  if(ares_ver < 0x011400) { /* c-ares included similar change since 1.20.0 */
+    options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
+    optmask |= ARES_OPT_TIMEOUTMS;
+  }
+
+  /*
+     if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
+
+     if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
+     to set the timeout value;
+
+     if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
+     overwrite c-ares' timeout.
+  */
 
   status = ares_init_options((ares_channel*)resolver, &options, optmask);
   if(status != ARES_SUCCESS) {
@@ -755,7 +771,7 @@
   size_t namelen = strlen(hostname);
   *waitp = 0; /* default to synchronous response */
 
-  res = calloc(sizeof(struct thread_data) + namelen, 1);
+  res = calloc(1, sizeof(struct thread_data) + namelen);
   if(res) {
     strcpy(res->hostname, hostname);
     data->state.async.hostname = res->hostname;
@@ -858,6 +874,7 @@
   case ARES_ENODATA:
   case ARES_EBADSTR:
   default:
+    DEBUGF(infof(data, "bad servers set"));
     result = CURLE_BAD_FUNCTION_ARGUMENT;
     break;
   }
@@ -896,6 +913,7 @@
   }
   else {
     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
+      DEBUGF(infof(data, "bad DNS IPv4 address"));
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
   }
@@ -923,6 +941,7 @@
   }
   else {
     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
+      DEBUGF(infof(data, "bad DNS IPv6 address"));
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
   }
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index a2e294f..d4d382a 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -54,6 +54,7 @@
 #  define RESOLVER_ENOMEM  ENOMEM
 #endif
 
+#include "system_win32.h"
 #include "urldata.h"
 #include "sendf.h"
 #include "hostip.h"
@@ -144,9 +145,22 @@
                                 const char *hostname, int port,
                                 const struct addrinfo *hints);
 
+#ifdef _WIN32
+/* Thread sync data used by GetAddrInfoExW for win8+ */
+struct thread_sync_data_w8
+{
+  OVERLAPPED overlapped;
+  ADDRINFOEXW_ *res;
+  HANDLE cancel_ev;
+  ADDRINFOEXW_ hints;
+};
+#endif
 
 /* Data for synchronization between resolver thread and its parent */
 struct thread_sync_data {
+#ifdef _WIN32
+  struct thread_sync_data_w8 w8;
+#endif
   curl_mutex_t *mtx;
   int done;
   int port;
@@ -165,6 +179,9 @@
 };
 
 struct thread_data {
+#ifdef _WIN32
+  HANDLE complete_ev;
+#endif
   curl_thread_t thread_hnd;
   unsigned int poll_interval;
   timediff_t interval_end;
@@ -196,7 +213,7 @@
    * the other end (for reading) is always closed in the parent thread.
    */
   if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
-    sclose(tsd->sock_pair[1]);
+    wakeup_close(tsd->sock_pair[1]);
   }
 #endif
   memset(tsd, 0, sizeof(*tsd));
@@ -233,8 +250,8 @@
   Curl_mutex_init(tsd->mtx);
 
 #ifndef CURL_DISABLE_SOCKETPAIR
-  /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */
-  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) {
+  /* create socket pair or pipe */
+  if(wakeup_create(&tsd->sock_pair[0]) < 0) {
     tsd->sock_pair[0] = CURL_SOCKET_BAD;
     tsd->sock_pair[1] = CURL_SOCKET_BAD;
     goto err_exit;
@@ -254,7 +271,7 @@
 err_exit:
 #ifndef CURL_DISABLE_SOCKETPAIR
   if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
-    sclose(tsd->sock_pair[0]);
+    wakeup_close(tsd->sock_pair[0]);
     tsd->sock_pair[0] = CURL_SOCKET_BAD;
   }
 #endif
@@ -276,6 +293,151 @@
   return result;
 }
 
+#ifdef _WIN32
+static VOID WINAPI
+query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
+{
+  size_t ss_size;
+  const ADDRINFOEXW_ *ai;
+  struct Curl_addrinfo *ca;
+  struct Curl_addrinfo *cafirst = NULL;
+  struct Curl_addrinfo *calast = NULL;
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-align"
+#endif
+  struct thread_sync_data *tsd =
+    CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+  struct thread_data *td = tsd->td;
+  const ADDRINFOEXW_ *res = tsd->w8.res;
+  int error = (int)err;
+  (void)bytes;
+
+  if(error == ERROR_SUCCESS) {
+    /* traverse the addrinfo list */
+
+    for(ai = res; ai != NULL; ai = ai->ai_next) {
+      size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
+      /* ignore elements with unsupported address family, */
+      /* settle family-specific sockaddr structure size.  */
+      if(ai->ai_family == AF_INET)
+        ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+      else if(ai->ai_family == AF_INET6)
+        ss_size = sizeof(struct sockaddr_in6);
+#endif
+      else
+        continue;
+
+      /* ignore elements without required address info */
+      if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+        continue;
+
+      /* ignore elements with bogus address size */
+      if((size_t)ai->ai_addrlen < ss_size)
+        continue;
+
+      ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
+      if(!ca) {
+        error = EAI_MEMORY;
+        break;
+      }
+
+      /* copy each structure member individually, member ordering, */
+      /* size, or padding might be different for each platform.    */
+      ca->ai_flags     = ai->ai_flags;
+      ca->ai_family    = ai->ai_family;
+      ca->ai_socktype  = ai->ai_socktype;
+      ca->ai_protocol  = ai->ai_protocol;
+      ca->ai_addrlen   = (curl_socklen_t)ss_size;
+      ca->ai_addr      = NULL;
+      ca->ai_canonname = NULL;
+      ca->ai_next      = NULL;
+
+      ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+      memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+      if(namelen) {
+        size_t i;
+        ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
+        for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
+          ca->ai_canonname[i] = (char)ai->ai_canonname[i];
+        ca->ai_canonname[namelen] = '\0';
+      }
+
+      /* if the return list is empty, this becomes the first element */
+      if(!cafirst)
+        cafirst = ca;
+
+      /* add this element last in the return list */
+      if(calast)
+        calast->ai_next = ca;
+      calast = ca;
+    }
+
+    /* if we failed, also destroy the Curl_addrinfo list */
+    if(error) {
+      Curl_freeaddrinfo(cafirst);
+      cafirst = NULL;
+    }
+    else if(!cafirst) {
+#ifdef EAI_NONAME
+      /* rfc3493 conformant */
+      error = EAI_NONAME;
+#else
+      /* rfc3493 obsoleted */
+      error = EAI_NODATA;
+#endif
+#ifdef USE_WINSOCK
+      SET_SOCKERRNO(error);
+#endif
+    }
+    tsd->res = cafirst;
+  }
+
+  if(tsd->w8.res) {
+    Curl_FreeAddrInfoExW(tsd->w8.res);
+    tsd->w8.res = NULL;
+  }
+
+  if(error) {
+    tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
+    if(tsd->sock_error == 0)
+      tsd->sock_error = RESOLVER_ENOMEM;
+  }
+  else {
+    Curl_addrinfo_set_port(tsd->res, tsd->port);
+  }
+
+  Curl_mutex_acquire(tsd->mtx);
+  if(tsd->done) {
+    /* too late, gotta clean up the mess */
+    Curl_mutex_release(tsd->mtx);
+    destroy_thread_sync_data(tsd);
+    free(td);
+  }
+  else {
+#ifndef CURL_DISABLE_SOCKETPAIR
+    char buf[1];
+    if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
+      /* DNS has been resolved, signal client task */
+      buf[0] = 1;
+      if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
+        /* update sock_erro to errno */
+        tsd->sock_error = SOCKERRNO;
+      }
+    }
+#endif
+    tsd->done = 1;
+    Curl_mutex_release(tsd->mtx);
+    if(td->complete_ev)
+      SetEvent(td->complete_ev); /* Notify caller that the query completed */
+  }
+}
+#endif
 
 #ifdef HAVE_GETADDRINFO
 
@@ -320,7 +482,7 @@
     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
       /* DNS has been resolved, signal client task */
       buf[0] = 1;
-      if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
+      if(wakeup_write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
         /* update sock_erro to errno */
         tsd->sock_error = SOCKERRNO;
       }
@@ -391,9 +553,21 @@
     Curl_mutex_release(td->tsd.mtx);
 
     if(!done) {
+#ifdef _WIN32
+      if(td->complete_ev)
+        CloseHandle(td->complete_ev);
+      else
+#endif
       Curl_thread_destroy(td->thread_hnd);
     }
     else {
+#ifdef _WIN32
+      if(td->complete_ev) {
+        Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
+        WaitForSingleObject(td->complete_ev, INFINITE);
+        CloseHandle(td->complete_ev);
+      }
+#endif
       if(td->thread_hnd != curl_thread_t_null)
         Curl_thread_join(&td->thread_hnd);
 
@@ -439,6 +613,9 @@
   asp->status = 0;
   asp->dns = NULL;
   td->thread_hnd = curl_thread_t_null;
+#ifdef _WIN32
+  td->complete_ev = NULL;
+#endif
 
   if(!init_thread_sync_data(td, hostname, port, hints)) {
     asp->tdata = NULL;
@@ -454,6 +631,41 @@
   /* The thread will set this to 1 when complete. */
   td->tsd.done = 0;
 
+#ifdef _WIN32
+  if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
+     Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
+#define MAX_NAME_LEN 256 /* max domain name is 253 chars */
+#define MAX_PORT_LEN 8
+    WCHAR namebuf[MAX_NAME_LEN];
+    WCHAR portbuf[MAX_PORT_LEN];
+    /* calculate required length */
+    int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
+                                    -1, NULL, 0);
+    if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
+      /* do utf8 conversion */
+      w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
+      if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
+        swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
+        td->tsd.w8.hints.ai_family = hints->ai_family;
+        td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
+        td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+        if(!td->complete_ev) {
+          /* failed to start, mark it as done here for proper cleanup. */
+          td->tsd.done = 1;
+          goto err_exit;
+        }
+        err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
+                                  NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
+                                  NULL, &td->tsd.w8.overlapped,
+                                  &query_complete, &td->tsd.w8.cancel_ev);
+        if(err != WSA_IO_PENDING)
+          query_complete(err, 0, &td->tsd.w8.overlapped);
+        return TRUE;
+      }
+    }
+  }
+#endif
+
 #ifdef HAVE_GETADDRINFO
   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
 #else
@@ -490,9 +702,22 @@
   DEBUGASSERT(data);
   td = data->state.async.tdata;
   DEBUGASSERT(td);
+#ifdef _WIN32
+  DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
+#else
   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
+#endif
 
   /* wait for the thread to resolve the name */
+#ifdef _WIN32
+  if(td->complete_ev) {
+    WaitForSingleObject(td->complete_ev, INFINITE);
+    CloseHandle(td->complete_ev);
+    if(entry)
+      result = getaddrinfo_complete(data);
+  }
+  else
+#endif
   if(Curl_thread_join(&td->thread_hnd)) {
     if(entry)
       result = getaddrinfo_complete(data);
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c
index 2a49b5a..919eb62 100644
--- a/Utilities/cmcurl/lib/base64.c
+++ b/Utilities/cmcurl/lib/base64.c
@@ -31,6 +31,7 @@
   !defined(CURL_DISABLE_SMTP) || \
   !defined(CURL_DISABLE_POP3) || \
   !defined(CURL_DISABLE_IMAP) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
   !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
 #include "curl/curl.h"
 #include "warnless.h"
diff --git a/Utilities/cmcurl/lib/bufref.c b/Utilities/cmcurl/lib/bufref.c
index ce686b6..f0a0e2a 100644
--- a/Utilities/cmcurl/lib/bufref.c
+++ b/Utilities/cmcurl/lib/bufref.c
@@ -25,6 +25,7 @@
 #include "curl_setup.h"
 #include "urldata.h"
 #include "bufref.h"
+#include "strdup.h"
 
 #include "curl_memory.h"
 #include "memdebug.h"
@@ -116,12 +117,9 @@
   DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
 
   if(ptr) {
-    cpy = malloc(len + 1);
+    cpy = Curl_memdup0(ptr, len);
     if(!cpy)
       return CURLE_OUT_OF_MEMORY;
-    if(len)
-      memcpy(cpy, ptr, len);
-    cpy[len] = '\0';
   }
 
   Curl_bufref_set(br, cpy, len, curl_free);
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c
index 5726ff1..d02ecd7 100644
--- a/Utilities/cmcurl/lib/c-hyper.c
+++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -22,6 +22,10 @@
  *
  ***************************************************************************/
 
+/* Curl's integration with Hyper. This replaces certain functions in http.c,
+ * based on configuration #defines. This implementation supports HTTP/1.1 but
+ * not HTTP/2.
+ */
 #include "curl_setup.h"
 
 #if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
@@ -144,7 +148,7 @@
 
   if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
     failf(data, "Too long response header");
-    data->state.hresult = CURLE_OUT_OF_MEMORY;
+    data->state.hresult = CURLE_TOO_LARGE;
     return HYPER_ITER_BREAK;
   }
 
@@ -172,17 +176,15 @@
 
   Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
 
-  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
-    writetype = CLIENTWRITE_HEADER;
-    if(data->state.hconnect)
-      writetype |= CLIENTWRITE_CONNECT;
-    if(data->req.httpcode/100 == 1)
-      writetype |= CLIENTWRITE_1XX;
-    result = Curl_client_write(data, writetype, headp, len);
-    if(result) {
-      data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
-      return HYPER_ITER_BREAK;
-    }
+  writetype = CLIENTWRITE_HEADER;
+  if(data->state.hconnect)
+    writetype |= CLIENTWRITE_CONNECT;
+  if(data->req.httpcode/100 == 1)
+    writetype |= CLIENTWRITE_1XX;
+  result = Curl_client_write(data, writetype, headp, len);
+  if(result) {
+    data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+    return HYPER_ITER_BREAK;
   }
 
   result = Curl_bump_headersize(data, len, FALSE);
@@ -201,7 +203,7 @@
   struct SingleRequest *k = &data->req;
   CURLcode result = CURLE_OK;
 
-  if(0 == k->bodywrites++) {
+  if(0 == k->bodywrites) {
     bool done = FALSE;
 #if defined(USE_NTLM)
     struct connectdata *conn = data->conn;
@@ -241,11 +243,6 @@
       return HYPER_ITER_BREAK;
     }
   }
-  if(k->ignorebody)
-    return HYPER_ITER_CONTINUE;
-  if(0 == len)
-    return HYPER_ITER_CONTINUE;
-  Curl_debug(data, CURLINFO_DATA_IN, buf, len);
   result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
 
   if(result) {
@@ -253,12 +250,6 @@
     return HYPER_ITER_BREAK;
   }
 
-  data->req.bytecount += len;
-  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
-  if(result) {
-    data->state.hresult = result;
-    return HYPER_ITER_BREAK;
-  }
   return HYPER_ITER_CONTINUE;
 }
 
@@ -310,13 +301,14 @@
   Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
              len);
 
-  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
-    writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
-    result = Curl_client_write(data, writetype,
-                               Curl_dyn_ptr(&data->state.headerb), len);
-    if(result)
-      return result;
-  }
+  writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
+  if(data->state.hconnect)
+    writetype |= CLIENTWRITE_CONNECT;
+  result = Curl_client_write(data, writetype,
+                             Curl_dyn_ptr(&data->state.headerb), len);
+  if(result)
+    return result;
+
   result = Curl_bump_headersize(data, len, FALSE);
   return result;
 }
@@ -333,6 +325,9 @@
       CURLE_WRITE_ERROR : CURLE_OK;
     if(result)
       failf(data, "hyperstream: couldn't pass blank header");
+    /* Hyper does chunked decoding itself. If it was added during
+     * response header processing, remove it again. */
+    Curl_cwriter_remove_by_name(data, "chunked");
   }
   return result;
 }
@@ -551,11 +546,9 @@
 
 static CURLcode debug_request(struct Curl_easy *data,
                               const char *method,
-                              const char *path,
-                              bool h2)
+                              const char *path)
 {
-  char *req = aprintf("%s %s HTTP/%s\r\n", method, path,
-                      h2?"2":"1.1");
+  char *req = aprintf("%s %s HTTP/1.1\r\n", method, path);
   if(!req)
     return CURLE_OUT_OF_MEMORY;
   Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
@@ -637,7 +630,6 @@
 static CURLcode request_target(struct Curl_easy *data,
                                struct connectdata *conn,
                                const char *method,
-                               bool h2,
                                hyper_request *req)
 {
   CURLcode result;
@@ -649,26 +641,13 @@
   if(result)
     return result;
 
-  if(h2 && hyper_request_set_uri_parts(req,
-                                       /* scheme */
-                                       (uint8_t *)data->state.up.scheme,
-                                       strlen(data->state.up.scheme),
-                                       /* authority */
-                                       (uint8_t *)conn->host.name,
-                                       strlen(conn->host.name),
-                                       /* path_and_query */
-                                       (uint8_t *)Curl_dyn_uptr(&r),
-                                       Curl_dyn_len(&r))) {
-    failf(data, "error setting uri parts to hyper");
-    result = CURLE_OUT_OF_MEMORY;
-  }
-  else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
+  if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
                                        Curl_dyn_len(&r))) {
     failf(data, "error setting uri to hyper");
     result = CURLE_OUT_OF_MEMORY;
   }
   else
-    result = debug_request(data, method, Curl_dyn_ptr(&r), h2);
+    result = debug_request(data, method, Curl_dyn_ptr(&r));
 
   Curl_dyn_free(&r);
 
@@ -899,7 +878,6 @@
   const char *p_accept; /* Accept: string */
   const char *method;
   Curl_HttpReq httpreq;
-  bool h2 = FALSE;
   const char *te = NULL; /* transfer-encoding */
   hyper_code rc;
 
@@ -907,6 +885,7 @@
      may be parts of the request that is not yet sent, since we can deal with
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
+  Curl_client_cleanup(data);
 
   infof(data, "Time for the Hyper dance");
   memset(h, 0, sizeof(struct hyptransfer));
@@ -917,6 +896,8 @@
 
   Curl_http_method(data, conn, &method, &httpreq);
 
+  DEBUGASSERT(data->req.bytecount ==  0);
+
   /* setup the authentication headers */
   {
     char *pq = NULL;
@@ -972,8 +953,9 @@
     goto error;
   }
   if(conn->alpn == CURL_HTTP_VERSION_2) {
-    hyper_clientconn_options_http2(options, 1);
-    h2 = TRUE;
+    failf(data, "ALPN protocol h2 not supported with Hyper");
+    result = CURLE_UNSUPPORTED_PROTOCOL;
+    goto error;
   }
   hyper_clientconn_options_set_preserve_header_case(options, 1);
   hyper_clientconn_options_set_preserve_header_order(options, 1);
@@ -1024,7 +1006,7 @@
     }
   }
   else {
-    if(!h2 && !data->state.disableexpect) {
+    if(!data->state.disableexpect) {
       data->state.expect100header = TRUE;
     }
   }
@@ -1035,7 +1017,7 @@
     goto error;
   }
 
-  result = request_target(data, conn, method, h2, req);
+  result = request_target(data, conn, method, req);
   if(result)
     goto error;
 
@@ -1056,19 +1038,10 @@
   if(result)
     goto error;
 
-  if(!h2) {
-    if(data->state.aptr.host) {
-      result = Curl_hyper_header(data, headers, data->state.aptr.host);
-      if(result)
-        goto error;
-    }
-  }
-  else {
-    /* For HTTP/2, we show the Host: header as if we sent it, to make it look
-       like for HTTP/1 but it isn't actually sent since :authority is then
-       used. */
-    Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
-               strlen(data->state.aptr.host));
+  if(data->state.aptr.host) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.host);
+    if(result)
+      goto error;
   }
 
   if(data->state.aptr.proxyuserpwd) {
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c
index 6748021..167e531 100644
--- a/Utilities/cmcurl/lib/cf-h1-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -70,6 +70,7 @@
   struct dynbuf request_data;
   size_t nsent;
   size_t headerlines;
+  struct Curl_chunker ch;
   enum keeponval {
     KEEPON_DONE,
     KEEPON_CONNECT,
@@ -133,6 +134,7 @@
 
   Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
   Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
+  Curl_httpchunk_init(data, &ts->ch, TRUE);
 
   *pts =  ts;
   connkeep(cf->conn, "HTTP proxy CONNECT");
@@ -146,14 +148,6 @@
 {
   if(ts->tunnel_state == new_state)
     return;
-  /* leaving this one */
-  switch(ts->tunnel_state) {
-  case H1_TUNNEL_CONNECT:
-    data->req.ignorebody = FALSE;
-    break;
-  default:
-    break;
-  }
   /* entering this one */
   switch(new_state) {
   case H1_TUNNEL_INIT:
@@ -183,7 +177,7 @@
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case H1_TUNNEL_FAILED:
     if(new_state == H1_TUNNEL_FAILED)
       CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
@@ -212,6 +206,7 @@
     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     Curl_dyn_free(&ts->rcvbuf);
     Curl_dyn_free(&ts->request_data);
+    Curl_httpchunk_free(data, &ts->ch);
     free(ts);
     cf->ctx = NULL;
   }
@@ -344,8 +339,8 @@
                                STRCONST("chunked"))) {
       infof(data, "CONNECT responded chunked");
       ts->chunked_encoding = TRUE;
-      /* init our chunky engine */
-      Curl_httpchunk_init(data);
+      /* reset our chunky engine */
+      Curl_httpchunk_reset(data, &ts->ch, TRUE);
     }
   }
   else if(Curl_compareheader(header,
@@ -373,8 +368,8 @@
   struct SingleRequest *k = &data->req;
   curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
   char *linep;
-  size_t perline;
-  int error;
+  size_t line_len;
+  int error, writetype;
 
 #define SELECT_OK      0
 #define SELECT_ERROR   1
@@ -386,12 +381,12 @@
     return CURLE_OK;
 
   while(ts->keepon) {
-    ssize_t gotbytes;
+    ssize_t nread;
     char byte;
 
     /* Read one byte at a time to avoid a race condition. Wait at most one
        second before looping to ensure continuous pgrsUpdates. */
-    result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
+    result = Curl_read(data, tunnelsocket, &byte, 1, &nread);
     if(result == CURLE_AGAIN)
       /* socket buffer drained, return */
       return CURLE_OK;
@@ -404,7 +399,7 @@
       break;
     }
 
-    if(gotbytes <= 0) {
+    if(nread <= 0) {
       if(data->set.proxyauth && data->state.authproxy.avail &&
          data->state.aptr.proxyuserpwd) {
         /* proxy auth was requested and there was proxy auth available,
@@ -432,17 +427,17 @@
           break;
         }
       }
-      else {
+      else if(ts->chunked_encoding) {
         /* chunked-encoded body, so we need to do the chunked dance
            properly to know when the end of the body is reached */
-        CHUNKcode r;
-        CURLcode extra;
-        ssize_t tookcareof = 0;
+        size_t consumed = 0;
 
         /* now parse the chunked piece of data so that we can
            properly tell when the stream ends */
-        r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
-        if(r == CHUNKE_STOP) {
+        result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
+        if(result)
+          return result;
+        if(Curl_httpchunk_is_done(data, &ts->ch)) {
           /* we're done reading chunks! */
           infof(data, "chunk reading DONE");
           ts->keepon = KEEPON_DONE;
@@ -462,22 +457,19 @@
 
     ts->headerlines++;
     linep = Curl_dyn_ptr(&ts->rcvbuf);
-    perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
+    line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
 
     /* output debug if that is requested */
-    Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
+    Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
 
-    if(!data->set.suppress_connect_headers) {
-      /* send the header to the callback */
-      int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
-        (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
+    /* send the header to the callback */
+    writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
+      (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
+    result = Curl_client_write(data, writetype, linep, line_len);
+    if(result)
+      return result;
 
-      result = Curl_client_write(data, writetype, linep, perline);
-      if(result)
-        return result;
-    }
-
-    result = Curl_bump_headersize(data, perline, TRUE);
+    result = Curl_bump_headersize(data, line_len, TRUE);
     if(result)
       return result;
 
@@ -500,29 +492,7 @@
                 " bytes of response-body", ts->cl);
         }
         else if(ts->chunked_encoding) {
-          CHUNKcode r;
-          CURLcode extra;
-
           infof(data, "Ignore chunked response-body");
-
-          /* We set ignorebody true here since the chunked decoder
-             function will acknowledge that. Pay attention so that this is
-             cleared again when this function returns! */
-          k->ignorebody = TRUE;
-
-          if(linep[1] == '\n')
-            /* this can only be a LF if the letter at index 0 was a CR */
-            linep++;
-
-          /* now parse the chunked piece of data so that we can properly
-             tell when the stream ends */
-          r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
-                                  &extra);
-          if(r == CHUNKE_STOP) {
-            /* we're done reading chunks! */
-            infof(data, "chunk reading DONE");
-            ts->keepon = KEEPON_DONE;
-          }
         }
         else {
           /* without content-length or chunked encoding, we
@@ -755,7 +725,7 @@
   }
 
   if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
-     data->set.str[STRING_USERAGENT]) {
+     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
     struct dynbuf ua;
     Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
     result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
@@ -915,7 +885,7 @@
       if(result)
         goto out;
       h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case H1_TUNNEL_CONNECT:
       /* see that the request is completely sent */
@@ -924,7 +894,7 @@
       if(result || !done)
         goto out;
       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case H1_TUNNEL_RECEIVE:
       /* read what is there */
@@ -939,7 +909,7 @@
         goto out;
       /* got it */
       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case H1_TUNNEL_RESPONSE:
       CURL_TRC_CF(data, cf, "CONNECT response");
@@ -1033,36 +1003,42 @@
   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
   if(*done) {
     cf->connected = TRUE;
+    /* Restore `data->req` fields that may habe been touched */
+    data->req.header = TRUE; /* assume header */
+    data->req.bytecount = 0;
+    data->req.ignorebody = FALSE;
+    Curl_client_cleanup(data);
+    Curl_pgrsSetUploadCounter(data, 0);
+    Curl_pgrsSetDownloadCounter(data, 0);
+
     tunnel_free(cf, data);
   }
   return result;
 }
 
-static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf,
+static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
-                                        curl_socket_t *socks)
+                                        struct easy_pollset *ps)
 {
   struct h1_tunnel_state *ts = cf->ctx;
-  int fds;
 
-  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  if(!fds && cf->next->connected && !cf->connected) {
+  if(!cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are tunneling. */
-    socks[0] = Curl_conn_cf_get_socket(cf, data);
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
     if(ts) {
       /* when we've sent a CONNECT to a proxy, we should rather either
          wait for the socket to become readable to be able to get the
          response headers or if we're still sending the request, wait
          for write. */
-      if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
-        return GETSOCK_WRITESOCK(0);
-      }
-      return GETSOCK_READSOCK(0);
+      if(ts->CONNECT.sending == HTTPSEND_REQUEST)
+        Curl_pollset_set_out_only(data, ps, sock);
+      else
+        Curl_pollset_set_in_only(data, ps, sock);
     }
-    return GETSOCK_WRITESOCK(0);
+    else
+      Curl_pollset_set_out_only(data, ps, sock);
   }
-  return fds;
 }
 
 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
@@ -1093,7 +1069,7 @@
   cf_h1_proxy_connect,
   cf_h1_proxy_close,
   Curl_cf_http_proxy_get_host,
-  cf_h1_proxy_get_select_socks,
+  cf_h1_proxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c
index dbc895d..f8f2f3c 100644
--- a/Utilities/cmcurl/lib/cf-h2-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -155,7 +155,7 @@
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case H2_TUNNEL_FAILED:
     if(new_state == H2_TUNNEL_FAILED)
       CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id);
@@ -221,10 +221,10 @@
   bits = CURL_CSELECT_IN;
   if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len)
     bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
+  if(data->state.select_bits != bits) {
+    CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
                 tunnel->stream_id, bits);
-    data->state.dselect_bits = bits;
+    data->state.select_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
 }
@@ -688,12 +688,8 @@
        * window and *assume* that we treat this like a WINDOW_UPDATE. Some
        * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
        * To be safe, we UNHOLD a stream in order not to stall. */
-      if((data->req.keepon & KEEP_SEND_HOLD) &&
-         (data->req.keepon & KEEP_SEND)) {
-        data->req.keepon &= ~KEEP_SEND_HOLD;
+      if(CURL_WANT_SEND(data)) {
         drain_tunnel(cf, data, &ctx->tunnel);
-        CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
-                    stream_id);
       }
       break;
     case NGHTTP2_GOAWAY:
@@ -727,12 +723,8 @@
     }
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if((data->req.keepon & KEEP_SEND_HOLD) &&
-       (data->req.keepon & KEEP_SEND)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
-      Curl_expire(data, 0, EXPIRE_RUN_NOW);
-      CURL_TRC_CF(data, cf, "[%d] unpausing after win update",
-                  stream_id);
+    if(CURL_WANT_SEND(data)) {
+      drain_tunnel(cf, data, &ctx->tunnel);
     }
     break;
   default:
@@ -909,7 +901,6 @@
 {
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
-  unsigned int i;
   int32_t stream_id = -1;
   size_t nheader;
   CURLcode result;
@@ -920,22 +911,12 @@
   if(result)
     goto out;
 
-  nheader = Curl_dynhds_count(&h2_headers);
-  nva = malloc(sizeof(nghttp2_nv) * nheader);
+  nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
 
-  for(i = 0; i < nheader; ++i) {
-    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
-    nva[i].name = (unsigned char *)e->name;
-    nva[i].namelen = e->namelen;
-    nva[i].value = (unsigned char *)e->value;
-    nva[i].valuelen = e->valuelen;
-    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
-  }
-
   if(read_callback) {
     nghttp2_data_provider data_prd;
 
@@ -1052,7 +1033,7 @@
       if(result)
         goto out;
       h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case H2_TUNNEL_CONNECT:
       /* see that the request is completely sent */
@@ -1071,7 +1052,7 @@
         result = CURLE_OK;
         goto out;
       }
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case H2_TUNNEL_RESPONSE:
       DEBUGASSERT(ts->has_final_response);
@@ -1187,25 +1168,31 @@
   return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
 }
 
-static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf,
-                                        struct Curl_easy *data,
-                                        curl_socket_t *sock)
+static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data,
+                                       struct easy_pollset *ps)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  int bitmap = GETSOCK_BLANK;
-  struct cf_call_data save;
+  curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+  bool want_recv, want_send;
 
-  CF_DATA_SAVE(save, cf, data);
-  sock[0] = Curl_conn_cf_get_socket(cf, data);
-  bitmap |= GETSOCK_READSOCK(0);
+  Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
+  if(ctx->h2 && (want_recv || want_send)) {
+    struct cf_call_data save;
+    bool c_exhaust, s_exhaust;
 
-  /* HTTP/2 layer wants to send data) AND there's a window to send data in */
-  if(nghttp2_session_want_write(ctx->h2) &&
-     nghttp2_session_get_remote_window_size(ctx->h2))
-    bitmap |= GETSOCK_WRITESOCK(0);
+    CF_DATA_SAVE(save, cf, data);
+    c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2);
+    s_exhaust = ctx->tunnel.stream_id >= 0 &&
+                !nghttp2_session_get_stream_remote_window_size(
+                   ctx->h2, ctx->tunnel.stream_id);
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                (!c_exhaust && nghttp2_session_want_write(ctx->h2));
 
-  CF_DATA_RESTORE(cf, save);
-  return bitmap;
+    Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CF_DATA_RESTORE(cf, save);
+  }
 }
 
 static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf,
@@ -1542,7 +1529,7 @@
   cf_h2_proxy_connect,
   cf_h2_proxy_close,
   Curl_cf_http_proxy_get_host,
-  cf_h2_proxy_get_select_socks,
+  cf_h2_proxy_adjust_pollset,
   cf_h2_proxy_data_pending,
   cf_h2_proxy_send,
   cf_h2_proxy_recv,
@@ -1560,7 +1547,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c
index 39ac415..c062887 100644
--- a/Utilities/cmcurl/lib/cf-haproxy.c
+++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -125,7 +125,7 @@
     if(result)
       goto out;
     ctx->state = HAPROXY_SEND;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case HAPROXY_SEND:
     len = Curl_dyn_len(&ctx->data_out);
     if(len > 0) {
@@ -141,7 +141,7 @@
       }
     }
     ctx->state = HAPROXY_DONE;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   default:
     Curl_dyn_free(&ctx->data_out);
     break;
@@ -171,23 +171,17 @@
     cf->next->cft->do_close(cf->next, data);
 }
 
-static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
-                                       struct Curl_easy *data,
-                                       curl_socket_t *socks)
+static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct easy_pollset *ps)
 {
-  int fds;
-
-  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  if(!fds && cf->next->connected && !cf->connected) {
+  if(cf->next->connected && !cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are sending. */
-    socks[0] = Curl_conn_cf_get_socket(cf, data);
-    return GETSOCK_WRITESOCK(0);
+    Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
   }
-  return fds;
 }
 
-
 struct Curl_cftype Curl_cft_haproxy = {
   "HAPROXY",
   0,
@@ -196,7 +190,7 @@
   cf_haproxy_connect,
   cf_haproxy_close,
   Curl_cf_def_get_host,
-  cf_haproxy_get_select_socks,
+  cf_haproxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -214,7 +208,7 @@
   CURLcode result;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c
index be54aec..b23fa05 100644
--- a/Utilities/cmcurl/lib/cf-https-connect.c
+++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -188,9 +188,6 @@
 #endif
     infof(data, "using HTTP/2");
     break;
-  case CURL_HTTP_VERSION_1_1:
-    infof(data, "using HTTP/1.1");
-    break;
   default:
     infof(data, "using HTTP/1.x");
     break;
@@ -269,7 +266,7 @@
       cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
                        cf->conn->transport);
     ctx->state = CF_HC_CONNECT;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
 
   case CF_HC_CONNECT:
     if(cf_hc_baller_is_active(&ctx->h3_baller)) {
@@ -325,42 +322,25 @@
   return result;
 }
 
-static int cf_hc_get_select_socks(struct Curl_cfilter *cf,
+static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  curl_socket_t *socks)
+                                  struct easy_pollset *ps)
 {
-  struct cf_hc_ctx *ctx = cf->ctx;
-  size_t i, j, s;
-  int brc, rc = GETSOCK_BLANK;
-  curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE];
-  struct cf_hc_baller *ballers[2];
+  if(!cf->connected) {
+    struct cf_hc_ctx *ctx = cf->ctx;
+    struct cf_hc_baller *ballers[2];
+    size_t i;
 
-  if(cf->connected)
-    return cf->next->cft->get_select_socks(cf->next, data, socks);
-
-  ballers[0] = &ctx->h3_baller;
-  ballers[1] = &ctx->h21_baller;
-  for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
-    struct cf_hc_baller *b = ballers[i];
-    if(!cf_hc_baller_is_active(b))
-      continue;
-    brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
-    CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc);
-    if(!brc)
-      continue;
-    for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
-      if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) {
-        socks[s] = bsocks[j];
-        if(brc & GETSOCK_WRITESOCK(j))
-          rc |= GETSOCK_WRITESOCK(s);
-        if(brc & GETSOCK_READSOCK(j))
-          rc |= GETSOCK_READSOCK(s);
-        s++;
-      }
+    ballers[0] = &ctx->h3_baller;
+    ballers[1] = &ctx->h21_baller;
+    for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
+      struct cf_hc_baller *b = ballers[i];
+      if(!cf_hc_baller_is_active(b))
+        continue;
+      Curl_conn_cf_adjust_pollset(b->cf, data, ps);
     }
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
   }
-  CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc);
-  return rc;
 }
 
 static bool cf_hc_data_pending(struct Curl_cfilter *cf,
@@ -455,7 +435,7 @@
   cf_hc_connect,
   cf_hc_close,
   Curl_cf_def_get_host,
-  cf_hc_get_select_socks,
+  cf_hc_adjust_pollset,
   cf_hc_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -475,7 +455,7 @@
   CURLcode result = CURLE_OK;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
index ce3f9e9..742902f 100644
--- a/Utilities/cmcurl/lib/cf-socket.c
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -81,7 +81,7 @@
 #include "memdebug.h"
 
 
-#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(WIN32)
+#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
 /* It makes support for IPv4-mapped IPv6 addresses.
  * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
  * Windows Vista and later: default is on;
@@ -102,11 +102,7 @@
 #if defined(TCP_NODELAY)
   curl_socklen_t onoff = (curl_socklen_t) 1;
   int level = IPPROTO_TCP;
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
   char buffer[STRERROR_LEN];
-#else
-  (void) data;
-#endif
 
   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
                 sizeof(onoff)) < 0)
@@ -127,6 +123,7 @@
                       curl_socket_t sockfd)
 {
   int onoff = 1;
+  (void)data;
   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
                 sizeof(onoff)) < 0) {
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -140,14 +137,14 @@
 #define nosigpipe(x,y) Curl_nop_stmt
 #endif
 
-#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
+#if defined(__DragonFly__) || defined(USE_WINSOCK)
 /* DragonFlyBSD and Windows use millisecond units */
 #define KEEPALIVE_FACTOR(x) (x *= 1000)
 #else
 #define KEEPALIVE_FACTOR(x)
 #endif
 
-#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
+#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
 #define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
 
 struct tcp_keepalive {
@@ -166,7 +163,9 @@
   /* only set IDLE and INTVL if setting KEEPALIVE is successful */
   if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
         (void *)&optval, sizeof(optval)) < 0) {
-    infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
+    infof(data, "Failed to set SO_KEEPALIVE on fd "
+          "%" CURL_FORMAT_SOCKET_T ": errno %d",
+          sockfd, SOCKERRNO);
   }
   else {
 #if defined(SIO_KEEPALIVE_VALS)
@@ -181,8 +180,9 @@
     vals.keepaliveinterval = optval;
     if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
                 NULL, 0, &dummy, NULL, NULL) != 0) {
-      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
-            (int)sockfd, WSAGetLastError());
+      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
+                  "%" CURL_FORMAT_SOCKET_T ": errno %d",
+                  sockfd, SOCKERRNO);
     }
 #else
 #ifdef TCP_KEEPIDLE
@@ -190,7 +190,9 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
+      infof(data, "Failed to set TCP_KEEPIDLE on fd "
+            "%" CURL_FORMAT_SOCKET_T ": errno %d",
+            sockfd, SOCKERRNO);
     }
 #elif defined(TCP_KEEPALIVE)
     /* Mac OS X style */
@@ -198,7 +200,9 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
       (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
+      infof(data, "Failed to set TCP_KEEPALIVE on fd "
+            "%" CURL_FORMAT_SOCKET_T ": errno %d",
+            sockfd, SOCKERRNO);
     }
 #endif
 #ifdef TCP_KEEPINTVL
@@ -206,7 +210,9 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
+      infof(data, "Failed to set TCP_KEEPINTVL on fd "
+            "%" CURL_FORMAT_SOCKET_T ": errno %d",
+            sockfd, SOCKERRNO);
     }
 #endif
 #endif
@@ -662,7 +668,7 @@
   int err = 0;
   curl_socklen_t errSize = sizeof(err);
 
-#ifdef WIN32
+#ifdef _WIN32
   /*
    * In October 2003 we effectively nullified this function on Windows due to
    * problems with it using all CPU in multi-threaded cases.
@@ -786,6 +792,7 @@
 #endif
   BIT(got_first_byte);               /* if first byte was received */
   BIT(accepted);                     /* socket was accepted, not connected */
+  BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
   BIT(active);
   BIT(buffer_recv);
 };
@@ -883,34 +890,14 @@
   struct cf_socket_ctx *ctx = cf->ctx;
 
   if(ctx && CURL_SOCKET_BAD != ctx->sock) {
-    if(ctx->active) {
-      /* We share our socket at cf->conn->sock[cf->sockindex] when active.
-       * If it is no longer there, someone has stolen (and hopefully
-       * closed it) and we just forget about it.
-       */
-      if(ctx->sock == cf->conn->sock[cf->sockindex]) {
-        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                    ", active)", ctx->sock);
-        socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
-        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
-      }
-      else {
-        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                    ") no longer at conn->sock[], discarding", ctx->sock);
-        /* TODO: we do not want this to happen. Need to check which
-         * code is messing with conn->sock[cf->sockindex] */
-      }
-      ctx->sock = CURL_SOCKET_BAD;
-      if(cf->sockindex == FIRSTSOCKET)
-        cf->conn->remote_addr = NULL;
-    }
-    else {
-      /* this is our local socket, we did never publish it */
-      CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                  ", not active)", ctx->sock);
-      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
-      ctx->sock = CURL_SOCKET_BAD;
-    }
+    CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+                ")", ctx->sock);
+    if(ctx->sock == cf->conn->sock[cf->sockindex])
+      cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+    socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
+    ctx->sock = CURL_SOCKET_BAD;
+    if(ctx->active && cf->sockindex == FIRSTSOCKET)
+      cf->conn->remote_addr = NULL;
     Curl_bufq_reset(&ctx->recvbuf);
     ctx->active = FALSE;
     ctx->buffer_recv = FALSE;
@@ -1006,20 +993,14 @@
   if(result)
     goto out;
 
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  {
-    const char *ipmsg;
 #ifdef ENABLE_IPV6
-    if(ctx->addr.family == AF_INET6) {
-      set_ipv6_v6only(ctx->sock, 0);
-      ipmsg = "  Trying [%s]:%d...";
-    }
-    else
-#endif
-      ipmsg = "  Trying %s:%d...";
-    infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+  if(ctx->addr.family == AF_INET6) {
+    set_ipv6_v6only(ctx->sock, 0);
+    infof(data, "  Trying [%s]:%d...", ctx->r_ip, ctx->r_port);
   }
+  else
 #endif
+    infof(data, "  Trying %s:%d...", ctx->r_ip, ctx->r_port);
 
 #ifdef ENABLE_IPV6
   is_tcp = (ctx->addr.family == AF_INET
@@ -1077,7 +1058,7 @@
 
   /* set socket non-blocking */
   (void)curlx_nonblock(ctx->sock, TRUE);
-
+  ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
 out:
   if(result) {
     if(ctx->sock != CURL_SOCKET_BAD) {
@@ -1169,6 +1150,7 @@
 
   *done = FALSE; /* a very negative world view is best */
   if(ctx->sock == CURL_SOCKET_BAD) {
+    int error;
 
     result = cf_socket_open(cf, data);
     if(result)
@@ -1181,8 +1163,12 @@
 
     /* Connect TCP socket */
     rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
+    error = SOCKERRNO;
+    set_local_ip(cf, data);
+    CURL_TRC_CF(data, cf, "local address %s port %d...",
+                ctx->l_ip, ctx->l_port);
     if(-1 == rc) {
-      result = socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+      result = socket_connect_result(data, ctx->r_ip, error);
       goto out;
     }
   }
@@ -1220,13 +1206,14 @@
 out:
   if(result) {
     if(ctx->error) {
+      set_local_ip(cf, data);
       data->state.os_errno = ctx->error;
       SET_SOCKERRNO(ctx->error);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       {
         char buffer[STRERROR_LEN];
-        infof(data, "connect to %s port %u failed: %s",
-              ctx->r_ip, ctx->r_port,
+        infof(data, "connect to %s port %u from %s port %d failed: %s",
+              ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port,
               Curl_strerror(ctx->error, buffer, sizeof(buffer)));
       }
 #endif
@@ -1252,20 +1239,22 @@
   *pport = cf->conn->port;
 }
 
-static int cf_socket_get_select_socks(struct Curl_cfilter *cf,
+static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
-                                      curl_socket_t *socks)
+                                      struct easy_pollset *ps)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
-  int rc = GETSOCK_BLANK;
 
-  (void)data;
-  if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) {
-    socks[0] = ctx->sock;
-    rc |= GETSOCK_WRITESOCK(0);
+  if(ctx->sock != CURL_SOCKET_BAD) {
+    if(!cf->connected) {
+      Curl_pollset_set_out_only(data, ps, ctx->sock);
+      CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num);
+    }
+    else if(!ctx->active) {
+      Curl_pollset_add_in(data, ps, ctx->sock);
+      CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num);
+    }
   }
-
-  return rc;
 }
 
 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
@@ -1447,36 +1436,11 @@
 static void conn_set_primary_ip(struct Curl_cfilter *cf,
                                 struct Curl_easy *data)
 {
-#ifdef HAVE_GETPEERNAME
   struct cf_socket_ctx *ctx = cf->ctx;
-  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
-    /* TFTP does not connect the endpoint: getpeername() failed with errno
-       107: Transport endpoint is not connected */
 
-    char buffer[STRERROR_LEN];
-    struct Curl_sockaddr_storage ssrem;
-    curl_socklen_t plen;
-    int port;
-
-    plen = sizeof(ssrem);
-    memset(&ssrem, 0, plen);
-    if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
-      int error = SOCKERRNO;
-      failf(data, "getpeername() failed with errno %d: %s",
-            error, Curl_strerror(error, buffer, sizeof(buffer)));
-      return;
-    }
-    if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
-                         cf->conn->primary_ip, &port)) {
-      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
-            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-      return;
-    }
-  }
-#else
-  cf->conn->primary_ip[0] = 0;
   (void)data;
-#endif
+  DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip));
+  memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip));
 }
 
 static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -1518,6 +1482,9 @@
   case CF_CTRL_DATA_SETUP:
     Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
     break;
+  case CF_CTRL_FORGET_SOCKET:
+    ctx->sock = CURL_SOCKET_BAD;
+    break;
   }
   return CURLE_OK;
 }
@@ -1589,7 +1556,7 @@
         *when = ctx->first_byte_at;
         break;
       }
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     default:
       *when = ctx->connected_at;
       break;
@@ -1612,7 +1579,7 @@
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1635,7 +1602,7 @@
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_TCP);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1663,10 +1630,17 @@
   /* QUIC needs a connected socket, nonblocking */
   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
 
+#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
+  (void)rc;
+  /* On macOS OpenSSL QUIC fails on connected sockets.
+   * see: <https://github.com/openssl/openssl/issues/23251> */
+#else
   rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
   if(-1 == rc) {
     return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
   }
+  ctx->sock_connected = TRUE;
+#endif
   set_local_ip(cf, data);
   CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
               " connected: [%s:%d] -> [%s:%d]",
@@ -1742,7 +1716,7 @@
   cf_udp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1765,7 +1739,7 @@
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1793,7 +1767,7 @@
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1816,7 +1790,7 @@
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_UNIX);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1857,7 +1831,7 @@
   cf_tcp_accept_connect,
   cf_socket_close,
   cf_socket_get_host,              /* TODO: not accurate */
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1879,7 +1853,7 @@
   Curl_conn_cf_discard_all(data, conn, sockindex);
   DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
 
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h
index 1d40df7..87e0f30 100644
--- a/Utilities/cmcurl/lib/cf-socket.h
+++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -34,23 +34,6 @@
 struct connectdata;
 struct Curl_sockaddr_ex;
 
-#ifndef SIZEOF_CURL_SOCKET_T
-/* configure and cmake check and set the define */
-# ifdef _WIN64
-#  define SIZEOF_CURL_SOCKET_T 8
-# else
-/* default guess */
-#  define SIZEOF_CURL_SOCKET_T 4
-# endif
-#endif
-
-#if SIZEOF_CURL_SOCKET_T < 8
-# define CURL_FORMAT_SOCKET_T "d"
-#else
-# define CURL_FORMAT_SOCKET_T "qd"
-#endif
-
-
 /*
  * The Curl_sockaddr_ex structure is basically libcurl's external API
  * curl_sockaddr structure with enough space available to directly hold any
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index f74eb40..823e90c 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -33,6 +33,7 @@
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "multiif.h"
 #include "progress.h"
+#include "select.h"
 #include "warnless.h"
 
 /* The last 3 #include files should be in this order */
@@ -70,12 +71,14 @@
   }
 }
 
-int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
+void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
-                                 curl_socket_t *socks)
+                                 struct easy_pollset *ps)
 {
-  return cf->next?
-    cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
+  /* NOP */
+  (void)cf;
+  (void)data;
+  (void)ps;
 }
 
 bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
@@ -212,7 +215,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   DEBUGASSERT(cft);
-  cf = calloc(sizeof(*cf), 1);
+  cf = calloc(1, sizeof(*cf));
   if(!cf)
     goto out;
 
@@ -303,15 +306,6 @@
     cf->cft->do_close(cf, data);
 }
 
-int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *socks)
-{
-  if(cf)
-    return cf->cft->get_select_socks(cf, data, socks);
-  return 0;
-}
-
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
 {
@@ -433,22 +427,31 @@
   return FALSE;
 }
 
-int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
-                               curl_socket_t *socks)
+void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
 {
-  struct Curl_cfilter *cf;
+  /* Get the lowest not-connected filter, if there are any */
+  while(cf && !cf->connected && cf->next && !cf->next->connected)
+    cf = cf->next;
+  /* From there on, give all filters a chance to adjust the pollset.
+   * Lower filters are called later, so they may override */
+  while(cf) {
+    cf->cft->adjust_pollset(cf, data, ps);
+    cf = cf->next;
+  }
+}
+
+void Curl_conn_adjust_pollset(struct Curl_easy *data,
+                               struct easy_pollset *ps)
+{
+  int i;
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  cf = data->conn->cfilter[sockindex];
-
-  /* if the next one is not yet connected, that's the one we want */
-  while(cf && cf->next && !cf->next->connected)
-    cf = cf->next;
-  if(cf) {
-    return cf->cft->get_select_socks(cf, data, socks);
+  for(i = 0; i < 2; ++i) {
+    Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps);
   }
-  return GETSOCK_BLANK;
 }
 
 void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
@@ -524,6 +527,18 @@
   return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
 }
 
+void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
+{
+  if(data->conn) {
+    struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+    if(cf)
+      (void)Curl_conn_cf_cntrl(cf, data, TRUE,
+                               CF_CTRL_FORGET_SOCKET, 0, NULL);
+    fake_sclose(data->conn->sock[sockindex]);
+    data->conn->sock[sockindex] = CURL_SOCKET_BAD;
+  }
+}
+
 static CURLcode cf_cntrl_all(struct connectdata *conn,
                              struct Curl_easy *data,
                              bool ignore_result,
@@ -646,3 +661,128 @@
                               &n, NULL) : CURLE_UNKNOWN_OPTION;
   return (result || n <= 0)? 1 : (size_t)n;
 }
+
+
+void Curl_pollset_reset(struct Curl_easy *data,
+                        struct easy_pollset *ps)
+{
+  size_t i;
+  (void)data;
+  memset(ps, 0, sizeof(*ps));
+  for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
+    ps->sockets[i] = CURL_SOCKET_BAD;
+}
+
+/**
+ *
+ */
+void Curl_pollset_change(struct Curl_easy *data,
+                       struct easy_pollset *ps, curl_socket_t sock,
+                       int add_flags, int remove_flags)
+{
+  unsigned int i;
+
+  (void)data;
+  DEBUGASSERT(VALID_SOCK(sock));
+  if(!VALID_SOCK(sock))
+    return;
+
+  DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
+  DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
+  DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */
+  for(i = 0; i < ps->num; ++i) {
+    if(ps->sockets[i] == sock) {
+      ps->actions[i] &= (unsigned char)(~remove_flags);
+      ps->actions[i] |= (unsigned char)add_flags;
+      /* all gone? remove socket */
+      if(!ps->actions[i]) {
+        if((i + 1) < ps->num) {
+          memmove(&ps->sockets[i], &ps->sockets[i + 1],
+                  (ps->num - (i + 1)) * sizeof(ps->sockets[0]));
+          memmove(&ps->actions[i], &ps->actions[i + 1],
+                  (ps->num - (i + 1)) * sizeof(ps->actions[0]));
+        }
+        --ps->num;
+      }
+      return;
+    }
+  }
+  /* not present */
+  if(add_flags) {
+    /* Having more SOCKETS per easy handle than what is defined
+     * is a programming error. This indicates that we need
+     * to raise this limit, making easy_pollset larger.
+     * Since we use this in tight loops, we do not want to make
+     * the pollset dynamic unnecessarily.
+     * The current maximum in practise is HTTP/3 eyeballing where
+     * we have up to 4 sockets involved in connection setup.
+     */
+    DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE);
+    if(i < MAX_SOCKSPEREASYHANDLE) {
+      ps->sockets[i] = sock;
+      ps->actions[i] = (unsigned char)add_flags;
+      ps->num = i + 1;
+    }
+  }
+}
+
+void Curl_pollset_set(struct Curl_easy *data,
+                      struct easy_pollset *ps, curl_socket_t sock,
+                      bool do_in, bool do_out)
+{
+  Curl_pollset_change(data, ps, sock,
+                      (do_in?CURL_POLL_IN:0)|(do_out?CURL_POLL_OUT:0),
+                      (!do_in?CURL_POLL_IN:0)|(!do_out?CURL_POLL_OUT:0));
+}
+
+static void ps_add(struct Curl_easy *data, struct easy_pollset *ps,
+                   int bitmap, curl_socket_t *socks)
+{
+  if(bitmap) {
+    int i;
+    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) {
+      if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) {
+        break;
+      }
+      if(bitmap & GETSOCK_READSOCK(i)) {
+        if(bitmap & GETSOCK_WRITESOCK(i))
+          Curl_pollset_add_inout(data, ps, socks[i]);
+        else
+          /* is READ, since we checked MASK_RW above */
+          Curl_pollset_add_in(data, ps, socks[i]);
+      }
+      else
+        Curl_pollset_add_out(data, ps, socks[i]);
+    }
+  }
+}
+
+void Curl_pollset_add_socks(struct Curl_easy *data,
+                            struct easy_pollset *ps,
+                            int (*get_socks_cb)(struct Curl_easy *data,
+                                                curl_socket_t *socks))
+{
+  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+
+  bitmap = get_socks_cb(data, socks);
+  ps_add(data, ps, bitmap, socks);
+}
+
+void Curl_pollset_check(struct Curl_easy *data,
+                        struct easy_pollset *ps, curl_socket_t sock,
+                        bool *pwant_read, bool *pwant_write)
+{
+  unsigned int i;
+
+  (void)data;
+  DEBUGASSERT(VALID_SOCK(sock));
+  for(i = 0; i < ps->num; ++i) {
+    if(ps->sockets[i] == sock) {
+      *pwant_read = !!(ps->actions[i] & CURL_POLL_IN);
+      *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT);
+      return;
+    }
+  }
+  *pwant_read = *pwant_write = FALSE;
+}
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h
index 2c65264..f838429 100644
--- a/Utilities/cmcurl/lib/cfilters.h
+++ b/Utilities/cmcurl/lib/cfilters.h
@@ -60,14 +60,34 @@
                                   const char **pdisplay_host,
                                   int *pport);
 
-/* Filters may return sockets and fdset flags they are waiting for.
- * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets.
- * @return read/write fdset for index in socks
- *         or GETSOCK_BLANK when nothing to wait on
+struct easy_pollset;
+
+/* Passing in an easy_pollset for monitoring of sockets, let
+ * filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN).
+ * This may add a socket or, in case no actions remain, remove
+ * a socket from the set.
+ *
+ * Filter implementations need to call filters "below" *after* they have
+ * made their adjustments. This allows lower filters to override "upper"
+ * actions. If a "lower" filter is unable to write, it needs to be able
+ * to disallow POLL_OUT.
+ *
+ * A filter without own restrictions/preferences should not modify
+ * the pollset. Filters, whose filter "below" is not connected, should
+ * also do no adjustments.
+ *
+ * Examples: a TLS handshake, while ongoing, might remove POLL_IN
+ * when it needs to write, or vice versa. A HTTP/2 filter might remove
+ * POLL_OUT when a stream window is exhausted and a WINDOW_UPDATE needs
+ * to be received first and add instead POLL_IN.
+ *
+ * @param cf     the filter to ask
+ * @param data   the easy handle the pollset is about
+ * @param ps     the pollset (inout) for the easy handle
  */
-typedef int      Curl_cft_get_select_socks(struct Curl_cfilter *cf,
-                                           struct Curl_easy *data,
-                                           curl_socket_t *socks);
+typedef void     Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          struct easy_pollset *ps);
 
 typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
                                        const struct Curl_easy *data);
@@ -110,6 +130,7 @@
 #define CF_CTRL_DATA_DONE_SEND        8  /* 0          NULL     ignored */
 /* update conn info at connection and data */
 #define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0          NULL     ignored */
+#define CF_CTRL_FORGET_SOCKET    (256+1) /* 0          NULL     ignored */
 
 /**
  * Handle event/control for the filter.
@@ -171,7 +192,7 @@
   Curl_cft_connect *do_connect;           /* establish connection */
   Curl_cft_close *do_close;               /* close conn */
   Curl_cft_get_host *get_host;            /* host filter talks to */
-  Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */
+  Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
   Curl_cft_send *do_send;                 /* send data */
   Curl_cft_recv *do_recv;                 /* receive data */
@@ -200,9 +221,9 @@
 void     Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
                               const char **phost, const char **pdisplay_host,
                               int *pport);
-int      Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      curl_socket_t *socks);
+void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data);
 ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -279,9 +300,6 @@
                               struct Curl_easy *data,
                               bool blocking, bool *done);
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
-int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *socks);
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err);
 ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -364,11 +382,22 @@
 curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
 
 /**
- * Get any select fd flags and the socket filters at chain `sockindex`
- * at connection `conn` might be waiting for.
+ * Tell filters to forget about the socket at sockindex.
  */
-int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
-                               curl_socket_t *socks);
+void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex);
+
+/**
+ * Adjust the pollset for the filter chain startgin at `cf`.
+ */
+void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps);
+
+/**
+ * Adjust pollset from filters installed at transfer's connection.
+ */
+void Curl_conn_adjust_pollset(struct Curl_easy *data,
+                               struct easy_pollset *ps);
 
 /**
  * Receive data through the filter chain at `sockindex` for connection
@@ -468,6 +497,49 @@
                                     int sockindex);
 
 
+void Curl_pollset_reset(struct Curl_easy *data,
+                        struct easy_pollset *ps);
+
+/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for
+ * socket `sock`. If the socket is not already part of the poll set, it
+ * will be added.
+ * If the socket is present and all poll flags are cleared, it will be removed.
+ */
+void Curl_pollset_change(struct Curl_easy *data,
+                         struct easy_pollset *ps, curl_socket_t sock,
+                         int add_flags, int remove_flags);
+
+void Curl_pollset_set(struct Curl_easy *data,
+                      struct easy_pollset *ps, curl_socket_t sock,
+                      bool do_in, bool do_out);
+
+#define Curl_pollset_add_in(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0)
+#define Curl_pollset_add_out(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0)
+#define Curl_pollset_add_inout(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_IN|CURL_POLL_OUT, 0)
+#define Curl_pollset_set_in_only(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_IN, CURL_POLL_OUT)
+#define Curl_pollset_set_out_only(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_OUT, CURL_POLL_IN)
+
+void Curl_pollset_add_socks(struct Curl_easy *data,
+                            struct easy_pollset *ps,
+                            int (*get_socks_cb)(struct Curl_easy *data,
+                                                curl_socket_t *socks));
+
+/**
+ * Check if the pollset, as is, wants to read and/or write regarding
+ * the given socket.
+ */
+void Curl_pollset_check(struct Curl_easy *data,
+                        struct easy_pollset *ps, curl_socket_t sock,
+                        bool *pwant_read, bool *pwant_write);
+
 /**
  * Types and macros used to keep the current easy handle in filter calls,
  * allowing for nested invocations. See #10336.
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index 93d8768..66f18ec 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -107,7 +107,7 @@
   connc->closure_handle = curl_easy_init();
   if(!connc->closure_handle)
     return 1; /* bad */
-  connc->closure_handle->internal = true;
+  connc->closure_handle->state.internal = true;
 
   Curl_hash_init(&connc->hash, size, Curl_hash_str,
                  Curl_str_key_compare, free_bundle_hash_entry);
@@ -243,7 +243,7 @@
   conn->connection_id = connc->next_connection_id++;
   connc->num_conn++;
 
-  DEBUGF(infof(data, "Added connection %ld. "
+  DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". "
                "The cache now contains %zu members",
                conn->connection_id, connc->num_conn));
 
@@ -379,21 +379,26 @@
 bool Curl_conncache_return_conn(struct Curl_easy *data,
                                 struct connectdata *conn)
 {
-  /* data->multi->maxconnects can be negative, deal with it. */
-  size_t maxconnects =
-    (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
-    data->multi->maxconnects;
+  unsigned int maxconnects = !data->multi->maxconnects ?
+    data->multi->num_easy * 4: data->multi->maxconnects;
   struct connectdata *conn_candidate = NULL;
 
   conn->lastused = Curl_now(); /* it was used up until now */
-  if(maxconnects > 0 &&
-     Curl_conncache_size(data) > maxconnects) {
+  if(maxconnects && Curl_conncache_size(data) > maxconnects) {
     infof(data, "Connection cache is full, closing the oldest one");
 
     conn_candidate = Curl_conncache_extract_oldest(data);
     if(conn_candidate) {
-      /* the winner gets the honour of being disconnected */
-      Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
+      /* Use the closure handle for this disconnect so that anything that
+         happens during the disconnect is not stored and associated with the
+         'data' handle which already just finished a transfer and it is
+         important that details from this (unrelated) disconnect does not
+         taint meta-data in the data handle. */
+      struct conncache *connc = data->state.conn_cache;
+      connc->closure_handle->state.buffer = data->state.buffer;
+      connc->closure_handle->set.buffer_size = data->set.buffer_size;
+      Curl_disconnect(connc->closure_handle, conn_candidate,
+                      /* dead_connection */ FALSE);
     }
   }
 
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index c7ba3e2..45743e9 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -84,31 +84,26 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
 
 /*
  * Curl_timeleft() returns the amount of milliseconds left allowed for the
  * transfer/connection. If the value is 0, there's no timeout (ie there's
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
- *
- * If 'nowp' is non-NULL, it points to the current time.
- * 'duringconnect' is FALSE if not during a connect, as then of course the
- * connect timeout is not taken into account!
- *
+ * @param data the transfer to check on
+ * @param nowp timestamp to use for calculdation, NULL to use Curl_now()
+ * @param duringconnect TRUE iff connect timeout is also taken into account.
  * @unittest: 1303
  */
-
-#define TIMEOUT_CONNECT 1
-#define TIMEOUT_MAXTIME 2
-
 timediff_t Curl_timeleft(struct Curl_easy *data,
                          struct curltime *nowp,
                          bool duringconnect)
 {
-  unsigned int timeout_set = 0;
-  timediff_t connect_timeout_ms = 0;
-  timediff_t maxtime_timeout_ms = 0;
-  timediff_t timeout_ms = 0;
+  timediff_t timeleft_ms = 0;
+  timediff_t ctimeleft_ms = 0;
   struct curltime now;
 
   /* The duration of a connect and the total transfer are calculated from two
@@ -116,43 +111,35 @@
      before the connect timeout expires and we must acknowledge whichever
      timeout that is reached first. The total timeout is set per entire
      operation, while the connect timeout is set per connect. */
-
-  if(data->set.timeout > 0) {
-    timeout_set = TIMEOUT_MAXTIME;
-    maxtime_timeout_ms = data->set.timeout;
-  }
-  if(duringconnect) {
-    timeout_set |= TIMEOUT_CONNECT;
-    connect_timeout_ms = (data->set.connecttimeout > 0) ?
-      data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
-  }
-  if(!timeout_set)
-    /* no timeout  */
-    return 0;
+  if(data->set.timeout <= 0 && !duringconnect)
+    return 0; /* no timeout in place or checked, return "no limit" */
 
   if(!nowp) {
     now = Curl_now();
     nowp = &now;
   }
 
-  if(timeout_set & TIMEOUT_MAXTIME) {
-    maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
-    timeout_ms = maxtime_timeout_ms;
+  if(data->set.timeout > 0) {
+    timeleft_ms = data->set.timeout -
+                  Curl_timediff(*nowp, data->progress.t_startop);
+    if(!timeleft_ms)
+      timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
+    if(!duringconnect)
+      return timeleft_ms; /* no connect check, this is it */
   }
 
-  if(timeout_set & TIMEOUT_CONNECT) {
-    connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
-
-    if(!(timeout_set & TIMEOUT_MAXTIME) ||
-       (connect_timeout_ms < maxtime_timeout_ms))
-      timeout_ms = connect_timeout_ms;
+  if(duringconnect) {
+    timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
+      data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
+    ctimeleft_ms = ctimeout_ms -
+                   Curl_timediff(*nowp, data->progress.t_startsingle);
+    if(!ctimeleft_ms)
+      ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
+    if(!timeleft_ms)
+      return ctimeleft_ms; /* no general timeout, this is it */
   }
-
-  if(!timeout_ms)
-    /* avoid returning 0 as that means no timeout! */
-    return -1;
-
-  return timeout_ms;
+  /* return minimal time left or max amount already expired */
+  return (ctimeleft_ms < timeleft_ms)? ctimeleft_ms : timeleft_ms;
 }
 
 /* Copies connection info into the transfer handle to make it available when
@@ -348,6 +335,7 @@
  */
 struct eyeballer {
   const char *name;
+  const struct Curl_addrinfo *first; /* complete address list, not owned */
   const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
   int ai_family;                     /* matching address family only */
   cf_ip_connect_create *cf_create;   /* for creating cf */
@@ -359,9 +347,12 @@
   expire_id timeout_id;              /* ID for Curl_expire() */
   CURLcode result;
   int error;
+  BIT(rewinded);                     /* if we rewinded the addr list */
   BIT(has_started);                  /* attempts have started */
   BIT(is_done);                      /* out of addresses/time */
   BIT(connected);                    /* cf has connected */
+  BIT(inconclusive);                 /* connect was not a hard failure, we
+                                      * might talk to a restarting server */
 };
 
 
@@ -398,7 +389,7 @@
   struct eyeballer *baller;
 
   *pballer = NULL;
-  baller = calloc(1, sizeof(*baller) + 1000);
+  baller = calloc(1, sizeof(*baller));
   if(!baller)
     return CURLE_OUT_OF_MEMORY;
 
@@ -408,7 +399,7 @@
 #endif
                   "ip"));
   baller->cf_create = cf_create;
-  baller->addr = addr;
+  baller->first = baller->addr = addr;
   baller->ai_family = ai_family;
   baller->primary = primary;
   baller->delay_ms = delay_ms;
@@ -438,6 +429,13 @@
   }
 }
 
+static void baller_rewind(struct eyeballer *baller)
+{
+  baller->rewinded = TRUE;
+  baller->addr = baller->first;
+  baller->inconclusive = FALSE;
+}
+
 static void baller_next_addr(struct eyeballer *baller)
 {
   baller->addr = addr_next_match(baller->addr, baller->ai_family);
@@ -528,6 +526,10 @@
 {
   if(cf->sockindex == FIRSTSOCKET) {
     baller_next_addr(baller);
+    /* If we get inconclusive answers from the server(s), we make
+     * a second iteration over the address list */
+    if(!baller->addr && baller->inconclusive && !baller->rewinded)
+      baller_rewind(baller);
     baller_start(cf, data, baller, timeoutms);
   }
   else {
@@ -566,6 +568,8 @@
         baller->result = CURLE_OPERATION_TIMEDOUT;
       }
     }
+    else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
+      baller->inconclusive = TRUE;
   }
   return baller->result;
 }
@@ -595,7 +599,7 @@
   *connected = FALSE; /* a very negative world view is best */
   now = Curl_now();
   ongoing = not_started = 0;
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     if(!baller || baller->is_done)
@@ -656,7 +660,7 @@
   if(not_started > 0) {
     int added = 0;
 
-    for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
       struct eyeballer *baller = ctx->baller[i];
 
       if(!baller || baller->has_started)
@@ -691,13 +695,13 @@
   /* all ballers have failed to connect. */
   CURL_TRC_CF(data, cf, "all eyeballers failed");
   result = CURLE_COULDNT_CONNECT;
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
+    if(!baller)
+      continue;
     CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
-                baller?baller->name:NULL,
-                baller?baller->has_started:0,
-                baller?baller->result:0);
-    if(baller && baller->has_started && baller->result) {
+                baller->name, baller->has_started, baller->result);
+    if(baller->has_started && baller->result) {
       result = baller->result;
       break;
     }
@@ -838,7 +842,7 @@
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(data);
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     baller_free(ctx->baller[i], data);
     ctx->baller[i] = NULL;
   }
@@ -846,35 +850,22 @@
   ctx->winner = NULL;
 }
 
-static int cf_he_get_select_socks(struct Curl_cfilter *cf,
+static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  curl_socket_t *socks)
+                                  struct easy_pollset *ps)
 {
   struct cf_he_ctx *ctx = cf->ctx;
-  size_t i, s;
-  int wrc, rc = GETSOCK_BLANK;
-  curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE];
+  size_t i;
 
-  if(cf->connected)
-    return cf->next->cft->get_select_socks(cf->next, data, socks);
-
-  for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-    if(!baller || !baller->cf)
-      continue;
-
-    wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks);
-    if(wrc) {
-      /* TODO: we assume we get at most one socket back */
-      socks[s] = wsocks[0];
-      if(wrc & GETSOCK_WRITESOCK(0))
-        rc |= GETSOCK_WRITESOCK(s);
-      if(wrc & GETSOCK_READSOCK(0))
-        rc |= GETSOCK_READSOCK(s);
-      s++;
+  if(!cf->connected) {
+    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+      struct eyeballer *baller = ctx->baller[i];
+      if(!baller || !baller->cf)
+        continue;
+      Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
     }
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
   }
-  return rc;
 }
 
 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
@@ -901,7 +892,7 @@
       if(result)
         return result;
       ctx->state = SCFST_WAITING;
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SCFST_WAITING:
       result = is_connected(cf, data, done);
       if(!result && *done) {
@@ -956,7 +947,7 @@
   if(cf->connected)
     return cf->next->cft->has_data_pending(cf->next, data);
 
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
     if(!baller || !baller->cf)
       continue;
@@ -975,7 +966,7 @@
   size_t i;
 
   memset(&tmax, 0, sizeof(tmax));
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     memset(&t, 0, sizeof(t));
@@ -1000,7 +991,7 @@
       int reply_ms = -1;
       size_t i;
 
-      for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+      for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
         struct eyeballer *baller = ctx->baller[i];
         int breply_ms;
 
@@ -1055,7 +1046,7 @@
   cf_he_connect,
   cf_he_close,
   Curl_cf_def_get_host,
-  cf_he_get_select_socks,
+  cf_he_adjust_pollset,
   cf_he_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -1089,7 +1080,7 @@
   (void)data;
   (void)conn;
   *pcf = NULL;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1122,13 +1113,13 @@
 #ifdef ENABLE_QUIC
   { TRNSPRT_QUIC, Curl_cf_quic_create },
 #endif
+#ifndef CURL_DISABLE_TFTP
   { TRNSPRT_UDP, Curl_cf_udp_create },
-  { TRNSPRT_UNIX, Curl_cf_unix_create },
-};
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 #endif
+#ifdef USE_UNIX_SOCKETS
+  { TRNSPRT_UNIX, Curl_cf_unix_create },
+#endif
+};
 
 static cf_ip_connect_create *get_cf_create(int transport)
 {
@@ -1319,7 +1310,7 @@
   cf_setup_connect,
   cf_setup_close,
   Curl_cf_def_get_host,
-  Curl_cf_def_get_select_socks,
+  Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -1340,7 +1331,7 @@
   CURLcode result = CURLE_OK;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index ec4750e..06c161d 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -63,6 +63,9 @@
 
 #ifndef CURL_DISABLE_HTTP
 
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
 #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
 
 
@@ -95,7 +98,7 @@
 
 /* Deflate and gzip writer. */
 struct zlib_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   zlibInitState zlib_init;   /* zlib init state */
   uInt trailerlen;           /* Remaining trailer byte count. */
   z_stream z;                /* State structure for zlib. */
@@ -171,7 +174,7 @@
 }
 
 static CURLcode inflate_stream(struct Curl_easy *data,
-                               struct contenc_writer *writer,
+                               struct Curl_cwriter *writer, int type,
                                zlibInitState started)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
@@ -196,7 +199,7 @@
     return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
 
   /* because the buffer size is fixed, iteratively decompress and transfer to
-     the client via downstream_write function. */
+     the client via next_write function. */
   while(!done) {
     int status;                   /* zlib status */
     done = TRUE;
@@ -217,7 +220,7 @@
     if(z->avail_out != DSIZ) {
       if(status == Z_OK || status == Z_STREAM_END) {
         zp->zlib_init = started;      /* Data started. */
-        result = Curl_unencode_write(data, writer->downstream, decomp,
+        result = Curl_cwriter_write(data, writer->next, type, decomp,
                                      DSIZ - z->avail_out);
         if(result) {
           exit_zlib(data, z, &zp->zlib_init, result);
@@ -274,8 +277,8 @@
 
 
 /* Deflate handler. */
-static CURLcode deflate_init_writer(struct Curl_easy *data,
-                                    struct contenc_writer *writer)
+static CURLcode deflate_do_init(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -290,13 +293,16 @@
   return CURLE_OK;
 }
 
-static CURLcode deflate_unencode_write(struct Curl_easy *data,
-                                       struct contenc_writer *writer,
+static CURLcode deflate_do_write(struct Curl_easy *data,
+                                       struct Curl_cwriter *writer, int type,
                                        const char *buf, size_t nbytes)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   /* Set the compressed input when this function is called */
   z->next_in = (Bytef *) buf;
   z->avail_in = (uInt) nbytes;
@@ -305,11 +311,11 @@
     return process_trailer(data, zp);
 
   /* Now uncompress the data */
-  return inflate_stream(data, writer, ZLIB_INFLATING);
+  return inflate_stream(data, writer, type, ZLIB_INFLATING);
 }
 
-static void deflate_close_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static void deflate_do_close(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -317,19 +323,19 @@
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
 }
 
-static const struct content_encoding deflate_encoding = {
+static const struct Curl_cwtype deflate_encoding = {
   "deflate",
   NULL,
-  deflate_init_writer,
-  deflate_unencode_write,
-  deflate_close_writer,
+  deflate_do_init,
+  deflate_do_write,
+  deflate_do_close,
   sizeof(struct zlib_writer)
 };
 
 
 /* Gzip handler. */
-static CURLcode gzip_init_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static CURLcode gzip_do_init(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -359,11 +365,14 @@
 
 #ifdef OLD_ZLIB_SUPPORT
 /* Skip over the gzip header */
-static enum {
+typedef enum {
   GZIP_OK,
   GZIP_BAD,
   GZIP_UNDERFLOW
-} check_gzip_header(unsigned char const *data, ssize_t len, ssize_t *headerlen)
+} gzip_status;
+
+static gzip_status check_gzip_header(unsigned char const *data, ssize_t len,
+                                     ssize_t *headerlen)
 {
   int method, flags;
   const ssize_t totallen = len;
@@ -441,19 +450,22 @@
 }
 #endif
 
-static CURLcode gzip_unencode_write(struct Curl_easy *data,
-                                    struct contenc_writer *writer,
+static CURLcode gzip_do_write(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer, int type,
                                     const char *buf, size_t nbytes)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
     /* Let zlib handle the gzip decompression entirely */
     z->next_in = (Bytef *) buf;
     z->avail_in = (uInt) nbytes;
     /* Now uncompress the data */
-    return inflate_stream(data, writer, ZLIB_INIT_GZIP);
+    return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
   }
 
 #ifndef OLD_ZLIB_SUPPORT
@@ -565,12 +577,12 @@
   }
 
   /* We've parsed the header, now uncompress the data */
-  return inflate_stream(data, writer, ZLIB_GZIP_INFLATING);
+  return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING);
 #endif
 }
 
-static void gzip_close_writer(struct Curl_easy *data,
-                              struct contenc_writer *writer)
+static void gzip_do_close(struct Curl_easy *data,
+                              struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -578,12 +590,12 @@
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
 }
 
-static const struct content_encoding gzip_encoding = {
+static const struct Curl_cwtype gzip_encoding = {
   "gzip",
   "x-gzip",
-  gzip_init_writer,
-  gzip_unencode_write,
-  gzip_close_writer,
+  gzip_do_init,
+  gzip_do_write,
+  gzip_do_close,
   sizeof(struct zlib_writer)
 };
 
@@ -593,7 +605,7 @@
 #ifdef HAVE_BROTLI
 /* Brotli writer. */
 struct brotli_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   BrotliDecoderState *br;    /* State structure for brotli. */
 };
 
@@ -635,8 +647,8 @@
   return CURLE_WRITE_ERROR;
 }
 
-static CURLcode brotli_init_writer(struct Curl_easy *data,
-                                   struct contenc_writer *writer)
+static CURLcode brotli_do_init(struct Curl_easy *data,
+                                   struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
@@ -645,8 +657,8 @@
   return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
 }
 
-static CURLcode brotli_unencode_write(struct Curl_easy *data,
-                                      struct contenc_writer *writer,
+static CURLcode brotli_do_write(struct Curl_easy *data,
+                                      struct Curl_cwriter *writer, int type,
                                       const char *buf, size_t nbytes)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
@@ -657,6 +669,9 @@
   CURLcode result = CURLE_OK;
   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(!bp->br)
     return CURLE_WRITE_ERROR;  /* Stream already ended. */
 
@@ -670,7 +685,7 @@
     dstleft = DSIZ;
     r = BrotliDecoderDecompressStream(bp->br,
                                       &nbytes, &src, &dstleft, &dst, NULL);
-    result = Curl_unencode_write(data, writer->downstream,
+    result = Curl_cwriter_write(data, writer->next, type,
                                  decomp, DSIZ - dstleft);
     if(result)
       break;
@@ -693,8 +708,8 @@
   return result;
 }
 
-static void brotli_close_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+static void brotli_do_close(struct Curl_easy *data,
+                                struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
 
@@ -706,12 +721,12 @@
   }
 }
 
-static const struct content_encoding brotli_encoding = {
+static const struct Curl_cwtype brotli_encoding = {
   "br",
   NULL,
-  brotli_init_writer,
-  brotli_unencode_write,
-  brotli_close_writer,
+  brotli_do_init,
+  brotli_do_write,
+  brotli_do_close,
   sizeof(struct brotli_writer)
 };
 #endif
@@ -720,13 +735,13 @@
 #ifdef HAVE_ZSTD
 /* Zstd writer. */
 struct zstd_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   ZSTD_DStream *zds;    /* State structure for zstd. */
   void *decomp;
 };
 
-static CURLcode zstd_init_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static CURLcode zstd_do_init(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zstd_writer *zp = (struct zstd_writer *) writer;
 
@@ -737,8 +752,8 @@
   return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
 }
 
-static CURLcode zstd_unencode_write(struct Curl_easy *data,
-                                    struct contenc_writer *writer,
+static CURLcode zstd_do_write(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer, int type,
                                     const char *buf, size_t nbytes)
 {
   CURLcode result = CURLE_OK;
@@ -747,6 +762,9 @@
   ZSTD_outBuffer out;
   size_t errorCode;
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(!zp->decomp) {
     zp->decomp = malloc(DSIZ);
     if(!zp->decomp)
@@ -766,7 +784,7 @@
       return CURLE_BAD_CONTENT_ENCODING;
     }
     if(out.pos > 0) {
-      result = Curl_unencode_write(data, writer->downstream,
+      result = Curl_cwriter_write(data, writer->next, type,
                                    zp->decomp, out.pos);
       if(result)
         break;
@@ -778,8 +796,8 @@
   return result;
 }
 
-static void zstd_close_writer(struct Curl_easy *data,
-                              struct contenc_writer *writer)
+static void zstd_do_close(struct Curl_easy *data,
+                              struct Curl_cwriter *writer)
 {
   struct zstd_writer *zp = (struct zstd_writer *) writer;
 
@@ -795,52 +813,30 @@
   }
 }
 
-static const struct content_encoding zstd_encoding = {
+static const struct Curl_cwtype zstd_encoding = {
   "zstd",
   NULL,
-  zstd_init_writer,
-  zstd_unencode_write,
-  zstd_close_writer,
+  zstd_do_init,
+  zstd_do_write,
+  zstd_do_close,
   sizeof(struct zstd_writer)
 };
 #endif
 
 
 /* Identity handler. */
-static CURLcode identity_init_writer(struct Curl_easy *data,
-                                     struct contenc_writer *writer)
-{
-  (void)data;
-  (void)writer;
-  return CURLE_OK;
-}
-
-static CURLcode identity_unencode_write(struct Curl_easy *data,
-                                        struct contenc_writer *writer,
-                                        const char *buf, size_t nbytes)
-{
-  return Curl_unencode_write(data, writer->downstream, buf, nbytes);
-}
-
-static void identity_close_writer(struct Curl_easy *data,
-                                  struct contenc_writer *writer)
-{
-  (void) data;
-  (void) writer;
-}
-
-static const struct content_encoding identity_encoding = {
+static const struct Curl_cwtype identity_encoding = {
   "identity",
   "none",
-  identity_init_writer,
-  identity_unencode_write,
-  identity_close_writer,
-  sizeof(struct contenc_writer)
+  Curl_cwriter_def_init,
+  Curl_cwriter_def_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
 };
 
 
-/* supported content encodings table. */
-static const struct content_encoding * const encodings[] = {
+/* supported general content decoders. */
+static const struct Curl_cwtype * const general_unencoders[] = {
   &identity_encoding,
 #ifdef HAVE_LIBZ
   &deflate_encoding,
@@ -855,28 +851,39 @@
   NULL
 };
 
+/* supported content decoders only for transfer encodings */
+static const struct Curl_cwtype * const transfer_unencoders[] = {
+#ifndef CURL_DISABLE_HTTP
+  &Curl_httpchunk_unencoder,
+#endif
+  NULL
+};
 
-/* Return a list of comma-separated names of supported encodings. */
-char *Curl_all_content_encodings(void)
+/* Provide a list of comma-separated names of supported encodings.
+*/
+void Curl_all_content_encodings(char *buf, size_t blen)
 {
   size_t len = 0;
-  const struct content_encoding * const *cep;
-  const struct content_encoding *ce;
-  char *ace;
+  const struct Curl_cwtype * const *cep;
+  const struct Curl_cwtype *ce;
 
-  for(cep = encodings; *cep; cep++) {
+  DEBUGASSERT(buf);
+  DEBUGASSERT(blen);
+  buf[0] = 0;
+
+  for(cep = general_unencoders; *cep; cep++) {
     ce = *cep;
     if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
       len += strlen(ce->name) + 2;
   }
 
-  if(!len)
-    return strdup(CONTENT_ENCODING_DEFAULT);
-
-  ace = malloc(len);
-  if(ace) {
-    char *p = ace;
-    for(cep = encodings; *cep; cep++) {
+  if(!len) {
+    if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))
+      strcpy(buf, CONTENT_ENCODING_DEFAULT);
+  }
+  else if(blen > len) {
+    char *p = buf;
+    for(cep = general_unencoders; *cep; cep++) {
       ce = *cep;
       if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
         strcpy(p, ce->name);
@@ -887,75 +894,71 @@
     }
     p[-2] = '\0';
   }
-
-  return ace;
 }
 
-
 /* Deferred error dummy writer. */
-static CURLcode error_init_writer(struct Curl_easy *data,
-                                  struct contenc_writer *writer)
+static CURLcode error_do_init(struct Curl_easy *data,
+                                  struct Curl_cwriter *writer)
 {
   (void)data;
   (void)writer;
   return CURLE_OK;
 }
 
-static CURLcode error_unencode_write(struct Curl_easy *data,
-                                     struct contenc_writer *writer,
+static CURLcode error_do_write(struct Curl_easy *data,
+                                     struct Curl_cwriter *writer, int type,
                                      const char *buf, size_t nbytes)
 {
-  char *all = Curl_all_content_encodings();
+  char all[256];
+  (void)Curl_all_content_encodings(all, sizeof(all));
 
   (void) writer;
   (void) buf;
   (void) nbytes;
 
-  if(!all)
-    return CURLE_OUT_OF_MEMORY;
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   failf(data, "Unrecognized content encoding type. "
         "libcurl understands %s content encodings.", all);
-  free(all);
   return CURLE_BAD_CONTENT_ENCODING;
 }
 
-static void error_close_writer(struct Curl_easy *data,
-                               struct contenc_writer *writer)
+static void error_do_close(struct Curl_easy *data,
+                               struct Curl_cwriter *writer)
 {
   (void) data;
   (void) writer;
 }
 
-static const struct content_encoding error_encoding = {
+static const struct Curl_cwtype error_writer = {
+  "ce-error",
   NULL,
-  NULL,
-  error_init_writer,
-  error_unencode_write,
-  error_close_writer,
-  sizeof(struct contenc_writer)
+  error_do_init,
+  error_do_write,
+  error_do_close,
+  sizeof(struct Curl_cwriter)
 };
 
-/* Write data using an unencoding writer stack. "nbytes" is not
-   allowed to be 0. */
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes)
-{
-  if(!nbytes)
-    return CURLE_OK;
-  if(!writer)
-    return CURLE_WRITE_ERROR;
-  return writer->handler->unencode_write(data, writer, buf, nbytes);
-}
-
 /* Find the content encoding by name. */
-static const struct content_encoding *find_encoding(const char *name,
-                                                    size_t len)
+static const struct Curl_cwtype *find_unencode_writer(const char *name,
+                                                      size_t len,
+                                                      Curl_cwriter_phase phase)
 {
-  const struct content_encoding * const *cep;
+  const struct Curl_cwtype * const *cep;
 
-  for(cep = encodings; *cep; cep++) {
-    const struct content_encoding *ce = *cep;
+  if(phase == CURL_CW_TRANSFER_DECODE) {
+    for(cep = transfer_unencoders; *cep; cep++) {
+      const struct Curl_cwtype *ce = *cep;
+      if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
+         (ce->alias && strncasecompare(name, ce->alias, len)
+                    && !ce->alias[len]))
+        return ce;
+    }
+  }
+  /* look among the general decoders */
+  for(cep = general_unencoders; *cep; cep++) {
+    const struct Curl_cwtype *ce = *cep;
     if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
        (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
       return ce;
@@ -968,8 +971,8 @@
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer)
 {
-  struct SingleRequest *k = &data->req;
-  unsigned int order = is_transfer? 2: 1;
+  Curl_cwriter_phase phase = is_transfer?
+                             CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE;
   CURLcode result;
 
   do {
@@ -986,29 +989,36 @@
       if(!ISSPACE(*enclist))
         namelen = enclist - name + 1;
 
-    /* Special case: chunked encoding is handled at the reader level. */
-    if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) {
-      k->chunk = TRUE;             /* chunks coming our way. */
-      Curl_httpchunk_init(data);   /* init our chunky engine. */
-    }
-    else if(namelen) {
-      const struct content_encoding *encoding;
-      struct contenc_writer *writer;
-      if(is_transfer && !data->set.http_transfer_encoding)
+    if(namelen) {
+      const struct Curl_cwtype *cwt;
+      struct Curl_cwriter *writer;
+
+      /* if we skip the decoding in this phase, do not look further.
+       * Exception is "chunked" transfer-encoding which always must happen */
+      if((is_transfer && !data->set.http_transfer_encoding &&
+          (namelen != 7 || !strncasecompare(name, "chunked", 7))) ||
+         (!is_transfer && data->set.http_ce_skip)) {
         /* not requested, ignore */
         return CURLE_OK;
+      }
 
-      encoding = find_encoding(name, namelen);
-      if(!encoding)
-        encoding = &error_encoding;  /* Defer error at stack use. */
+      if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
+        failf(data, "Reject response due to more than %u content encodings",
+              MAX_ENCODE_STACK);
+        return CURLE_BAD_CONTENT_ENCODING;
+      }
 
-      result = Curl_client_create_writer(&writer, data, encoding, order);
+      cwt = find_unencode_writer(name, namelen, phase);
+      if(!cwt)
+        cwt = &error_writer;  /* Defer error at use. */
+
+      result = Curl_cwriter_create(&writer, data, cwt, phase);
       if(result)
         return result;
 
-      result = Curl_client_add_writer(data, writer);
+      result = Curl_cwriter_add(data, writer);
       if(result) {
-        Curl_client_free_writer(data, writer);
+        Curl_cwriter_free(data, writer);
         return result;
       }
     }
@@ -1028,20 +1038,15 @@
   return CURLE_NOT_BUILT_IN;
 }
 
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes)
+void Curl_all_content_encodings(char *buf, size_t blen)
 {
-  (void) data;
-  (void) writer;
-  (void) buf;
-  (void) nbytes;
-  return CURLE_NOT_BUILT_IN;
+  DEBUGASSERT(buf);
+  DEBUGASSERT(blen);
+  if(blen < sizeof(CONTENT_ENCODING_DEFAULT))
+    buf[0] = 0;
+  else
+    strcpy(buf, CONTENT_ENCODING_DEFAULT);
 }
 
-char *Curl_all_content_encodings(void)
-{
-  return strdup(CONTENT_ENCODING_DEFAULT);  /* Satisfy caller. */
-}
 
 #endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h
index ef7930c..1addf23 100644
--- a/Utilities/cmcurl/lib/content_encoding.h
+++ b/Utilities/cmcurl/lib/content_encoding.h
@@ -25,15 +25,10 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-struct contenc_writer;
+struct Curl_cwriter;
 
-char *Curl_all_content_encodings(void);
+void Curl_all_content_encodings(char *buf, size_t blen);
 
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer);
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes);
-void Curl_unencode_cleanup(struct Curl_easy *data);
-
 #endif /* HEADER_CURL_CONTENT_ENCODING_H */
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index af01203..dc319b6 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -330,7 +330,7 @@
  */
 void Curl_cookie_loadfiles(struct Curl_easy *data)
 {
-  struct curl_slist *list = data->set.cookielist;
+  struct curl_slist *list = data->state.cookielist;
   if(list) {
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
     while(list) {
@@ -365,9 +365,7 @@
   DEBUGASSERT(newstr);
   DEBUGASSERT(str);
   free(*str);
-  *str = Curl_memdup(newstr, len + 1);
-  if(*str)
-    (*str)[len] = 0;
+  *str = Curl_memdup0(newstr, len);
 }
 
 /*
@@ -823,10 +821,8 @@
         endslash = memrchr(path, '/', (queryp - path));
       if(endslash) {
         size_t pathlen = (endslash-path + 1); /* include end slash */
-        co->path = malloc(pathlen + 1); /* one extra for the zero byte */
+        co->path = Curl_memdup0(path, pathlen);
         if(co->path) {
-          memcpy(co->path, path, pathlen);
-          co->path[pathlen] = 0; /* null-terminate */
           co->spath = sanitize_cookie_path(co->path);
           if(!co->spath)
             badcookie = TRUE; /* out of memory bad */
@@ -929,7 +925,7 @@
         if(!co->spath)
           badcookie = TRUE;
         fields++; /* add a field and fall down to secure */
-        /* FALLTHROUGH */
+        FALLTHROUGH();
       case 3:
         co->secure = FALSE;
         if(strcasecompare(ptr, "TRUE")) {
@@ -1029,15 +1025,23 @@
    * dereference it.
    */
   if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
-    const psl_ctx_t *psl = Curl_psl_use(data);
-    int acceptable;
-
-    if(psl) {
-      acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
-      Curl_psl_release(data);
+    bool acceptable = FALSE;
+    char lcase[256];
+    char lcookie[256];
+    size_t dlen = strlen(domain);
+    size_t clen = strlen(co->domain);
+    if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
+      const psl_ctx_t *psl = Curl_psl_use(data);
+      if(psl) {
+        /* the PSL check requires lowercase domain name and pattern */
+        Curl_strntolower(lcase, domain, dlen + 1);
+        Curl_strntolower(lcookie, co->domain, clen + 1);
+        acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
+        Curl_psl_release(data);
+      }
+      else
+        acceptable = !bad_domain(domain, strlen(domain));
     }
-    else
-      acceptable = !bad_domain(domain, strlen(domain));
 
     if(!acceptable) {
       infof(data, "cookie '%s' dropped, domain '%s' must not "
@@ -1223,7 +1227,7 @@
 
   if(data) {
     FILE *fp = NULL;
-    if(file) {
+    if(file && *file) {
       if(!strcmp(file, "-"))
         fp = stdin;
       else {
@@ -1347,7 +1351,7 @@
 
 static struct Cookie *dup_cookie(struct Cookie *src)
 {
-  struct Cookie *d = calloc(sizeof(struct Cookie), 1);
+  struct Cookie *d = calloc(1, sizeof(struct Cookie));
   if(d) {
     CLONE(domain);
     CLONE(path);
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index d4bb274..98b787f 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -67,9 +67,15 @@
 /* disables FTP */
 #cmakedefine CURL_DISABLE_FTP 1
 
+/* disables curl_easy_options API for existing options to curl_easy_setopt */
+#cmakedefine CURL_DISABLE_GETOPTIONS 1
+
 /* disables GOPHER */
 #cmakedefine CURL_DISABLE_GOPHER 1
 
+/* disables headers-api support */
+#cmakedefine CURL_DISABLE_HEADERS_API 1
+
 /* disables HSTS support */
 #cmakedefine CURL_DISABLE_HSTS 1
 
@@ -91,6 +97,9 @@
 /* disables MIME support */
 #cmakedefine CURL_DISABLE_MIME 1
 
+/* disables local binding support */
+#cmakedefine CURL_DISABLE_BINDLOCAL 1
+
 /* disables MQTT */
 #cmakedefine CURL_DISABLE_MQTT 1
 
@@ -161,9 +170,6 @@
 /* Define to 1 if you have _Atomic support. */
 #cmakedefine HAVE_ATOMIC 1
 
-/* Define to 1 if you have the `fchmod' function. */
-#cmakedefine HAVE_FCHMOD 1
-
 /* Define to 1 if you have the `fnmatch' function. */
 #cmakedefine HAVE_FNMATCH 1
 
@@ -201,6 +207,9 @@
 /* Define to 1 if you have the fseeko function. */
 #cmakedefine HAVE_FSEEKO 1
 
+/* Define to 1 if you have the fseeko declaration. */
+#cmakedefine HAVE_DECL_FSEEKO 1
+
 /* Define to 1 if you have the _fseeki64 function. */
 #cmakedefine HAVE__FSEEKI64 1
 
@@ -282,12 +291,6 @@
 /* if you have the GNU gssapi libraries */
 #cmakedefine HAVE_GSSGNU 1
 
-/* if you have the Heimdal gssapi libraries */
-#cmakedefine HAVE_GSSHEIMDAL 1
-
-/* if you have the MIT gssapi libraries */
-#cmakedefine HAVE_GSSMIT 1
-
 /* Define to 1 if you have the `idna_strerror' function. */
 #cmakedefine HAVE_IDNA_STRERROR 1
 
@@ -306,9 +309,6 @@
 /* Define to 1 if symbol `ADDRESS_FAMILY' exists */
 #cmakedefine HAVE_ADDRESS_FAMILY 1
 
-/* Define to 1 if you have the <inttypes.h> header file. */
-#cmakedefine HAVE_INTTYPES_H 1
-
 /* Define to 1 if you have the ioctlsocket function. */
 #cmakedefine HAVE_IOCTLSOCKET 1
 
@@ -492,9 +492,6 @@
 /* Define to 1 if you have the <stdbool.h> header file. */
 #cmakedefine HAVE_STDBOOL_H 1
 
-/* Define to 1 if you have the <stdint.h> header file. */
-#cmakedefine HAVE_STDINT_H 1
-
 /* Define to 1 if you have the strcasecmp function. */
 #cmakedefine HAVE_STRCASECMP 1
 
@@ -591,24 +588,9 @@
 /* Define to 1 if you have the <utime.h> header file. */
 #cmakedefine HAVE_UTIME_H 1
 
-/* Define to 1 if compiler supports C99 variadic macro style. */
-#cmakedefine HAVE_VARIADIC_MACROS_C99 1
-
-/* Define to 1 if compiler supports old gcc variadic macro style. */
-#cmakedefine HAVE_VARIADIC_MACROS_GCC 1
-
-/* Define to 1 if you have the windows.h header file. */
-#cmakedefine HAVE_WINDOWS_H 1
-
-/* Define to 1 if you have the winsock2.h header file. */
-#cmakedefine HAVE_WINSOCK2_H 1
-
 /* Define this symbol if your OS supports changing the contents of argv */
 #cmakedefine HAVE_WRITABLE_ARGV 1
 
-/* Define to 1 if you have the ws2tcpip.h header file. */
-#cmakedefine HAVE_WS2TCPIP_H 1
-
 /* Define to 1 if you need the lber.h header file even with ldap.h */
 #cmakedefine NEED_LBER_H 1
 
@@ -725,9 +707,6 @@
 /* if libPSL is in use */
 #cmakedefine USE_LIBPSL 1
 
-/* If you want to build curl with the built-in manual */
-#cmakedefine USE_MANUAL 1
-
 /* if you want to use OpenLDAP code instead of legacy ldap implementation */
 #cmakedefine USE_OPENLDAP 1
 
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h
index 2ea03dd..7a5387a 100644
--- a/Utilities/cmcurl/lib/curl_hmac.h
+++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -25,7 +25,8 @@
  ***************************************************************************/
 
 #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI))         \
-  || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH)
+  || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH)   \
+  || defined(USE_LIBSSH2)
 
 #include <curl/curl.h>
 
diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h
index b8c46d7..714ad71 100644
--- a/Utilities/cmcurl/lib/curl_memory.h
+++ b/Utilities/cmcurl/lib/curl_memory.h
@@ -68,7 +68,7 @@
 #undef send
 #undef recv
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    undef _wcsdup
@@ -134,7 +134,7 @@
 extern curl_realloc_callback Curl_crealloc;
 extern curl_strdup_callback Curl_cstrdup;
 extern curl_calloc_callback Curl_ccalloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 extern curl_wcsdup_callback Curl_cwcsdup;
 #endif
 
@@ -160,7 +160,7 @@
 #undef free
 #define free(ptr) Curl_cfree(ptr)
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    define wcsdup(ptr) Curl_cwcsdup(ptr)
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c
index 522ea34..ff21098 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.c
+++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -32,7 +32,7 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 #include "curl_multibyte.h"
 
@@ -84,7 +84,7 @@
   return str_utf8;
 }
 
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 #if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES)
 
diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h
index ddac1f6..8b9ac71 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.h
+++ b/Utilities/cmcurl/lib/curl_multibyte.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
  /*
   * MultiByte conversions using Windows kernel32 library.
@@ -33,7 +33,7 @@
 
 wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8);
 char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 /*
  * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8()
@@ -54,7 +54,7 @@
  * ensure that the curl memdebug override macros do not replace them.
  */
 
-#if defined(UNICODE) && defined(WIN32)
+#if defined(UNICODE) && defined(_WIN32)
 
 #define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr))
 #define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr))
@@ -78,7 +78,7 @@
   const unsigned char *const_tbyte_ptr;
 } xcharp_u;
 
-#endif /* UNICODE && WIN32 */
+#endif /* UNICODE && _WIN32 */
 
 #define curlx_unicodefree(ptr)                          \
   do {                                                  \
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index cc0ed91..6f6d75c 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -111,6 +111,7 @@
 #  include <wincrypt.h>
 #else
 #  error "Can't compile NTLM support without a crypto library with DES."
+#  define CURL_NTLM_NOT_SUPPORTED
 #endif
 
 #include "urldata.h"
@@ -130,6 +131,7 @@
 #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
 #define NTLMv2_BLOB_LEN       (44 -16 + ntlm->target_info_len + 4)
 
+#if !defined(CURL_NTLM_NOT_SUPPORTED)
 /*
 * Turns a 56-bit key into being 64-bit wide.
 */
@@ -144,6 +146,7 @@
   key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
   key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
 }
+#endif
 
 #if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
 /*
@@ -337,6 +340,10 @@
   encrypt_des(plaintext, results, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
   encrypt_des(plaintext, results + 16, keys + 14);
+#else
+  (void)keys;
+  (void)plaintext;
+  (void)results;
 #endif
 }
 
@@ -347,9 +354,11 @@
                                    unsigned char *lmbuffer /* 21 bytes */)
 {
   unsigned char pw[14];
+#if !defined(CURL_NTLM_NOT_SUPPORTED)
   static const unsigned char magic[] = {
     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
   };
+#endif
   size_t len = CURLMIN(strlen(password), 14);
 
   Curl_strntoupper((char *)pw, password, len);
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index aa7bea7..0c7892a 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -68,7 +68,9 @@
 
 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
    to avoid fooling the socket leak detector */
-#if defined(HAVE_CLOSESOCKET)
+#ifdef HAVE_PIPE
+#  define sclose_nolog(x)  close((x))
+#elif defined(HAVE_CLOSESOCKET)
 #  define sclose_nolog(x)  closesocket((x))
 #elif defined(HAVE_CLOSESOCKET_CAMEL)
 #  define sclose_nolog(x)  CloseSocket((x))
@@ -189,7 +191,7 @@
     goto done;
   }
 
-  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
+  if(wakeup_create(sockfds)) {
     failf(data, "Could not open socket pair. errno %d: %s",
           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
     goto done;
@@ -197,8 +199,8 @@
 
   child_pid = fork();
   if(child_pid == -1) {
-    sclose(sockfds[0]);
-    sclose(sockfds[1]);
+    wakeup_close(sockfds[0]);
+    wakeup_close(sockfds[1]);
     failf(data, "Could not fork. errno %d: %s",
           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
     goto done;
@@ -264,11 +266,11 @@
   size_t len_in = strlen(input), len_out = 0;
   struct dynbuf b;
   char *ptr = NULL;
-  unsigned char *buf = (unsigned char *)data->state.buffer;
+  usigned char buf[1024]
   Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
 
   while(len_in > 0) {
-    ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in);
+    ssize_t written = wakeup_write(ntlm->ntlm_auth_hlpr_socket, input, len_in);
     if(written == -1) {
       /* Interrupted by a signal, retry it */
       if(errno == EINTR)
@@ -282,7 +284,7 @@
   /* Read one line */
   while(1) {
     ssize_t size =
-      sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size);
+      wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, sizeof(buf));
     if(size == -1) {
       if(errno == EINTR)
         continue;
@@ -479,7 +481,7 @@
     /* connection is already authenticated,
      * don't send a header in future requests */
     *state = NTLMSTATE_LAST;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case NTLMSTATE_LAST:
     Curl_safefree(*allocuserpwd);
     authp->done = TRUE;
diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h
index 9ed09de..cbe51c2 100644
--- a/Utilities/cmcurl/lib/curl_path.h
+++ b/Utilities/cmcurl/lib/curl_path.h
@@ -28,7 +28,7 @@
 #include <curl/curl.h>
 #include "urldata.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 #  undef  PATH_MAX
 #  define PATH_MAX MAX_PATH
 #  ifndef R_OK
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h
index 46ef344..c2457d2 100644
--- a/Utilities/cmcurl/lib/curl_printf.h
+++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -31,6 +31,10 @@
 
 #include <curl/mprintf.h>
 
+#define MERR_OK        0
+#define MERR_MEM       1
+#define MERR_TOO_LARGE 2
+
 # undef printf
 # undef fprintf
 # undef msnprintf
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c
index 406fb42..147b12a 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.c
+++ b/Utilities/cmcurl/lib/curl_rtmp.c
@@ -39,7 +39,7 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#if defined(WIN32) && !defined(USE_LWIPSOCK)
+#if defined(_WIN32) && !defined(USE_LWIPSOCK)
 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
@@ -79,7 +79,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
@@ -102,7 +102,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
@@ -125,7 +125,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
@@ -148,7 +148,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
@@ -171,7 +171,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
@@ -194,7 +194,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index 91ddf10..66639cb 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -205,18 +205,23 @@
   sasl->force_ir = FALSE;          /* Respect external option */
 
   if(auth != CURLAUTH_BASIC) {
-    sasl->resetprefs = FALSE;
-    sasl->prefmech = SASL_AUTH_NONE;
+    unsigned short mechs = SASL_AUTH_NONE;
+
+    /* If some usable http authentication options have been set, determine
+       new defaults from them. */
     if(auth & CURLAUTH_BASIC)
-      sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
+      mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
     if(auth & CURLAUTH_DIGEST)
-      sasl->prefmech |= SASL_MECH_DIGEST_MD5;
+      mechs |= SASL_MECH_DIGEST_MD5;
     if(auth & CURLAUTH_NTLM)
-      sasl->prefmech |= SASL_MECH_NTLM;
+      mechs |= SASL_MECH_NTLM;
     if(auth & CURLAUTH_BEARER)
-      sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
+      mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
     if(auth & CURLAUTH_GSSAPI)
-      sasl->prefmech |= SASL_MECH_GSSAPI;
+      mechs |= SASL_MECH_GSSAPI;
+
+    if(mechs != SASL_AUTH_NONE)
+      sasl->prefmech = mechs;
   }
 }
 
@@ -262,6 +267,8 @@
   sasl->state = newstate;
 }
 
+#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH)
 /* Get the SASL server message and convert it to binary. */
 static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
                                    struct bufref *out)
@@ -284,6 +291,7 @@
   }
   return result;
 }
+#endif
 
 /* Encode the outgoing SASL message. */
 static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 8557cf4..f856f07 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -28,6 +28,18 @@
 #define CURL_NO_OLDIES
 #endif
 
+/* FIXME: Delete this once the warnings have been fixed. */
+#if !defined(CURL_WARN_SIGN_CONVERSION)
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
+#endif
+
+/* Set default _WIN32_WINNT */
+#ifdef __MINGW32__
+#include <_mingw.h>
+#endif
+
 /*
  * Disable Visual Studio warnings:
  * 4127 "conditional expression is constant"
@@ -36,15 +48,7 @@
 #pragma warning(disable:4127)
 #endif
 
-/*
- * Define WIN32 when build target is Win32 API
- */
-
-#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
-#define WIN32
-#endif
-
-#ifdef WIN32
+#ifdef _WIN32
 /*
  * Don't include unneeded stuff in Windows headers to avoid compiler
  * warnings and macro clashes.
@@ -82,7 +86,7 @@
 #ifdef _WIN32_WCE
 #  include "config-win32ce.h"
 #else
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include "config-win32.h"
 #  endif
 #endif
@@ -218,6 +222,23 @@
 #  define CURL_DISABLE_RTSP
 #endif
 
+/*
+ * When HTTP is disabled, disable HTTP-only features
+ */
+
+#if defined(CURL_DISABLE_HTTP)
+#  define CURL_DISABLE_ALTSVC 1
+#  define CURL_DISABLE_COOKIES 1
+#  define CURL_DISABLE_BASIC_AUTH 1
+#  define CURL_DISABLE_BEARER_AUTH 1
+#  define CURL_DISABLE_AWS 1
+#  define CURL_DISABLE_DOH 1
+#  define CURL_DISABLE_FORM_API 1
+#  define CURL_DISABLE_HEADERS_API 1
+#  define CURL_DISABLE_HSTS 1
+#  define CURL_DISABLE_HTTP_AUTH 1
+#endif
+
 /* ================================================================ */
 /* No system header file shall be included in this file before this */
 /* point.                                                           */
@@ -243,12 +264,39 @@
  * Windows setup file includes some system headers.
  */
 
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  include "setup-win32.h"
 #endif
 
 #include <curl/system.h>
 
+/* curl uses its own printf() function internally. It understands the GNU
+ * format. Use this format, so that is matches the GNU format attribute we
+ * use with the mingw compiler, allowing it to verify them at compile-time.
+ */
+#ifdef  __MINGW32__
+#  undef CURL_FORMAT_CURL_OFF_T
+#  undef CURL_FORMAT_CURL_OFF_TU
+#  define CURL_FORMAT_CURL_OFF_T   "lld"
+#  define CURL_FORMAT_CURL_OFF_TU  "llu"
+#endif
+
+/* based on logic in "curl/mprintf.h" */
+
+#if (defined(__GNUC__) || defined(__clang__)) &&                        \
+  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
+  !defined(CURL_NO_FMT_CHECKS)
+#if defined(__MINGW32__) && !defined(__clang__)
+#define CURL_PRINTF(fmt, arg) \
+  __attribute__((format(gnu_printf, fmt, arg)))
+#else
+#define CURL_PRINTF(fmt, arg) \
+  __attribute__((format(__printf__, fmt, arg)))
+#endif
+#else
+#define CURL_PRINTF(fmt, arg)
+#endif
+
 /*
  * Use getaddrinfo to resolve the IPv4 address literal. If the current network
  * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64,
@@ -335,23 +383,6 @@
 #include <curl/stdcheaders.h>
 #endif
 
-#ifdef __POCC__
-#  include <sys/types.h>
-#  include <unistd.h>
-#  define sys_nerr EILSEQ
-#endif
-
-/*
- * Salford-C kludge section (mostly borrowed from wxWidgets).
- */
-#ifdef __SALFORDC__
-  #pragma suppress 353             /* Possible nested comments */
-  #pragma suppress 593             /* Define not used */
-  #pragma suppress 61              /* enum has no name */
-  #pragma suppress 106             /* unnamed, unused parameter */
-  #include <clib.h>
-#endif
-
 /* Default Windows file API selection.  */
 #ifdef _WIN32
 # if defined(_MSC_VER) && (_INTEGRAL_MAX_BITS >= 64)
@@ -426,6 +457,24 @@
 #define SIZEOF_TIME_T 4
 #endif
 
+#ifndef SIZEOF_CURL_SOCKET_T
+/* configure and cmake check and set the define */
+#  ifdef _WIN64
+#    define SIZEOF_CURL_SOCKET_T 8
+#  else
+/* default guess */
+#    define SIZEOF_CURL_SOCKET_T 4
+#  endif
+#endif
+
+#if SIZEOF_CURL_SOCKET_T < 8
+#  define CURL_FORMAT_SOCKET_T "d"
+#elif defined(__MINGW32__)
+#  define CURL_FORMAT_SOCKET_T "zd"
+#else
+#  define CURL_FORMAT_SOCKET_T "qd"
+#endif
+
 /*
  * Default sizeof(off_t) in case it hasn't been defined in config file.
  */
@@ -515,11 +564,11 @@
    5. set dir/file naming defines
    */
 
-#ifdef WIN32
+#ifdef _WIN32
 
 #  define DIR_CHAR      "\\"
 
-#else /* WIN32 */
+#else /* _WIN32 */
 
 #  ifdef MSDOS  /* Watt-32 */
 
@@ -544,27 +593,7 @@
 
 #  define DIR_CHAR      "/"
 
-#  ifndef fileno /* sunos 4 have this as a macro! */
-     int fileno(FILE *stream);
-#  endif
-
-#endif /* WIN32 */
-
-/*
- * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN
- * defined in ws2tcpip.h as well as to provide IPv6 support.
- * Does not apply if lwIP is used.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK)
-#  if !defined(HAVE_WS2TCPIP_H) || \
-     ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN))
-#    undef HAVE_GETADDRINFO_THREADSAFE
-#    undef HAVE_FREEADDRINFO
-#    undef HAVE_GETADDRINFO
-#    undef ENABLE_IPV6
-#  endif
-#endif
+#endif /* _WIN32 */
 
 /* ---------------------------------------------------------------- */
 /*             resolver specialty compile-time defines              */
@@ -572,20 +601,11 @@
 /* ---------------------------------------------------------------- */
 
 /*
- * lcc-win32 doesn't have _beginthreadex(), lacks threads support.
- */
-
-#if defined(__LCC__) && defined(WIN32)
-#  undef USE_THREADS_POSIX
-#  undef USE_THREADS_WIN32
-#endif
-
-/*
  * MSVC threads support requires a multi-threaded runtime library.
  * _beginthreadex() is not available in single-threaded ones.
  */
 
-#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT)
+#if defined(_MSC_VER) && !defined(_MT)
 #  undef USE_THREADS_POSIX
 #  undef USE_THREADS_WIN32
 #endif
@@ -596,6 +616,9 @@
 
 #if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO)
 #  define CURLRES_IPV6
+#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__))
+/* assume on Windows that IPv6 without getaddrinfo is a broken build */
+#  error "Unexpected build: IPv6 is enabled but getaddrinfo was not found."
 #else
 #  define CURLRES_IPV4
 #endif
@@ -615,35 +638,6 @@
 
 /* ---------------------------------------------------------------- */
 
-/*
- * msvc 6.0 does not have struct sockaddr_storage and
- * does not define IPPROTO_ESP in winsock2.h. But both
- * are available if PSDK is properly installed.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__)
-#  if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP))
-#    undef HAVE_STRUCT_SOCKADDR_STORAGE
-#  endif
-#endif
-
-/*
- * Intentionally fail to build when using msvc 6.0 without PSDK installed.
- * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK
- * in lib/config-win32.h although absolutely discouraged and unsupported.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__)
-#  if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_))
-#    if !defined(ALLOW_MSVC6_WITHOUT_PSDK)
-#      error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \
-             "Windows Server 2003 PSDK"
-#    else
-#      define CURL_DISABLE_LDAP 1
-#    endif
-#  endif
-#endif
-
 #if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN)
 /* The lib and header are present */
 #define USE_LIBIDN2
@@ -709,6 +703,29 @@
 #  define WARN_UNUSED_RESULT
 #endif
 
+/* noreturn attribute */
+
+#if !defined(CURL_NORETURN)
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)
+#  define CURL_NORETURN  __attribute__((__noreturn__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+#  define CURL_NORETURN  __declspec(noreturn)
+#else
+#  define CURL_NORETURN
+#endif
+#endif
+
+/* fallthrough attribute */
+
+#if !defined(FALLTHROUGH)
+#if (defined(__GNUC__) && __GNUC__ >= 7) || \
+    (defined(__clang__) && __clang_major__ >= 10)
+#  define FALLTHROUGH()  __attribute__((fallthrough))
+#else
+#  define FALLTHROUGH()  do {} while (0)
+#endif
+#endif
+
 /*
  * Include macros and defines that should only be processed once.
  */
@@ -730,10 +747,7 @@
  */
 
 #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)
-#  if defined(SOCKET) || \
-     defined(USE_WINSOCK) || \
-     defined(HAVE_WINSOCK2_H) || \
-     defined(HAVE_WS2TCPIP_H)
+#  if defined(SOCKET) || defined(USE_WINSOCK)
 #    error "WinSock and lwIP TCP/IP stack definitions shall not coexist!"
 #  endif
 #endif
@@ -767,7 +781,7 @@
 /* In Windows the default file mode is text but an application can override it.
 Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
 */
-#if defined(WIN32) || defined(MSDOS)
+#if defined(_WIN32) || defined(MSDOS)
 #define FOPEN_READTEXT "rt"
 #define FOPEN_WRITETEXT "wt"
 #define FOPEN_APPENDTEXT "at"
@@ -822,12 +836,19 @@
 #define UNITTEST static
 #endif
 
-#if defined(USE_NGHTTP2) || defined(USE_HYPER)
+/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */
+#if defined(USE_NGHTTP2)
 #define USE_HTTP2
 #endif
 
 #if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
+    (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \
     defined(USE_QUICHE) || defined(USE_MSH3)
+
+#ifdef CURL_WITH_MULTI_SSL
+#error "Multi-SSL combined with QUIC is not supported"
+#endif
+
 #define ENABLE_QUIC
 #define USE_HTTP3
 #endif
@@ -835,11 +856,11 @@
 /* Certain Windows implementations are not aligned with what curl expects,
    so always use the local one on this platform. E.g. the mingw-w64
    implementation can return wrong results for non-ASCII inputs. */
-#if defined(HAVE_BASENAME) && defined(WIN32)
+#if defined(HAVE_BASENAME) && defined(_WIN32)
 #undef HAVE_BASENAME
 #endif
 
-#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
+#if defined(USE_UNIX_SOCKETS) && defined(_WIN32)
 #  if !defined(UNIX_PATH_MAX)
      /* Replicating logic present in afunix.h
         (distributed with newer Windows 10 SDK versions only) */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h
index c1ed059..bf0ee66 100644
--- a/Utilities/cmcurl/lib/curl_setup_once.h
+++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -56,7 +56,7 @@
 #include <sys/time.h>
 #endif
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <io.h>
 #include <fcntl.h>
 #endif
@@ -70,11 +70,7 @@
 #endif
 
 #ifdef USE_WOLFSSL
-#  if defined(HAVE_STDINT_H)
-#    include <stdint.h>
-#  elif defined(HAVE_INTTYPES_H)
-#    include <inttypes.h>
-#  endif
+#include <stdint.h>
 #endif
 
 #ifdef USE_SCHANNEL
diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h
index 5af7c24..b26c391 100644
--- a/Utilities/cmcurl/lib/curl_sspi.h
+++ b/Utilities/cmcurl/lib/curl_sspi.h
@@ -88,6 +88,22 @@
 # define CRYPT_E_REVOKED                      ((HRESULT)0x80092010L)
 #endif
 
+#ifndef CRYPT_E_NO_REVOCATION_DLL
+# define CRYPT_E_NO_REVOCATION_DLL            ((HRESULT)0x80092011L)
+#endif
+
+#ifndef CRYPT_E_NO_REVOCATION_CHECK
+# define CRYPT_E_NO_REVOCATION_CHECK          ((HRESULT)0x80092012L)
+#endif
+
+#ifndef CRYPT_E_REVOCATION_OFFLINE
+# define CRYPT_E_REVOCATION_OFFLINE           ((HRESULT)0x80092013L)
+#endif
+
+#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
+# define CRYPT_E_NOT_IN_REVOCATION_DATABASE   ((HRESULT)0x80092014L)
+#endif
+
 #ifdef UNICODE
 #  define SECFLAG_WINNT_AUTH_IDENTITY \
      (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c
index e53b305..b8dccc4 100644
--- a/Utilities/cmcurl/lib/curl_trc.c
+++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -61,10 +61,6 @@
       "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
     if(data->set.fdebug) {
       bool inCallback = Curl_is_in_callback(data);
-      /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to
-         distinguish their handle from internal handles. */
-      if(data->internal)
-        DEBUGASSERT(!data->set.private_data);
       Curl_set_in_callback(data, true);
       (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
       Curl_set_in_callback(data, inCallback);
@@ -109,6 +105,8 @@
   }
 }
 
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+
 /* Curl_infof() is for info message along the way */
 #define MAXINFO 2048
 
@@ -128,13 +126,11 @@
   }
 }
 
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-
 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...)
 {
   DEBUGASSERT(cf);
-  if(data && Curl_trc_cf_is_verbose(cf, data)) {
+  if(Curl_trc_cf_is_verbose(cf, data)) {
     va_list ap;
     int len;
     char buffer[MAXINFO + 2];
@@ -161,8 +157,10 @@
 #endif
 #ifdef USE_SSL
   &Curl_cft_ssl,
+#ifndef CURL_DISABLE_PROXY
   &Curl_cft_ssl_proxy,
 #endif
+#endif
 #if !defined(CURL_DISABLE_PROXY)
 #if !defined(CURL_DISABLE_HTTP)
   &Curl_cft_h1_proxy,
@@ -232,24 +230,14 @@
   if(config) {
     return Curl_trc_opt(config);
   }
-#endif
+#endif /* DEBUGBUILD */
   return CURLE_OK;
 }
-#else /* !CURL_DISABLE_VERBOSE_STRINGS) */
+#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
 CURLcode Curl_trc_init(void)
 {
   return CURLE_OK;
 }
 
-#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
-                       const char *fmt, ...)
-{
-  (void)data;
-  (void)cf;
-  (void)fmt;
-}
-#endif
-
-#endif /* !DEBUGBUILD */
+#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h
index 84b5471..3a5387a 100644
--- a/Utilities/cmcurl/lib/curl_trc.h
+++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -55,66 +55,22 @@
                 char *ptr, size_t size);
 
 /**
- * Output an informational message when transfer's verbose logging is enabled.
- */
-void Curl_infof(struct Curl_easy *data,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 2, 3)));
-#else
-                       const char *fmt, ...);
-#endif
-
-/**
  * Output a failure message on registered callbacks for transfer.
  */
 void Curl_failf(struct Curl_easy *data,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 2, 3)));
-#else
-                       const char *fmt, ...);
-#endif
+                const char *fmt, ...) CURL_PRINTF(2, 3);
 
 #define failf Curl_failf
 
-/**
- * Output an informational message when both transfer's verbose logging
- * and connection filters verbose logging are enabled.
- */
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 3, 4)));
-#else
-                       const char *fmt, ...);
-#endif
-
 #define CURL_LOG_LVL_NONE  0
 #define CURL_LOG_LVL_INFO  1
 
 
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-/* informational messages enabled */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define CURL_HAVE_C99
+#endif
 
-#define Curl_trc_is_verbose(data)    ((data) && (data)->set.verbose)
-#define Curl_trc_cf_is_verbose(cf, data) \
-                            ((data) && (data)->set.verbose && \
-                            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
-
-/* explainer: we have some mix configuration and werror settings
- * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
- * on gnuc and some other compiler. Need to treat carefully.
- */
-#if defined(HAVE_VARIADIC_MACROS_C99) && \
-    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-
+#ifdef CURL_HAVE_C99
 #define infof(data, ...) \
   do { if(Curl_trc_is_verbose(data)) \
          Curl_infof(data, __VA_ARGS__); } while(0)
@@ -122,29 +78,50 @@
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
          Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
 
-#else /* no variadic macro args */
+#else
 #define infof Curl_infof
 #define CURL_TRC_CF Curl_trc_cf_infof
-#endif /* variadic macro args */
+#endif
 
-#else /* !CURL_DISABLE_VERBOSE_STRINGS */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+/* informational messages enabled */
+
+#define Curl_trc_is_verbose(data)    ((data) && (data)->set.verbose)
+#define Curl_trc_cf_is_verbose(cf, data) \
+                            ((data) && (data)->set.verbose && \
+                            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
+
+/**
+ * Output an informational message when transfer's verbose logging is enabled.
+ */
+void Curl_infof(struct Curl_easy *data,
+                const char *fmt, ...) CURL_PRINTF(2, 3);
+
+/**
+ * Output an informational message when both transfer's verbose logging
+ * and connection filters verbose logging are enabled.
+ */
+void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+                       const char *fmt, ...) CURL_PRINTF(3, 4);
+
+#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 /* All informational messages are not compiled in for size savings */
 
 #define Curl_trc_is_verbose(d)        ((void)(d), FALSE)
 #define Curl_trc_cf_is_verbose(x,y)   ((void)(x), (void)(y), FALSE)
 
-#if defined(HAVE_VARIADIC_MACROS_C99)
-#define infof(...)  Curl_nop_stmt
-#define CURL_TRC_CF(...)  Curl_nop_stmt
-#define Curl_trc_cf_infof(...)  Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC)
-#define infof(x...)  Curl_nop_stmt
-#define CURL_TRC_CF(x...)  Curl_nop_stmt
-#define Curl_trc_cf_infof(x...)  Curl_nop_stmt
-#else
-#error "missing VARIADIC macro define, fix and rebuild!"
-#endif
+static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
 
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+static void Curl_trc_cf_infof(struct Curl_easy *data,
+                              struct Curl_cfilter *cf,
+                              const char *fmt, ...)
+{
+  (void)data; (void)cf; (void)fmt;
+}
+
+#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
 #endif /* HEADER_CURL_TRC_H */
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index 3172b38..3239848 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -89,7 +89,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_DICT,                            /* defport */
@@ -123,6 +123,9 @@
 
 /* sendf() sends formatted data to the server */
 static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
+                      const char *fmt, ...) CURL_PRINTF(3, 4);
+
+static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
                       const char *fmt, ...)
 {
   ssize_t bytes_written;
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index bb0c89e..ef32d50 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -218,7 +218,6 @@
                          struct curl_slist *headers)
 {
   struct Curl_easy *doh = NULL;
-  char *nurl = NULL;
   CURLcode result = CURLE_OK;
   timediff_t timeout_ms;
   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
@@ -242,7 +241,7 @@
     /* pass in the struct pointer via a local variable to please coverity and
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
-    doh->internal = true;
+    doh->state.internal = true;
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
@@ -252,6 +251,7 @@
     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
 #ifdef USE_HTTP2
     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+    ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
 #endif
 #ifndef CURLDEBUG
     /* enforce HTTPS if not debug */
@@ -339,9 +339,10 @@
     doh->set.dohfor = data; /* identify for which transfer this is done */
     p->easy = doh;
 
-    /* DoH private_data must be null because the user must have a way to
-       distinguish their transfer's handle from DoH handles in user
-       callbacks (ie SSL CTX callback). */
+    /* DoH handles must not inherit private_data. The handles may be passed to
+       the user via callbacks and the user will be able to identify them as
+       internal handles because private data is not set. The user can then set
+       private_data via CURLOPT_PRIVATE if they so choose. */
     DEBUGASSERT(!doh->set.private_data);
 
     if(curl_multi_add_handle(multi, doh))
@@ -349,11 +350,9 @@
   }
   else
     goto error;
-  free(nurl);
   return CURLE_OK;
 
 error:
-  free(nurl);
   Curl_close(&doh);
   return result;
 }
@@ -372,7 +371,7 @@
   int slot;
   struct dohdata *dohp;
   struct connectdata *conn = data->conn;
-  *waitp = TRUE; /* this never returns synchronously */
+  *waitp = FALSE;
   (void)hostname;
   (void)port;
 
@@ -380,7 +379,7 @@
   DEBUGASSERT(conn);
 
   /* start clean, consider allocating this struct on demand */
-  dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
+  dohp = data->req.doh = calloc(1, sizeof(struct dohdata));
   if(!dohp)
     return NULL;
 
@@ -412,12 +411,14 @@
     dohp->pending++;
   }
 #endif
+  *waitp = TRUE; /* this never returns synchronously */
   return NULL;
 
 error:
   curl_slist_free_all(dohp->headers);
   data->req.doh->headers = NULL;
   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+    (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
     Curl_close(&dohp->probe[slot].easy);
   }
   Curl_safefree(data->req.doh);
@@ -443,7 +444,7 @@
       return DOH_DNS_BAD_LABEL;
     if(dohlen < (*indexp + 1 + length))
       return DOH_DNS_OUT_OF_RANGE;
-    *indexp += 1 + length;
+    *indexp += (unsigned int)(1 + length);
   } while(length);
   return DOH_OK;
 }
@@ -455,14 +456,15 @@
 
 static unsigned int get32bit(const unsigned char *doh, int index)
 {
-   /* make clang and gcc optimize this to bswap by incrementing
-      the pointer first. */
-   doh += index;
+  /* make clang and gcc optimize this to bswap by incrementing
+     the pointer first. */
+  doh += index;
 
-   /* avoid undefined behavior by casting to unsigned before shifting
-      24 bits, possibly into the sign bit. codegen is same, but
-      ub sanitizer won't be upset */
-  return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
+  /* avoid undefined behavior by casting to unsigned before shifting
+     24 bits, possibly into the sign bit. codegen is same, but
+     ub sanitizer won't be upset */
+  return ((unsigned)doh[0] << 24) | ((unsigned)doh[1] << 16) |
+         ((unsigned)doh[2] << 8) | doh[3];
 }
 
 static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
@@ -787,8 +789,8 @@
  * must be an associated call later to Curl_freeaddrinfo().
  */
 
-static struct Curl_addrinfo *
-doh2ai(const struct dohentry *de, const char *hostname, int port)
+static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
+                       int port, struct Curl_addrinfo **aip)
 {
   struct Curl_addrinfo *ai;
   struct Curl_addrinfo *prevai = NULL;
@@ -801,9 +803,10 @@
   int i;
   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
 
-  if(!de)
-    /* no input == no output! */
-    return NULL;
+  DEBUGASSERT(de);
+
+  if(!de->numaddr)
+    return CURLE_COULDNT_RESOLVE_HOST;
 
   for(i = 0; i < de->numaddr; i++) {
     size_t ss_size;
@@ -876,8 +879,9 @@
     Curl_freeaddrinfo(firstai);
     firstai = NULL;
   }
+  *aip = firstai;
 
-  return firstai;
+  return result;
 }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -932,10 +936,12 @@
                             p->dnstype,
                             &de);
       Curl_dyn_free(&p->serverdoh);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
       if(rc[slot]) {
         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
               type2name(p->dnstype), dohp->host);
       }
+#endif
     } /* next slot */
 
     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
@@ -947,10 +953,10 @@
       infof(data, "DoH Host name: %s", dohp->host);
       showdoh(data, &de);
 
-      ai = doh2ai(&de, dohp->host, dohp->port);
-      if(!ai) {
+      result = doh2ai(&de, dohp->host, dohp->port, &ai);
+      if(result) {
         de_cleanup(&de);
-        return CURLE_OUT_OF_MEMORY;
+        return result;
       }
 
       if(data->share)
diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c
index 0c9c491..a4c599d 100644
--- a/Utilities/cmcurl/lib/dynbuf.c
+++ b/Utilities/cmcurl/lib/dynbuf.c
@@ -77,10 +77,11 @@
   DEBUGASSERT(indx < s->toobig);
   DEBUGASSERT(!s->leng || s->bufr);
   DEBUGASSERT(a <= s->toobig);
+  DEBUGASSERT(!len || mem);
 
   if(fit > s->toobig) {
     Curl_dyn_free(s);
-    return CURLE_OUT_OF_MEMORY;
+    return CURLE_TOO_LARGE;
   }
   else if(!a) {
     DEBUGASSERT(!indx);
@@ -174,10 +175,12 @@
  */
 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
 {
-  size_t n = strlen(str);
+  size_t n;
+  DEBUGASSERT(str);
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
+  n = strlen(str);
   return dyn_nappend(s, (unsigned char *)str, n);
 }
 
@@ -191,10 +194,14 @@
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
+  DEBUGASSERT(fmt);
   rc = Curl_dyn_vprintf(s, fmt, ap);
 
   if(!rc)
     return CURLE_OK;
+  else if(rc == MERR_TOO_LARGE)
+    return CURLE_TOO_LARGE;
+  return CURLE_OUT_OF_MEMORY;
 #else
   char *str;
   str = vaprintf(fmt, ap); /* this allocs a new string to append */
@@ -206,8 +213,8 @@
   }
   /* If we failed, we cleanup the whole buffer and return error */
   Curl_dyn_free(s);
+  return CURLE_OK;
 #endif
-  return CURLE_OUT_OF_MEMORY;
 }
 
 /*
diff --git a/Utilities/cmcurl/lib/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h
index 31a9130..7dbaab8 100644
--- a/Utilities/cmcurl/lib/dynbuf.h
+++ b/Utilities/cmcurl/lib/dynbuf.h
@@ -61,9 +61,9 @@
 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
   WARN_UNUSED_RESULT;
 CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
-  WARN_UNUSED_RESULT;
+  WARN_UNUSED_RESULT CURL_PRINTF(2, 3);
 CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
-  WARN_UNUSED_RESULT;
+  WARN_UNUSED_RESULT CURL_PRINTF(2, 0);
 void Curl_dyn_reset(struct dynbuf *s);
 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
 CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c
index 979b3e8..d754895 100644
--- a/Utilities/cmcurl/lib/dynhds.c
+++ b/Utilities/cmcurl/lib/dynhds.c
@@ -27,6 +27,10 @@
 #include "strcase.h"
 
 /* The last 3 #include files should be in this order */
+#ifdef USE_NGHTTP2
+#include <stdint.h>
+#include <nghttp2/nghttp2.h>
+#endif /* USE_NGHTTP2 */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
@@ -365,3 +369,28 @@
   return result;
 }
 
+#ifdef USE_NGHTTP2
+
+nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount)
+{
+  nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len);
+  size_t i;
+
+  *pcount = 0;
+  if(!nva)
+    return NULL;
+
+  for(i = 0; i < dynhds->hds_len; ++i) {
+    struct dynhds_entry *e = dynhds->hds[i];
+    DEBUGASSERT(e);
+    nva[i].name = (unsigned char *)e->name;
+    nva[i].namelen = e->namelen;
+    nva[i].value = (unsigned char *)e->value;
+    nva[i].valuelen = e->valuelen;
+    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
+  }
+  *pcount = dynhds->hds_len;
+  return nva;
+}
+
+#endif /* USE_NGHTTP2 */
diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h
index 8a05348..3b53600 100644
--- a/Utilities/cmcurl/lib/dynhds.h
+++ b/Utilities/cmcurl/lib/dynhds.h
@@ -171,4 +171,13 @@
  */
 CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf);
 
+#ifdef USE_NGHTTP2
+
+#include <stdint.h>
+#include <nghttp2/nghttp2.h>
+
+nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount);
+
+#endif /* USE_NGHTTP2 */
+
 #endif /* HEADER_CURL_DYNHDS_H */
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index 6b4fb8e..067b6d7 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -112,7 +112,7 @@
 #define system_strdup strdup
 #endif
 
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
 #endif
 
@@ -125,11 +125,11 @@
 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
 #endif
 
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(default:4232) /* MSVC extension, dllimport identity */
 #endif
 
@@ -153,7 +153,7 @@
     Curl_crealloc = (curl_realloc_callback)realloc;
     Curl_cstrdup = (curl_strdup_callback)system_strdup;
     Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
     Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
 #endif
   }
@@ -188,18 +188,10 @@
     goto fail;
   }
 
-#if defined(USE_SSH)
   if(Curl_ssh_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n"));
     goto fail;
   }
-#endif
-
-#ifdef USE_WOLFSSH
-  if(WS_SUCCESS != wolfSSH_Init()) {
-    DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
-    return CURLE_FAILED_INIT;
-  }
-#endif
 
   easy_init_flags = flags;
 
@@ -295,7 +287,7 @@
   Curl_ssl_cleanup();
   Curl_resolver_global_cleanup();
 
-#ifdef WIN32
+#ifdef _WIN32
   Curl_win32_cleanup(easy_init_flags);
 #endif
 
@@ -488,13 +480,15 @@
           ev->list = nxt;
         free(m);
         m = nxt;
-        infof(easy, "socket cb: socket %d REMOVED", s);
+        infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+              " REMOVED", s);
       }
       else {
         /* The socket 's' is already being monitored, update the activity
            mask. Convert from libcurl bitmask to the poll one. */
         m->socket.events = socketcb2poll(what);
-        infof(easy, "socket cb: socket %d UPDATED as %s%s", s,
+        infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+              " UPDATED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -518,7 +512,8 @@
         m->socket.events = socketcb2poll(what);
         m->socket.revents = 0;
         ev->list = m;
-        infof(easy, "socket cb: socket %d ADDED as %s%s", s,
+        infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
+              " ADDED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -607,8 +602,9 @@
         if(fds[i].revents) {
           /* socket activity, tell libcurl */
           int act = poll2cselect(fds[i].revents); /* convert */
-          infof(multi->easyp, "call curl_multi_socket_action(socket %d)",
-                fds[i].fd);
+          infof(multi->easyp,
+                "call curl_multi_socket_action(socket "
+                "%" CURL_FORMAT_SOCKET_T ")", fds[i].fd);
           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
                                            &ev->running_handles);
         }
@@ -692,9 +688,9 @@
   /* Make sure to return some kind of error if there was a multi problem */
   if(mcode) {
     result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY :
-              /* The other multi errors should never happen, so return
-                 something suitably generic */
-              CURLE_BAD_FUNCTION_ARGUMENT;
+      /* The other multi errors should never happen, so return
+         something suitably generic */
+      CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
   return result;
@@ -752,7 +748,7 @@
     return CURLE_RECURSIVE_API_CALL;
 
   /* Copy the MAXCONNECTS option to the multi handle */
-  curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
+  curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
 
   mcode = curl_multi_add_handle(multi, data);
   if(mcode) {
@@ -845,8 +841,10 @@
   dst->set = src->set;
   Curl_mime_initpart(&dst->set.mimepost);
 
-  /* clear all string pointers first */
+  /* clear all dest string and blob pointers first, in case we error out
+     mid-function */
   memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
+  memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
 
   /* duplicate all strings */
   for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) {
@@ -855,8 +853,6 @@
       return result;
   }
 
-  /* clear all blob pointers first */
-  memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
   /* duplicate all blobs */
   for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
     result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
@@ -866,10 +862,13 @@
 
   /* duplicate memory areas pointed to */
   i = STRING_COPYPOSTFIELDS;
-  if(src->set.postfieldsize && src->set.str[i]) {
-    /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
-    dst->set.str[i] = Curl_memdup(src->set.str[i],
-                                  curlx_sotouz(src->set.postfieldsize));
+  if(src->set.str[i]) {
+    if(src->set.postfieldsize == -1)
+      dst->set.str[i] = strdup(src->set.str[i]);
+    else
+      /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
+      dst->set.str[i] = Curl_memdup(src->set.str[i],
+                                    curlx_sotouz(src->set.postfieldsize));
     if(!dst->set.str[i])
       return CURLE_OUT_OF_MEMORY;
     /* point to the new copy */
@@ -919,18 +918,19 @@
   outcurl->progress.callback = data->progress.callback;
 
 #ifndef CURL_DISABLE_COOKIES
-  if(data->cookies) {
+  outcurl->state.cookielist = NULL;
+  if(data->cookies && data->state.cookie_engine) {
     /* If cookies are enabled in the parent handle, we enable them
        in the clone as well! */
-    outcurl->cookies = Curl_cookie_init(data, NULL, outcurl->cookies,
+    outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies,
                                         data->set.cookiesession);
     if(!outcurl->cookies)
       goto fail;
   }
 
-  if(data->set.cookielist) {
-    outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist);
-    if(!outcurl->set.cookielist)
+  if(data->state.cookielist) {
+    outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
+    if(!outcurl->state.cookielist)
       goto fail;
   }
 #endif
@@ -976,11 +976,14 @@
     (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
   }
 #endif
+
+#ifdef CURLRES_ASYNCH
   /* Clone the resolver handle, if present, for the new handle */
   if(Curl_resolver_duphandle(outcurl,
                              &outcurl->state.async.resolver,
                              data->state.async.resolver))
     goto fail;
+#endif
 
 #ifdef USE_ARES
   {
@@ -1016,13 +1019,10 @@
 
   if(outcurl) {
 #ifndef CURL_DISABLE_COOKIES
-    curl_slist_free_all(outcurl->set.cookielist);
-    outcurl->set.cookielist = NULL;
+    free(outcurl->cookies);
 #endif
-    Curl_safefree(outcurl->state.buffer);
+    free(outcurl->state.buffer);
     Curl_dyn_free(&outcurl->state.headerb);
-    Curl_safefree(outcurl->state.url);
-    Curl_safefree(outcurl->state.referer);
     Curl_altsvc_cleanup(&outcurl->asi);
     Curl_hsts_cleanup(&outcurl->hsts);
     Curl_freeset(outcurl);
@@ -1145,7 +1145,7 @@
     if(!data->state.tempcount)
       /* if not pausing again, force a recv/send check of this connection as
          the data might've been read off the socket already */
-      data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
+      data->state.select_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
     if(data->multi) {
       if(Curl_update_timer(data->multi))
         return CURLE_ABORTED_BY_CALLBACK;
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
index d3fffd0..4f6764d 100644
--- a/Utilities/cmcurl/lib/easy_lock.h
+++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -93,6 +93,15 @@
   atomic_store_explicit(lock, false, memory_order_release);
 }
 
+#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+
+#include <pthread.h>
+
+#define curl_simple_lock pthread_mutex_t
+#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
+#define curl_simple_lock_lock(m) pthread_mutex_lock(m)
+#define curl_simple_lock_unlock(m) pthread_mutex_unlock(m)
+
 #else
 
 #undef  GLOBAL_INIT_IS_THREADSAFE
diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c
index e69c658..da4c611 100644
--- a/Utilities/cmcurl/lib/easyoptions.c
+++ b/Utilities/cmcurl/lib/easyoptions.c
@@ -274,6 +274,8 @@
   {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0},
   {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
    CURLOT_LONG, 0},
+  {"SERVER_RESPONSE_TIMEOUT_MS", CURLOPT_SERVER_RESPONSE_TIMEOUT_MS,
+   CURLOT_LONG, 0},
   {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0},
   {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0},
   {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0},
@@ -373,6 +375,6 @@
  */
 int Curl_easyopts_check(void)
 {
-  return ((CURLOPT_LASTENTRY%10000) != (323 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (324 + 1));
 }
 #endif
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index ffa9fb7..b7ce3a8 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -69,7 +69,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__)
 #define DOS_FILESYSTEM 1
 #elif defined(__amigaos4__)
 #define AMIGA_FILESYSTEM 1
@@ -113,7 +113,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   file_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   0,                                    /* defport */
@@ -290,16 +290,15 @@
   int fd;
   int mode;
   CURLcode result = CURLE_OK;
-  char *buf = data->state.buffer;
+  char buffer[8*1024], *uphere_save;
   curl_off_t bytecount = 0;
   struct_stat file_stat;
-  const char *buf2;
+  const char *sendbuf;
 
   /*
    * Since FILE: doesn't do the full init, we need to provide some extra
    * assignments here.
    */
-  data->req.upload_fromhere = buf;
 
   if(!dir)
     return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
@@ -338,11 +337,15 @@
     data->state.resume_from = (curl_off_t)file_stat.st_size;
   }
 
+  /* Yikes! Curl_fillreadbuffer uses data->req.upload_fromhere to READ
+   * client data to! Please, someone fix... */
+  uphere_save = data->req.upload_fromhere;
   while(!result) {
     size_t nread;
     ssize_t nwrite;
     size_t readcount;
-    result = Curl_fillreadbuffer(data, data->set.buffer_size, &readcount);
+    data->req.upload_fromhere = buffer;
+    result = Curl_fillreadbuffer(data, sizeof(buffer), &readcount);
     if(result)
       break;
 
@@ -356,19 +359,19 @@
       if((curl_off_t)nread <= data->state.resume_from) {
         data->state.resume_from -= nread;
         nread = 0;
-        buf2 = buf;
+        sendbuf = buffer;
       }
       else {
-        buf2 = buf + data->state.resume_from;
+        sendbuf = buffer + data->state.resume_from;
         nread -= (size_t)data->state.resume_from;
         data->state.resume_from = 0;
       }
     }
     else
-      buf2 = buf;
+      sendbuf = buffer;
 
     /* write the data to the target */
-    nwrite = write(fd, buf2, nread);
+    nwrite = write(fd, sendbuf, nread);
     if((size_t)nwrite != nread) {
       result = CURLE_SEND_ERROR;
       break;
@@ -387,6 +390,7 @@
     result = CURLE_ABORTED_BY_CALLBACK;
 
   close(fd);
+  data->req.upload_fromhere = uphere_save;
 
   return result;
 }
@@ -413,15 +417,11 @@
   curl_off_t expected_size = -1;
   bool size_known;
   bool fstated = FALSE;
-  char *buf = data->state.buffer;
-  curl_off_t bytecount = 0;
   int fd;
   struct FILEPROTO *file;
 
   *done = TRUE; /* unconditionally */
 
-  Curl_pgrsStartNow(data);
-
   if(data->state.upload)
     return file_upload(data);
 
@@ -544,34 +544,30 @@
   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
 
   while(!result) {
+    char tmpbuf[8*1024];
     ssize_t nread;
     /* Don't fill a whole buffer if we want less than all data */
     size_t bytestoread;
 
     if(size_known) {
-      bytestoread = (expected_size < data->set.buffer_size) ?
-        curlx_sotouz(expected_size) : (size_t)data->set.buffer_size;
+      bytestoread = (expected_size < (curl_off_t)(sizeof(tmpbuf)-1)) ?
+        curlx_sotouz(expected_size) : (sizeof(tmpbuf)-1);
     }
     else
-      bytestoread = data->set.buffer_size-1;
+      bytestoread = sizeof(tmpbuf)-1;
 
-    nread = read(fd, buf, bytestoread);
+    nread = read(fd, tmpbuf, bytestoread);
 
     if(nread > 0)
-      buf[nread] = 0;
+      tmpbuf[nread] = 0;
 
     if(nread <= 0 || (size_known && (expected_size == 0)))
       break;
 
-    bytecount += nread;
     if(size_known)
       expected_size -= nread;
 
-    result = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
-    if(result)
-      return result;
-
-    result = Curl_pgrsSetDownloadCounter(data, bytecount);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, tmpbuf, nread);
     if(result)
       return result;
 
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c
index 75b8a7a..851279f 100644
--- a/Utilities/cmcurl/lib/fopen.c
+++ b/Utilities/cmcurl/lib/fopen.c
@@ -40,6 +40,51 @@
 #include "memdebug.h"
 
 /*
+  The dirslash() function breaks a null-terminated pathname string into
+  directory and filename components then returns the directory component up
+  to, *AND INCLUDING*, a final '/'.  If there is no directory in the path,
+  this instead returns a "" string.
+
+  This function returns a pointer to malloc'ed memory.
+
+  The input path to this function is expected to have a file name part.
+*/
+
+#ifdef _WIN32
+#define PATHSEP "\\"
+#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))
+#elif defined(MSDOS) || defined(__EMX__) || defined(OS2)
+#define PATHSEP "\\"
+#define IS_SEP(x) ((x) == '\\')
+#else
+#define PATHSEP "/"
+#define IS_SEP(x) ((x) == '/')
+#endif
+
+static char *dirslash(const char *path)
+{
+  size_t n;
+  struct dynbuf out;
+  DEBUGASSERT(path);
+  Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
+  n = strlen(path);
+  if(n) {
+    /* find the rightmost path separator, if any */
+    while(n && !IS_SEP(path[n-1]))
+      --n;
+    /* skip over all the path separators, if any */
+    while(n && IS_SEP(path[n-1]))
+      --n;
+  }
+  if(Curl_dyn_addn(&out, path, n))
+    return NULL;
+  /* if there was a directory, append a single trailing slash */
+  if(n && Curl_dyn_addn(&out, PATHSEP, 1))
+    return NULL;
+  return Curl_dyn_ptr(&out);
+}
+
+/*
  * Curl_fopen() opens a file for writing with a temp name, to be renamed
  * to the final name when completed. If there is an existing file using this
  * name at the time of the open, this function will clone the mode from that
@@ -50,47 +95,44 @@
                     FILE **fh, char **tempname)
 {
   CURLcode result = CURLE_WRITE_ERROR;
-  unsigned char randsuffix[9];
+  unsigned char randbuf[41];
   char *tempstore = NULL;
   struct_stat sb;
   int fd = -1;
+  char *dir = NULL;
   *tempname = NULL;
 
   *fh = fopen(filename, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
-  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode))
+  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) {
     return CURLE_OK;
+  }
   fclose(*fh);
   *fh = NULL;
 
-  result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix));
+  result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
   if(result)
     goto fail;
 
-  tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
+  dir = dirslash(filename);
+  if(dir) {
+    /* The temp file name should not end up too long for the target file
+       system */
+    tempstore = aprintf("%s%s.tmp", dir, randbuf);
+    free(dir);
+  }
+
   if(!tempstore) {
     result = CURLE_OUT_OF_MEMORY;
     goto fail;
   }
 
   result = CURLE_WRITE_ERROR;
-  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600);
+  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode);
   if(fd == -1)
     goto fail;
 
-#ifdef HAVE_FCHMOD
-  {
-    struct_stat nsb;
-    if((fstat(fd, &nsb) != -1) &&
-       (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
-      /* if the user and group are the same, clone the original mode */
-      if(fchmod(fd, (mode_t)sb.st_mode) == -1)
-        goto fail;
-    }
-  }
-#endif
-
   *fh = fdopen(fd, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
@@ -105,7 +147,6 @@
   }
 
   free(tempstore);
-
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index e40c4bc..d6a1697 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -277,7 +277,7 @@
     case CURLFORM_PTRNAME:
       current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
 
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case CURLFORM_COPYNAME:
       if(current_form->name)
         return_value = CURL_FORMADD_OPTION_TWICE;
@@ -303,7 +303,7 @@
        */
     case CURLFORM_PTRCONTENTS:
       current_form->flags |= HTTPPOST_PTRCONTENTS;
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case CURLFORM_COPYCONTENTS:
       if(current_form->value)
         return_value = CURL_FORMADD_OPTION_TWICE;
@@ -603,9 +603,9 @@
            app passed in a bad combo, so we better check for that first. */
         if(form->name) {
           /* copy name (without strdup; possibly not null-terminated) */
-          form->name = Curl_memdup(form->name, form->namelength?
-                                   form->namelength:
-                                   strlen(form->name) + 1);
+          form->name = Curl_memdup0(form->name, form->namelength?
+                                    form->namelength:
+                                    strlen(form->name));
         }
         if(!form->name) {
           return_value = CURL_FORMADD_MEMORY;
@@ -779,11 +779,9 @@
 
   if(!name || !len)
     return curl_mime_name(part, name);
-  zname = malloc(len + 1);
+  zname = Curl_memdup0(name, len);
   if(!zname)
     return CURLE_OUT_OF_MEMORY;
-  memcpy(zname, name, len);
-  zname[len] = '\0';
   res = curl_mime_name(part, zname);
   free(zname);
   return res;
@@ -792,7 +790,7 @@
 /* wrap call to fseeko so it matches the calling convention of callback */
 static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
 {
-#if defined(HAVE_FSEEKO)
+#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
   return fseeko(stream, (off_t)offset, whence);
 #elif defined(HAVE__FSEEKI64)
   return _fseeki64(stream, (__int64)offset, whence);
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 6e7fda0..58d6e1d 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -72,6 +72,7 @@
 #include "warnless.h"
 #include "http_proxy.h"
 #include "socks.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -167,7 +168,7 @@
   ftp_domore_getsock,              /* domore_getsock */
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
-  ZERO_NULL,                       /* readwrite */
+  ZERO_NULL,                       /* write_resp */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   PORT_FTP,                        /* defport */
@@ -198,7 +199,7 @@
   ftp_domore_getsock,              /* domore_getsock */
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
-  ZERO_NULL,                       /* readwrite */
+  ZERO_NULL,                       /* write_resp */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   PORT_FTPS,                       /* defport */
@@ -362,10 +363,11 @@
   curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct pingpong *pp = &ftpc->pp;
-  int result;
+  int socketstate = 0;
   timediff_t timeout_ms;
   ssize_t nread;
   int ftpcode;
+  bool response = FALSE;
 
   *received = FALSE;
 
@@ -378,17 +380,21 @@
   }
 
   /* First check whether there is a cached response from server */
-  if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
+  if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
     /* Data connection could not be established, let's return */
     infof(data, "There is negative response in cache while serv connect");
     (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
     return CURLE_FTP_ACCEPT_FAILED;
   }
 
-  result = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
+  if(pp->overflow)
+    /* there is pending control data still in the buffer to read */
+    response = TRUE;
+  else
+    socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
 
   /* see if the connection request is already here */
-  switch(result) {
+  switch(socketstate) {
   case -1: /* error */
     /* let's die here */
     failf(data, "Error while waiting for server connect");
@@ -396,23 +402,23 @@
   case 0:  /* Server connect is not received yet */
     break; /* loop */
   default:
-
-    if(result & CURL_CSELECT_IN2) {
+    if(socketstate & CURL_CSELECT_IN2) {
       infof(data, "Ready to accept data connection from server");
       *received = TRUE;
     }
-    else if(result & CURL_CSELECT_IN) {
-      infof(data, "Ctrl conn has data while waiting for data conn");
-      (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
-
-      if(ftpcode/100 > 3)
-        return CURLE_FTP_ACCEPT_FAILED;
-
-      return CURLE_WEIRD_SERVER_REPLY;
-    }
-
+    else if(socketstate & CURL_CSELECT_IN)
+      response = TRUE;
     break;
-  } /* switch() */
+  }
+  if(response) {
+    infof(data, "Ctrl conn has data while waiting for data conn");
+    (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
+
+    if(ftpcode/100 > 3)
+      return CURLE_FTP_ACCEPT_FAILED;
+
+    return CURLE_WEIRD_SERVER_REPLY;
+  }
 
   return CURLE_OK;
 }
@@ -553,7 +559,7 @@
 #ifdef HAVE_GSSAPI
   {
     struct connectdata *conn = data->conn;
-    char * const buf = data->state.buffer;
+    char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
 
     /* handle the security-oriented responses 6xx ***/
     switch(code) {
@@ -659,7 +665,7 @@
      *
      */
 
-    if(pp->cache && (cache_skip < 2)) {
+    if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
       /*
        * There's a cache left since before. We then skipping the wait for
        * socket action, unless this is the same cache like the previous round
@@ -687,7 +693,7 @@
     if(result)
       break;
 
-    if(!nread && pp->cache)
+    if(!nread && Curl_dyn_len(&pp->recvbuf))
       /* bump cache skip counter as on repeated skips we must wait for more
          data */
       cache_skip++;
@@ -819,7 +825,7 @@
   DEBUGF(infof(data, "ftp_domore_getsock()"));
   if(conn->cfilter[SECONDARYSOCKET]
      && !Curl_conn_is_connected(conn, SECONDARYSOCKET))
-    return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks);
+    return 0;
 
   if(FTP_STOP == ftpc->state) {
     int bits = GETSOCK_READSOCK(0);
@@ -926,6 +932,8 @@
   bool possibly_non_local = TRUE;
   char buffer[STRERROR_LEN];
   char *addr = NULL;
+  size_t addrlen = 0;
+  char ipstr[50];
 
   /* Step 1, figure out what is requested,
    * accepted format :
@@ -934,32 +942,17 @@
 
   if(data->set.str[STRING_FTPPORT] &&
      (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
-
-#ifdef ENABLE_IPV6
-    size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
-      INET6_ADDRSTRLEN : strlen(string_ftpport);
-#else
-    size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
-      INET_ADDRSTRLEN : strlen(string_ftpport);
-#endif
-    char *ip_start = string_ftpport;
     char *ip_end = NULL;
-    char *port_start = NULL;
-    char *port_sep = NULL;
-
-    addr = calloc(addrlen + 1, 1);
-    if(!addr) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
 
 #ifdef ENABLE_IPV6
     if(*string_ftpport == '[') {
       /* [ipv6]:port(-range) */
-      ip_start = string_ftpport + 1;
-      ip_end = strchr(string_ftpport, ']');
-      if(ip_end)
-        strncpy(addr, ip_start, ip_end - ip_start);
+      char *ip_start = string_ftpport + 1;
+      ip_end = strchr(ip_start, ']');
+      if(ip_end) {
+        addrlen = ip_end - ip_start;
+        addr = ip_start;
+      }
     }
     else
 #endif
@@ -969,28 +962,27 @@
       }
       else {
         ip_end = strchr(string_ftpport, ':');
+        addr = string_ftpport;
         if(ip_end) {
           /* either ipv6 or (ipv4|domain|interface):port(-range) */
+          addrlen = ip_end - string_ftpport;
 #ifdef ENABLE_IPV6
           if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
             /* ipv6 */
             port_min = port_max = 0;
-            strcpy(addr, string_ftpport);
             ip_end = NULL; /* this got no port ! */
           }
-          else
 #endif
-            /* (ipv4|domain|interface):port(-range) */
-            strncpy(addr, string_ftpport, ip_end - ip_start);
         }
         else
           /* ipv4|interface */
-          strcpy(addr, string_ftpport);
+          addrlen = strlen(string_ftpport);
       }
 
     /* parse the port */
     if(ip_end) {
-      port_start = strchr(ip_end, ':');
+      char *port_sep = NULL;
+      char *port_start = strchr(ip_end, ':');
       if(port_start) {
         port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
         port_sep = strchr(port_start, '-');
@@ -1011,22 +1003,29 @@
     if(port_min > port_max)
       port_min = port_max = 0;
 
-    if(*addr != '\0') {
+    if(addrlen) {
+      DEBUGASSERT(addr);
+      if(addrlen >= sizeof(ipstr))
+        goto out;
+      memcpy(ipstr, addr, addrlen);
+      ipstr[addrlen] = 0;
+
       /* attempt to get the address of the given interface name */
       switch(Curl_if2ip(conn->remote_addr->family,
 #ifdef ENABLE_IPV6
                         Curl_ipv6_scope(&conn->remote_addr->sa_addr),
                         conn->scope_id,
 #endif
-                        addr, hbuf, sizeof(hbuf))) {
+                        ipstr, hbuf, sizeof(hbuf))) {
         case IF2IP_NOT_FOUND:
           /* not an interface, use the given string as host name instead */
-          host = addr;
+          host = ipstr;
           break;
         case IF2IP_AF_NOT_SUPPORTED:
           goto out;
         case IF2IP_FOUND:
           host = hbuf; /* use the hbuf for host name */
+          break;
       }
     }
     else
@@ -1266,7 +1265,6 @@
   }
   if(portsock != CURL_SOCKET_BAD)
     Curl_socket_close(data, conn, portsock);
-  free(addr);
   return result;
 }
 
@@ -1589,13 +1587,14 @@
       }
       /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
       do {
+        char scratch[4*1024];
         size_t readthisamountnow =
-          (data->state.resume_from - passed > data->set.buffer_size) ?
-          (size_t)data->set.buffer_size :
+          (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
+          sizeof(scratch) :
           curlx_sotouz(data->state.resume_from - passed);
 
         size_t actuallyread =
-          data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+          data->state.fread_func(scratch, 1, readthisamountnow,
                                  data->state.in);
 
         passed += actuallyread;
@@ -1828,7 +1827,9 @@
   struct Curl_dns_entry *addr = NULL;
   enum resolve_t rc;
   unsigned short connectport; /* the local port connect() should use! */
-  char *str = &data->state.buffer[4];  /* start on the first letter */
+  struct pingpong *pp = &ftpc->pp;
+  char *str =
+    Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
 
   /* if we come here again, make sure the former name is cleared */
   Curl_safefree(ftpc->newhost);
@@ -2106,8 +2107,9 @@
       /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
          last .sss part is optional and means fractions of a second */
       int year, month, day, hour, minute, second;
-      if(ftp_213_date(&data->state.buffer[4],
-                      &year, &month, &day, &hour, &minute, &second)) {
+      struct pingpong *pp = &ftpc->pp;
+      char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
+      if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
         /* we have a time, reformat it */
         char timebuf[24];
         msnprintf(timebuf, sizeof(timebuf),
@@ -2318,7 +2320,8 @@
 {
   CURLcode result = CURLE_OK;
   curl_off_t filesize = -1;
-  char *buf = data->state.buffer;
+  char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
+  size_t len = data->conn->proto.ftpc.pp.nfinal;
 
   /* get the size from the ascii string: */
   if(ftpcode == 213) {
@@ -2326,13 +2329,13 @@
        for all the digits at the end of the response and parse only those as a
        number. */
     char *start = &buf[4];
-    char *fdigit = strchr(start, '\r');
+    char *fdigit = memchr(start, '\r', len);
     if(fdigit) {
-      do
+      fdigit--;
+      if(*fdigit == '\n')
         fdigit--;
-      while(ISDIGIT(*fdigit) && (fdigit > start));
-      if(!ISDIGIT(*fdigit))
-        fdigit++;
+      while(ISDIGIT(fdigit[-1]) && (fdigit > start))
+        fdigit--;
     }
     else
       fdigit = start;
@@ -2501,7 +2504,7 @@
        *
        * Example D above makes this parsing a little tricky */
       char *bytes;
-      char *buf = data->state.buffer;
+      char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
       bytes = strstr(buf, " bytes");
       if(bytes) {
         long in = (long)(--bytes-buf);
@@ -2770,7 +2773,7 @@
     case FTP_AUTH:
       /* we have gotten the response to a previous AUTH command */
 
-      if(pp->cache_size)
+      if(pp->overflow)
         return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
 
       /* RFC2228 (page 5) says:
@@ -2868,14 +2871,11 @@
 
     case FTP_PWD:
       if(ftpcode == 257) {
-        char *ptr = &data->state.buffer[4];  /* start on the first letter */
-        const size_t buf_size = data->set.buffer_size;
-        char *dir;
+        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+                                                       letter */
         bool entry_extracted = FALSE;
-
-        dir = malloc(nread + 1);
-        if(!dir)
-          return CURLE_OUT_OF_MEMORY;
+        struct dynbuf out;
+        Curl_dyn_init(&out, 1000);
 
         /* Reply format is like
            257<space>[rubbish]"<directory-name>"<space><commentary> and the
@@ -2887,33 +2887,30 @@
         */
 
         /* scan for the first double-quote for non-standard responses */
-        while(ptr < &data->state.buffer[buf_size]
-              && *ptr != '\n' && *ptr != '\0' && *ptr != '"')
+        while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
           ptr++;
 
         if('\"' == *ptr) {
           /* it started good */
-          char *store;
-          ptr++;
-          for(store = dir; *ptr;) {
+          for(ptr++; *ptr; ptr++) {
             if('\"' == *ptr) {
               if('\"' == ptr[1]) {
                 /* "quote-doubling" */
-                *store = ptr[1];
+                result = Curl_dyn_addn(&out, &ptr[1], 1);
                 ptr++;
               }
               else {
                 /* end of path */
-                entry_extracted = TRUE;
+                if(Curl_dyn_len(&out))
+                  entry_extracted = TRUE;
                 break; /* get out of this loop */
               }
             }
             else
-              *store = *ptr;
-            store++;
-            ptr++;
+              result = Curl_dyn_addn(&out, ptr, 1);
+            if(result)
+              return result;
           }
-          *store = '\0'; /* null-terminate */
         }
         if(entry_extracted) {
           /* If the path name does not look like an absolute path (i.e.: it
@@ -2927,6 +2924,7 @@
                The method used here is to check the server OS: we do it only
              if the path name looks strange to minimize overhead on other
              systems. */
+          char *dir = Curl_dyn_ptr(&out);
 
           if(!ftpc->server_os && dir[0] != '/') {
             result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
@@ -2951,7 +2949,7 @@
         }
         else {
           /* couldn't get the path */
-          free(dir);
+          Curl_dyn_free(&out);
           infof(data, "Failed to figure out path");
         }
       }
@@ -2961,25 +2959,23 @@
 
     case FTP_SYST:
       if(ftpcode == 215) {
-        char *ptr = &data->state.buffer[4];  /* start on the first letter */
+        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+                                                       letter */
         char *os;
-        char *store;
-
-        os = malloc(nread + 1);
-        if(!os)
-          return CURLE_OUT_OF_MEMORY;
+        char *start;
 
         /* Reply format is like
            215<space><OS-name><space><commentary>
         */
         while(*ptr == ' ')
           ptr++;
-        for(store = os; *ptr && *ptr != ' ';)
-          *store++ = *ptr++;
-        *store = '\0'; /* null-terminate */
+        for(start = ptr; *ptr && *ptr != ' '; ptr++)
+          ;
+        os = Curl_memdup0(start, ptr - start);
+        if(!os)
+          return CURLE_OUT_OF_MEMORY;
 
         /* Check for special servers here. */
-
         if(strcasecompare(os, "OS/400")) {
           /* Force OS400 name format 1. */
           result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
@@ -3131,7 +3127,6 @@
       break;
 
     case FTP_QUIT:
-      /* fallthrough, just stop! */
     default:
       /* internal error */
       ftp_state(data, FTP_STOP);
@@ -3206,8 +3201,7 @@
     conn->bits.ftp_use_control_ssl = TRUE;
   }
 
-  Curl_pp_setup(pp); /* once per transfer */
-  Curl_pp_init(data, pp); /* init the generic pingpong data */
+  Curl_pp_init(pp); /* once per transfer */
 
   /* When we connect, we start in the state where we await the 220
      response */
@@ -3258,14 +3252,13 @@
   case CURLE_REMOTE_FILE_NOT_FOUND:
   case CURLE_WRITE_ERROR:
     /* the connection stays alive fine even though this happened */
-    /* fall-through */
   case CURLE_OK: /* doesn't affect the control connection's status */
     if(!premature)
       break;
 
     /* until we cope better with prematurely ended requests, let them
      * fallback as if in complete failure */
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   default:       /* by default, an error means the control connection is
                     wedged and should not be used anymore */
     ftpc->ctl_valid = FALSE;
@@ -4178,13 +4171,12 @@
           return CURLE_OUT_OF_MEMORY;
         }
 
-        ftpc->dirs[0] = calloc(1, dirlen + 1);
+        ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
         if(!ftpc->dirs[0]) {
           free(rawPath);
           return CURLE_OUT_OF_MEMORY;
         }
 
-        strncpy(ftpc->dirs[0], rawPath, dirlen);
         ftpc->dirdepth = 1; /* we consider it to be a single dir */
         fileName = slashPos + 1; /* rest is file name */
       }
@@ -4223,12 +4215,11 @@
              CWD requires a parameter and a non-existent parameter a) doesn't
              work on many servers and b) has no effect on the others. */
           if(compLen > 0) {
-            char *comp = calloc(1, compLen + 1);
+            char *comp = Curl_memdup0(curPos, compLen);
             if(!comp) {
               free(rawPath);
               return CURLE_OUT_OF_MEMORY;
             }
-            strncpy(comp, curPos, compLen);
             ftpc->dirs[ftpc->dirdepth++] = comp;
           }
           curPos = slashPos + 1;
@@ -4380,7 +4371,7 @@
   CURLcode result = CURLE_OK;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
 
-  ftp = calloc(sizeof(struct FTP), 1);
+  ftp = calloc(1, sizeof(struct FTP));
   if(!ftp)
     return CURLE_OUT_OF_MEMORY;
 
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index 2a7ca5b..82f1ea0 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -55,9 +55,6 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-/* allocs buffer which will contain one line of LIST command response */
-#define FTP_BUFFER_ALLOCSIZE 160
-
 typedef enum {
   PL_UNIX_TOTALSIZE = 0,
   PL_UNIX_FILETYPE,
diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h
index 075c02e..ea66d32 100644
--- a/Utilities/cmcurl/lib/functypes.h
+++ b/Utilities/cmcurl/lib/functypes.h
@@ -38,7 +38,7 @@
    2. For systems with config-*.h files, define them there.
 */
 
-#ifdef WIN32
+#ifdef _WIN32
 /* int recv(SOCKET, char *, int, int) */
 #define RECV_TYPE_ARG1 SOCKET
 #define RECV_TYPE_ARG2 char *
diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c
index 8069784..48ee972 100644
--- a/Utilities/cmcurl/lib/getenv.c
+++ b/Utilities/cmcurl/lib/getenv.c
@@ -31,10 +31,11 @@
 
 static char *GetEnv(const char *variable)
 {
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP)
+#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) || \
+  defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */
   (void)variable;
   return NULL;
-#elif defined(WIN32)
+#elif defined(_WIN32)
   /* This uses Windows API instead of C runtime getenv() to get the environment
      variable since some changes aren't always visible to the latter. #4774 */
   char *buf = NULL;
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index f1574e0..2f74629 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -409,6 +409,9 @@
   case CURLINFO_STARTTRANSFER_TIME_T:
     *param_offt = data->progress.t_starttransfer;
     break;
+  case CURLINFO_QUEUE_TIME_T:
+    *param_offt = data->progress.t_postqueue;
+    break;
   case CURLINFO_REDIRECT_TIME_T:
     *param_offt = data->progress.t_redirect;
     break;
@@ -420,7 +423,7 @@
     break;
   case CURLINFO_CONN_ID:
     *param_offt = data->conn?
-                  data->conn->connection_id : data->state.recent_conn_id;
+      data->conn->connection_id : data->state.recent_conn_id;
     break;
   default:
     return CURLE_UNKNOWN_OPTION;
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index 61e41b7..9ca0828 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -75,7 +75,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
@@ -99,7 +99,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c
index 3ff4d5e..8a3264a 100644
--- a/Utilities/cmcurl/lib/headers.c
+++ b/Utilities/cmcurl/lib/headers.c
@@ -185,7 +185,7 @@
 }
 
 static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
-                           char **name, char **value)
+                          char **name, char **value)
 {
   char *end = header + hlen - 1; /* point to the last byte */
   DEBUGASSERT(hlen);
@@ -292,9 +292,10 @@
   if(!end) {
     end = strchr(header, '\n');
     if(!end)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
+      /* neither CR nor LF as terminator is not a valid header */
+      return CURLE_WEIRD_SERVER_REPLY;
   }
-  hlen = end - header + 1;
+  hlen = end - header;
 
   if((header[0] == ' ') || (header[0] == '\t')) {
     if(data->state.prevhead)
@@ -319,21 +320,19 @@
   hs->buffer[hlen] = 0; /* nul terminate */
 
   result = namevalue(hs->buffer, hlen, type, &name, &value);
-  if(result)
-    goto fail;
+  if(!result) {
+    hs->name = name;
+    hs->value = value;
+    hs->type = type;
+    hs->request = data->state.requests;
 
-  hs->name = name;
-  hs->value = value;
-  hs->type = type;
-  hs->request = data->state.requests;
-
-  /* insert this node into the list of headers */
-  Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
-                         hs, &hs->node);
-  data->state.prevhead = hs;
-  return CURLE_OK;
-fail:
-  free(hs);
+    /* insert this node into the list of headers */
+    Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
+                           hs, &hs->node);
+    data->state.prevhead = hs;
+  }
+  else
+    free(hs);
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index 3cd9a65..4f44d34 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -117,6 +117,13 @@
 
 static void freednsentry(void *freethis);
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void show_resolve_info(struct Curl_easy *data,
+                              struct Curl_dns_entry *dns);
+#else
+#define show_resolve_info(x,y) Curl_nop_stmt
+#endif
+
 /*
  * Curl_printable_address() stores a printable version of the 1st address
  * given in the 'ai' argument. The result will be stored in the buf that is
@@ -481,9 +488,11 @@
       return NULL;
   }
 #endif
+  if(!hostlen)
+    hostlen = strlen(hostname);
 
   /* Create a new cache entry */
-  dns = calloc(1, sizeof(struct Curl_dns_entry));
+  dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
   if(!dns) {
     return NULL;
   }
@@ -497,6 +506,9 @@
   time(&dns->timestamp);
   if(dns->timestamp == 0)
     dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
+  dns->hostport = port;
+  if(hostlen)
+    memcpy(dns->hostname, hostname, hostlen);
 
   /* Store the resolved data in our DNS cache. */
   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
@@ -521,7 +533,7 @@
   struct sockaddr_in6 sa6;
   unsigned char ipv6[16];
   unsigned short port16 = (unsigned short)(port & 0xffff);
-  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
   if(!ca)
     return NULL;
 
@@ -568,7 +580,7 @@
     return NULL;
   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
 
-  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
   if(!ca)
     return NULL;
   ca->ai_flags     = 0;
@@ -742,16 +754,22 @@
 
 #ifndef USE_RESOLVE_ON_IPS
     /* First check if this is an IPv4 address string */
-    if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
+    if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
       /* This is a dotted IP address 123.123.123.123-style */
       addr = Curl_ip2addr(AF_INET, &in, hostname, port);
+      if(!addr)
+        return CURLRESOLV_ERROR;
+    }
 #ifdef ENABLE_IPV6
-    if(!addr) {
+    else {
       struct in6_addr in6;
       /* check if this is an IPv6 address string */
-      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
+      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
         /* This is an IPv6 address literal */
         addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
+        if(!addr)
+          return CURLRESOLV_ERROR;
+      }
     }
 #endif /* ENABLE_IPV6 */
 
@@ -823,8 +841,10 @@
       if(!dns)
         /* returned failure, bail out nicely */
         Curl_freeaddrinfo(addr);
-      else
+      else {
         rc = CURLRESOLV_RESOLVED;
+        show_resolve_info(data, dns);
+      }
     }
   }
 
@@ -839,7 +859,7 @@
  * execution.  This effectively causes the remainder of the application to run
  * within a signal handler which is nonportable and could lead to problems.
  */
-static
+CURL_NORETURN static
 void alarmfunc(int sig)
 {
   (void)sig;
@@ -1269,9 +1289,11 @@
         Curl_freeaddrinfo(head);
         return CURLE_OUT_OF_MEMORY;
       }
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
       infof(data, "Added %.*s:%d:%s to DNS cache%s",
             (int)hlen, host_begin, port, addresses,
             permanent ? "" : " (non-permanent)");
+#endif
 
       /* Wildcard hostname */
       if((hlen == 1) && (host_begin[0] == '*')) {
@@ -1285,18 +1307,89 @@
   return CURLE_OK;
 }
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void show_resolve_info(struct Curl_easy *data,
+                              struct Curl_dns_entry *dns)
+{
+  struct Curl_addrinfo *a;
+  CURLcode result = CURLE_OK;
+#ifdef CURLRES_IPV6
+  struct dynbuf out[2];
+#else
+  struct dynbuf out[1];
+#endif
+  DEBUGASSERT(data);
+  DEBUGASSERT(dns);
+
+  if(!data->set.verbose ||
+     /* ignore no name or numerical IP addresses */
+     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
+    return;
+
+  a = dns->addr;
+
+  infof(data, "Host %s:%d was resolved.",
+        (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
+
+  Curl_dyn_init(&out[0], 1024);
+#ifdef CURLRES_IPV6
+  Curl_dyn_init(&out[1], 1024);
+#endif
+
+  while(a) {
+    if(
+#ifdef CURLRES_IPV6
+       a->ai_family == PF_INET6 ||
+#endif
+       a->ai_family == PF_INET) {
+      char buf[MAX_IPADR_LEN];
+      struct dynbuf *d = &out[(a->ai_family != PF_INET)];
+      Curl_printable_address(a, buf, sizeof(buf));
+      if(Curl_dyn_len(d))
+        result = Curl_dyn_addn(d, ", ", 2);
+      if(!result)
+        result = Curl_dyn_add(d, buf);
+      if(result) {
+        infof(data, "too many IP, can't show");
+        goto fail;
+      }
+    }
+    a = a->ai_next;
+  }
+
+#ifdef CURLRES_IPV6
+  infof(data, "IPv6: %s",
+        (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
+#endif
+  infof(data, "IPv4: %s",
+        (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
+
+fail:
+  Curl_dyn_free(&out[0]);
+#ifdef CURLRES_IPV6
+  Curl_dyn_free(&out[1]);
+#endif
+}
+#endif
+
 CURLcode Curl_resolv_check(struct Curl_easy *data,
                            struct Curl_dns_entry **dns)
 {
+  CURLcode result;
 #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
   (void)data;
   (void)dns;
 #endif
 #ifndef CURL_DISABLE_DOH
-  if(data->conn->bits.doh)
-    return Curl_doh_is_resolved(data, dns);
+  if(data->conn->bits.doh) {
+    result = Curl_doh_is_resolved(data, dns);
+  }
+  else
 #endif
-  return Curl_resolver_is_resolved(data, dns);
+  result = Curl_resolver_is_resolved(data, dns);
+  if(*dns)
+    show_resolve_info(data, *dns);
+  return result;
 }
 
 int Curl_resolv_getsock(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index b68f539..fb53a57 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -64,6 +64,10 @@
   time_t timestamp;
   /* use-counter, use Curl_resolv_unlock to release reference */
   long inuse;
+  /* hostname port number that resolved to addr. */
+  int hostport;
+  /* hostname that resolved to addr. may be NULL (unix domain sockets). */
+  char hostname[1];
 };
 
 bool Curl_host_is_ipnum(const char *hostname);
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index 6b0ba55..18969a7 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -71,8 +71,7 @@
 #if defined(CURLRES_SYNCH)
 
 #ifdef DEBUG_ADDRINFO
-static void dump_addrinfo(struct connectdata *conn,
-                          const struct Curl_addrinfo *ai)
+static void dump_addrinfo(const struct Curl_addrinfo *ai)
 {
   printf("dump_addrinfo:\n");
   for(; ai; ai = ai->ai_next) {
@@ -84,7 +83,7 @@
   }
 }
 #else
-#define dump_addrinfo(x,y) Curl_nop_stmt
+#define dump_addrinfo(x) Curl_nop_stmt
 #endif
 
 /*
@@ -149,7 +148,7 @@
     Curl_addrinfo_set_port(res, port);
   }
 
-  dump_addrinfo(conn, res);
+  dump_addrinfo(res);
 
   return res;
 }
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index 7ecf004..8725a35 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -40,6 +40,7 @@
 #include "fopen.h"
 #include "rename.h"
 #include "share.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -76,7 +77,7 @@
 
 struct hsts *Curl_hsts_init(void)
 {
-  struct hsts *h = calloc(sizeof(struct hsts), 1);
+  struct hsts *h = calloc(1, sizeof(struct hsts));
   if(h) {
     Curl_llist_init(&h->list, NULL);
   }
@@ -108,7 +109,7 @@
 
 static struct stsentry *hsts_entry(void)
 {
-  return calloc(sizeof(struct stsentry), 1);
+  return calloc(1, sizeof(struct stsentry));
 }
 
 static CURLcode hsts_create(struct hsts *h,
@@ -116,27 +117,31 @@
                             bool subdomains,
                             curl_off_t expires)
 {
-  struct stsentry *sts = hsts_entry();
-  char *duphost;
   size_t hlen;
-  if(!sts)
-    return CURLE_OUT_OF_MEMORY;
+  DEBUGASSERT(h);
+  DEBUGASSERT(hostname);
 
-  duphost = strdup(hostname);
-  if(!duphost) {
-    free(sts);
-    return CURLE_OUT_OF_MEMORY;
+  hlen = strlen(hostname);
+  if(hlen && (hostname[hlen - 1] == '.'))
+    /* strip off any trailing dot */
+    --hlen;
+  if(hlen) {
+    char *duphost;
+    struct stsentry *sts = hsts_entry();
+    if(!sts)
+      return CURLE_OUT_OF_MEMORY;
+
+    duphost = Curl_memdup0(hostname, hlen);
+    if(!duphost) {
+      free(sts);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    sts->host = duphost;
+    sts->expires = expires;
+    sts->includeSubDomains = subdomains;
+    Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
   }
-
-  hlen = strlen(duphost);
-  if(duphost[hlen - 1] == '.')
-    /* strip off trailing any dot */
-    duphost[--hlen] = 0;
-
-  sts->host = duphost;
-  sts->expires = expires;
-  sts->includeSubDomains = subdomains;
-  Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
   return CURLE_OK;
 }
 
@@ -473,6 +478,7 @@
       if(sc == CURLSTS_OK) {
         time_t expires;
         CURLcode result;
+        DEBUGASSERT(e.name[0]);
         if(!e.name[0])
           /* bail out if no name was stored */
           return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -564,7 +570,7 @@
 
 void Curl_hsts_loadfiles(struct Curl_easy *data)
 {
-  struct curl_slist *l = data->set.hstslist;
+  struct curl_slist *l = data->state.hstslist;
   if(l) {
     Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
 
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 40ef70d..679931e 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -100,24 +100,14 @@
  * Forward declarations.
  */
 
-static int http_getsock_do(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           curl_socket_t *socks);
 static bool http_should_fail(struct Curl_easy *data);
 
-static CURLcode http_setup_conn(struct Curl_easy *data,
-                                struct connectdata *conn);
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
-                              struct connectdata *conn);
-#endif
-
 /*
  * HTTP handler interface.
  */
 const struct Curl_handler Curl_handler_http = {
   "HTTP",                               /* scheme */
-  http_setup_conn,                      /* setup_connection */
+  Curl_http_setup_conn,                 /* setup_connection */
   Curl_http,                            /* do_it */
   Curl_http_done,                       /* done */
   ZERO_NULL,                            /* do_more */
@@ -125,11 +115,11 @@
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
   ZERO_NULL,                            /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
+  Curl_http_getsock_do,                 /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  Curl_http_write_resp,                 /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTP,                            /* defport */
@@ -139,39 +129,13 @@
   PROTOPT_USERPWDCTRL
 };
 
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_ws = {
-  "WS",                                 /* scheme */
-  ws_setup_conn,                        /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  Curl_http_connect,                    /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  Curl_ws_disconnect,                   /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  PORT_HTTP,                            /* defport */
-  CURLPROTO_WS,                         /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_CREDSPERREQUEST |             /* flags */
-  PROTOPT_USERPWDCTRL
-};
-#endif
-
 #ifdef USE_SSL
 /*
  * HTTPS handler interface.
  */
 const struct Curl_handler Curl_handler_https = {
   "HTTPS",                              /* scheme */
-  http_setup_conn,                      /* setup_connection */
+  Curl_http_setup_conn,                 /* setup_connection */
   Curl_http,                            /* do_it */
   Curl_http_done,                       /* done */
   ZERO_NULL,                            /* do_more */
@@ -179,11 +143,11 @@
   NULL,                                 /* connecting */
   ZERO_NULL,                            /* doing */
   NULL,                                 /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
+  Curl_http_getsock_do,                 /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  Curl_http_write_resp,                 /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_HTTPS,                           /* defport */
@@ -193,36 +157,10 @@
   PROTOPT_USERPWDCTRL
 };
 
-#ifdef USE_WEBSOCKETS
-const struct Curl_handler Curl_handler_wss = {
-  "WSS",                                /* scheme */
-  ws_setup_conn,                        /* setup_connection */
-  Curl_http,                            /* do_it */
-  Curl_http_done,                       /* done */
-  ZERO_NULL,                            /* do_more */
-  Curl_http_connect,                    /* connect_it */
-  NULL,                                 /* connecting */
-  ZERO_NULL,                            /* doing */
-  NULL,                                 /* proto_getsock */
-  http_getsock_do,                      /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
-  Curl_ws_disconnect,                   /* disconnect */
-  ZERO_NULL,                            /* readwrite */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  PORT_HTTPS,                           /* defport */
-  CURLPROTO_WSS,                        /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
-  PROTOPT_USERPWDCTRL
-};
 #endif
 
-#endif
-
-static CURLcode http_setup_conn(struct Curl_easy *data,
-                                struct connectdata *conn)
+CURLcode Curl_http_setup_conn(struct Curl_easy *data,
+                              struct connectdata *conn)
 {
   /* allocate the HTTP-specific struct for the Curl_easy, only to survive
      during this request */
@@ -245,16 +183,6 @@
   return CURLE_OK;
 }
 
-#ifdef USE_WEBSOCKETS
-static CURLcode ws_setup_conn(struct Curl_easy *data,
-                              struct connectdata *conn)
-{
-  /* websockets is 1.1 only (for now) */
-  data->state.httpwant = CURL_HTTP_VERSION_1_1;
-  return http_setup_conn(data, conn);
-}
-#endif
-
 #ifndef CURL_DISABLE_PROXY
 /*
  * checkProxyHeaders() checks the linked list of custom proxy headers
@@ -297,7 +225,6 @@
 {
   const char *start;
   const char *end;
-  char *value;
   size_t len;
 
   /* Find the end of the header name */
@@ -330,14 +257,7 @@
   /* get length of the type */
   len = end - start + 1;
 
-  value = malloc(len + 1);
-  if(!value)
-    return NULL;
-
-  memcpy(value, start, len);
-  value[len] = 0; /* null-terminate */
-
-  return value;
+  return Curl_memdup0(start, len);
 }
 
 #ifndef CURL_DISABLE_HTTP_AUTH
@@ -836,6 +756,7 @@
           (data->state.aptr.user ?
            data->state.aptr.user : ""));
 #else
+    (void)proxy;
     infof(data, "Server auth using %s with user '%s'",
           auth, data->state.aptr.user ?
           data->state.aptr.user : "");
@@ -845,7 +766,7 @@
   else
     authstatus->multipass = FALSE;
 
-  return CURLE_OK;
+  return result;
 }
 
 /**
@@ -970,17 +891,21 @@
 }
 #endif
 
+#if defined(USE_SPNEGO) || defined(USE_NTLM) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
+  !defined(CURL_DISABLE_BASIC_AUTH) || \
+  !defined(CURL_DISABLE_BEARER_AUTH)
+static int is_valid_auth_separator(char ch)
+{
+  return ch == '\0' || ch == ',' || ISSPACE(ch);
+}
+#endif
+
 /*
  * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
  * headers. They are dealt with both in the transfer.c main loop and in the
  * proxy CONNECT loop.
  */
-
-static int is_valid_auth_separator(char ch)
-{
-  return ch == '\0' || ch == ',' || ISSPACE(ch);
-}
-
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                               const char *auth) /* the first non-space */
 {
@@ -992,11 +917,15 @@
   curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
                                     &conn->http_negotiate_state;
 #endif
+#if defined(USE_SPNEGO) || \
+  defined(USE_NTLM) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
+  !defined(CURL_DISABLE_BASIC_AUTH) || \
+  !defined(CURL_DISABLE_BEARER_AUTH)
+
   unsigned long *availp;
   struct auth *authp;
 
-  (void) conn; /* In case conditionals make it unused. */
-
   if(proxy) {
     availp = &data->info.proxyauthavail;
     authp = &data->state.authproxy;
@@ -1005,6 +934,11 @@
     availp = &data->info.httpauthavail;
     authp = &data->state.authhost;
   }
+#else
+  (void) proxy;
+#endif
+
+  (void) conn; /* In case conditionals make it unused. */
 
   /*
    * Here we check if we want the specific single authentication (using ==) and
@@ -1140,7 +1074,14 @@
               }
             }
 #else
-           ;
+            {
+              /*
+               * Empty block to terminate the if-else chain correctly.
+               *
+               * A semicolon would yield the same result here, but can cause a
+               * compiler warning when -Wextra is enabled.
+               */
+            }
 #endif
 
     /* there may be multiple methods on one line, so keep reading */
@@ -1403,7 +1344,7 @@
      *   and install our own `data->state.fread_func` that
      *   on subsequent calls reads `in` empty.
      * - when the whisked away `in` is empty, the `fread_func`
-     *   is restored ot its original state.
+     *   is restored to its original state.
      * The problem is that `fread_func` can only return
      * `upload_buffer_size` lengths. If the send we do here
      * is larger and blocks, we do re-sending with smaller
@@ -1576,9 +1517,9 @@
 /* this returns the socket to wait for in the DO and DOING state for the multi
    interface and then we're always _sending_ a request and thus we wait for
    the single socket to become writable only */
-static int http_getsock_do(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           curl_socket_t *socks)
+int Curl_http_getsock_do(struct Curl_easy *data,
+                         struct connectdata *conn,
+                         curl_socket_t *socks)
 {
   /* write mode */
   (void)conn;
@@ -1678,8 +1619,6 @@
                           struct dynbuf *req)
 {
   CURLcode result = CURLE_OK;
-  data->state.expect100header = FALSE; /* default to false unless it is set
-                                          to TRUE below */
   if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
      (conn->httpversion < 20)) {
     /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
@@ -2084,6 +2023,7 @@
 
   switch(data->set.timecondition) {
   default:
+    DEBUGF(infof(data, "invalid time condition"));
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
   case CURL_TIMECOND_IFMODSINCE:
@@ -2252,7 +2192,7 @@
     }
 #endif
 
-    if(strcmp("Host:", ptr)) {
+    if(!strcasecompare("Host:", ptr)) {
       aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
       if(!aptr->host)
         return CURLE_OUT_OF_MEMORY;
@@ -2340,9 +2280,7 @@
         return CURLE_OUT_OF_MEMORY;
       }
     }
-    /* Extract the URL to use in the request. Store in STRING_TEMP_URL for
-       clean-up reasons if the function returns before the free() further
-       down. */
+    /* Extract the URL to use in the request. */
     uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT);
     if(uc) {
       curl_url_cleanup(h);
@@ -2414,14 +2352,16 @@
     /* Convert the form structure into a mime structure, then keep
        the conversion */
     if(!data->state.formp) {
-      data->state.formp = calloc(sizeof(curl_mimepart), 1);
+      data->state.formp = calloc(1, sizeof(curl_mimepart));
       if(!data->state.formp)
         return CURLE_OUT_OF_MEMORY;
       Curl_mime_cleanpart(data->state.formp);
       result = Curl_getformdata(data, data->state.formp, data->set.httppost,
                                 data->state.fread_func);
-      if(result)
+      if(result) {
+        Curl_safefree(data->state.formp);
         return result;
+      }
       data->state.mimepost = data->state.formp;
     }
     break;
@@ -2494,6 +2434,29 @@
   return result;
 }
 
+static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn,
+                          struct dynbuf *r)
+{
+  data->state.expect100header = FALSE;
+  /* Avoid Expect: 100-continue if Upgrade: is used */
+  if(data->req.upgr101 == UPGR101_INIT) {
+    struct HTTP *http = data->req.p.http;
+    /* For really small puts we don't use Expect: headers at all, and for
+       the somewhat bigger ones we allow the app to disable it. Just make
+       sure that the expect100header is always set to the preferred value
+       here. */
+    char *ptr = Curl_checkheaders(data, STRCONST("Expect"));
+    if(ptr) {
+      data->state.expect100header =
+        Curl_compareheader(ptr, STRCONST("Expect:"),
+                           STRCONST("100-continue"));
+    }
+    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0)
+      return expect100(data, conn, r);
+  }
+  return CURLE_OK;
+}
+
 CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
                             struct dynbuf *r, Curl_HttpReq httpreq)
 {
@@ -2506,14 +2469,8 @@
 #endif
   CURLcode result = CURLE_OK;
   struct HTTP *http = data->req.p.http;
-  const char *ptr;
-
-  /* If 'authdone' is FALSE, we must not set the write socket index to the
-     Curl_transfer() call below, as we're not ready to actually upload any
-     data yet. */
 
   switch(httpreq) {
-
   case HTTPREQ_PUT: /* Let's PUT the data to the server! */
 
     if(conn->bits.authneg)
@@ -2531,20 +2488,9 @@
         return result;
     }
 
-    /* For really small puts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
     /* end of headers */
     result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2617,22 +2563,9 @@
     }
 #endif
 
-    /* For really small posts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
-    else
-      data->state.expect100header = FALSE;
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
     /* make the request end in a true CRLF */
     result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2692,22 +2625,9 @@
         return result;
     }
 
-    /* For really small posts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
-    else
-      data->state.expect100header = FALSE;
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
 #ifndef USE_HYPER
     /* With Hyper the body is always passed on separately */
@@ -3020,13 +2940,14 @@
         }
         /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
         do {
+          char scratch[4*1024];
           size_t readthisamountnow =
-            (data->state.resume_from - passed > data->set.buffer_size) ?
-            (size_t)data->set.buffer_size :
+            (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
+            sizeof(scratch) :
             curlx_sotouz(data->state.resume_from - passed);
 
           size_t actuallyread =
-            data->state.fread_func(data->state.buffer, 1, readthisamountnow,
+            data->state.fread_func(scratch, 1, readthisamountnow,
                                    data->state.in);
 
           passed += actuallyread;
@@ -3061,6 +2982,7 @@
 {
   struct SingleRequest *k = &data->req;
 
+  *done = FALSE;
   if(data->req.newurl) {
     if(conn->bits.close) {
       /* Abort after the headers if "follow Location" is set
@@ -3186,14 +3108,14 @@
       ) {
       result = Curl_http2_switch(data, conn, FIRSTSOCKET);
       if(result)
-        return result;
+        goto fail;
     }
     else
 #endif
       DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
     break;
   case CURL_HTTP_VERSION_1_1:
-    /* continue with HTTP/1.1 when explicitly requested */
+    /* continue with HTTP/1.x when explicitly requested */
     break;
   default:
     /* Check if user wants to use HTTP/2 with clear TCP */
@@ -3201,7 +3123,7 @@
       DEBUGF(infof(data, "HTTP/2 over clean TCP"));
       result = Curl_http2_switch(data, conn, FIRSTSOCKET);
       if(result)
-        return result;
+        goto fail;
     }
     break;
   }
@@ -3211,11 +3133,11 @@
 
   result = Curl_http_host(data, conn);
   if(result)
-    return result;
+    goto fail;
 
   result = Curl_http_useragent(data);
   if(result)
-    return result;
+    goto fail;
 
   Curl_http_method(data, conn, &request, &httpreq);
 
@@ -3231,7 +3153,7 @@
                                    (pq ? pq : data->state.up.path), FALSE);
     free(pq);
     if(result)
-      return result;
+      goto fail;
   }
 
   Curl_safefree(data->state.aptr.ref);
@@ -3256,23 +3178,23 @@
   /* we only consider transfer-encoding magic if libz support is built-in */
   result = Curl_transferencode(data);
   if(result)
-    return result;
+    goto fail;
 #endif
 
   result = Curl_http_body(data, conn, httpreq, &te);
   if(result)
-    return result;
+    goto fail;
 
   p_accept = Curl_checkheaders(data,
                                STRCONST("Accept"))?NULL:"Accept: */*\r\n";
 
   result = Curl_http_resume(data, conn, httpreq);
   if(result)
-    return result;
+    goto fail;
 
   result = Curl_http_range(data, httpreq);
   if(result)
-    return result;
+    goto fail;
 
   httpstring = get_http_string(data, conn);
 
@@ -3290,7 +3212,7 @@
     result = Curl_http_target(data, conn, &req);
   if(result) {
     Curl_dyn_free(&req);
-    return result;
+    goto fail;
   }
 
 #ifndef CURL_DISABLE_ALTSVC
@@ -3361,7 +3283,7 @@
 
   if(result) {
     Curl_dyn_free(&req);
-    return result;
+    goto fail;
   }
 
   if(!(conn->handler->flags&PROTOPT_SSL) &&
@@ -3397,7 +3319,7 @@
   }
   if(result) {
     Curl_dyn_free(&req);
-    return result;
+    goto fail;
   }
 
   if((http->postsize > -1) &&
@@ -3433,6 +3355,9 @@
        but is disabled here again to avoid that the chunked encoded version is
        actually used when sending the request body over h2 */
     data->req.upload_chunky = FALSE;
+fail:
+  if(CURLE_TOO_LARGE == result)
+    failf(data, "HTTP request too large");
   return result;
 }
 
@@ -3685,7 +3610,7 @@
           k->content_range = TRUE;
       }
     }
-    else
+    else if(k->httpcode < 300)
       data->state.resume_from = 0; /* get everything */
   }
 #if !defined(CURL_DISABLE_COOKIES)
@@ -3895,7 +3820,7 @@
      * fields.  */
     if(data->set.timecondition)
       data->info.timecond = TRUE;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case 204:
     /* (quote from RFC2616, section 10.2.5): The server has
      * fulfilled the request but does not need to return an
@@ -3994,37 +3919,33 @@
 /*
  * Read any HTTP header lines from the server and pass them to the client app.
  */
-CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
-                                     struct connectdata *conn,
-                                     ssize_t *nread,
-                                     bool *stop_reading)
+static CURLcode http_rw_headers(struct Curl_easy *data,
+                                const char *buf, size_t blen,
+                                size_t *pconsumed)
 {
-  CURLcode result;
+  struct connectdata *conn = data->conn;
+  CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
-  ssize_t onread = *nread;
-  char *ostr = k->str;
   char *headp;
-  char *str_start;
   char *end_ptr;
+  bool leftover_body = FALSE;
 
   /* header line within buffer loop */
+  *pconsumed = 0;
   do {
-    size_t rest_length;
-    size_t full_length;
+    size_t line_length;
     int writetype;
 
-    /* str_start is start of line within buf */
-    str_start = k->str;
-
     /* data is in network encoding so use 0x0a instead of '\n' */
-    end_ptr = memchr(str_start, 0x0a, *nread);
+    end_ptr = memchr(buf, 0x0a, blen);
 
     if(!end_ptr) {
       /* Not a complete header line within buffer, append the data to
          the end of the headerbuff. */
-      result = Curl_dyn_addn(&data->state.headerb, str_start, *nread);
+      result = Curl_dyn_addn(&data->state.headerb, buf, blen);
       if(result)
         return result;
+      *pconsumed += blen;
 
       if(!k->headerline) {
         /* check if this looks like a protocol header */
@@ -4036,31 +3957,28 @@
         if(st == STATUS_BAD) {
           /* this is not the beginning of a protocol first header line */
           k->header = FALSE;
-          k->badheader = HEADER_ALLBAD;
           streamclose(conn, "bad HTTP: No end-of-message indicator");
           if(!data->set.http09_allowed) {
             failf(data, "Received HTTP/0.9 when not allowed");
             return CURLE_UNSUPPORTED_PROTOCOL;
           }
-          break;
+          leftover_body = TRUE;
+          goto out;
         }
       }
-
-      break; /* read more and try again */
+      goto out; /* read more and try again */
     }
 
     /* decrease the size of the remaining (supposed) header line */
-    rest_length = (end_ptr - k->str) + 1;
-    *nread -= (ssize_t)rest_length;
-
-    k->str = end_ptr + 1; /* move past new line */
-
-    full_length = k->str - str_start;
-
-    result = Curl_dyn_addn(&data->state.headerb, str_start, full_length);
+    line_length = (end_ptr - buf) + 1;
+    result = Curl_dyn_addn(&data->state.headerb, buf, line_length);
     if(result)
       return result;
 
+    blen -= line_length;
+    buf += line_length;
+    *pconsumed += line_length;
+
     /****
      * We now have a FULL header line in 'headerb'.
      *****/
@@ -4078,17 +3996,8 @@
           return CURLE_UNSUPPORTED_PROTOCOL;
         }
         k->header = FALSE;
-        if(*nread)
-          /* since there's more, this is a partial bad header */
-          k->badheader = HEADER_PARTHEADER;
-        else {
-          /* this was all we read so it's all a bad header */
-          k->badheader = HEADER_ALLBAD;
-          *nread = onread;
-          k->str = ostr;
-          return CURLE_OK;
-        }
-        break;
+        leftover_body = TRUE;
+        goto out;
       }
     }
 
@@ -4097,6 +4006,7 @@
     headp = Curl_dyn_ptr(&data->state.headerb);
     if((0x0a == *headp) || (0x0d == *headp)) {
       size_t headerlen;
+      bool switch_to_h2 = FALSE;
       /* Zero-length header line means end of headers! */
 
       if('\r' == *headp)
@@ -4126,41 +4036,40 @@
           }
           break;
         case 101:
-          /* Switching Protocols */
-          if(k->upgr101 == UPGR101_H2) {
-            /* Switching to HTTP/2 */
-            DEBUGASSERT(conn->httpversion < 20);
-            infof(data, "Received 101, Switching to HTTP/2");
-            k->upgr101 = UPGR101_RECEIVED;
+          if(conn->httpversion == 11) {
+            /* Switching Protocols only allowed from HTTP/1.1 */
+            if(k->upgr101 == UPGR101_H2) {
+              /* Switching to HTTP/2 */
+              infof(data, "Received 101, Switching to HTTP/2");
+              k->upgr101 = UPGR101_RECEIVED;
 
-            /* we'll get more headers (HTTP/2 response) */
-            k->header = TRUE;
-            k->headerline = 0; /* restart the header line counter */
-
-            /* switch to http2 now. The bytes after response headers
-               are also processed here, otherwise they are lost. */
-            result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
-                                        k->str, *nread);
-            if(result)
-              return result;
-            *nread = 0;
-          }
+              /* we'll get more headers (HTTP/2 response) */
+              k->header = TRUE;
+              k->headerline = 0; /* restart the header line counter */
+              switch_to_h2 = TRUE;
+            }
 #ifdef USE_WEBSOCKETS
-          else if(k->upgr101 == UPGR101_WS) {
-            /* verify the response */
-            result = Curl_ws_accept(data, k->str, *nread);
-            if(result)
-              return result;
-            k->header = FALSE; /* no more header to parse! */
-            if(data->set.connect_only) {
-              k->keepon &= ~KEEP_RECV; /* read no more content */
-              *nread = 0;
+            else if(k->upgr101 == UPGR101_WS) {
+              /* verify the response */
+              result = Curl_ws_accept(data, buf, blen);
+              if(result)
+                return result;
+              k->header = FALSE; /* no more header to parse! */
+              *pconsumed += blen; /* ws accept handled the data */
+              blen = 0;
+              if(data->set.connect_only)
+                k->keepon &= ~KEEP_RECV; /* read no more content */
+            }
+#endif
+            else {
+              /* Not switching to another protocol */
+              k->header = FALSE; /* no more header to parse! */
             }
           }
-#endif
           else {
-            /* Not switching to another protocol */
-            k->header = FALSE; /* no more header to parse! */
+            /* invalid for other HTTP versions */
+            failf(data, "unexpected 101 response code");
+            return CURLE_WEIRD_SERVER_REPLY;
           }
           break;
         default:
@@ -4366,17 +4275,7 @@
          * out and return home.
          */
         if(data->req.no_body)
-          *stop_reading = TRUE;
-#ifndef CURL_DISABLE_RTSP
-        else if((conn->handler->protocol & CURLPROTO_RTSP) &&
-                (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
-                (k->size <= -1))
-          /* Respect section 4.4 of rfc2326: If the Content-Length header is
-             absent, a length 0 must be assumed.  It will prevent libcurl from
-             hanging on DESCRIBE request that got refused for whatever
-             reason */
-          *stop_reading = TRUE;
-#endif
+          k->download_done = TRUE;
 
         /* If max download size is *zero* (nothing) we already have
            nothing and can safely return ok now!  But for HTTP/2, we'd
@@ -4386,19 +4285,27 @@
         if(0 == k->maxdownload
            && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
            && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
-          *stop_reading = TRUE;
+          k->download_done = TRUE;
 
-        if(*stop_reading) {
-          /* we make sure that this socket isn't read more now */
-          k->keepon &= ~KEEP_RECV;
-        }
-
-        Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen);
-        break; /* exit header line loop */
+        Curl_debug(data, CURLINFO_HEADER_IN,
+                   Curl_dyn_ptr(&data->state.headerb),
+                   Curl_dyn_len(&data->state.headerb));
+        goto out; /* exit header line loop */
       }
 
       /* We continue reading headers, reset the line-based header */
       Curl_dyn_reset(&data->state.headerb);
+      if(switch_to_h2) {
+        /* Having handled the headers, we can do the HTTP/2 switch.
+         * Any remaining `buf` bytes are already HTTP/2 and passed to
+         * be processed. */
+        result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+        if(result)
+          return result;
+        *pconsumed += blen;
+        blen = 0;
+      }
+
       continue;
     }
 
@@ -4583,15 +4490,84 @@
 
     Curl_dyn_reset(&data->state.headerb);
   }
-  while(*k->str); /* header line within buffer */
+  while(blen);
 
   /* We might have reached the end of the header part here, but
      there might be a non-header part left in the end of the read
      buffer. */
-
+out:
+  if(!k->header && !leftover_body) {
+    Curl_dyn_free(&data->state.headerb);
+  }
   return CURLE_OK;
 }
 
+/*
+ * HTTP protocol `write_resp` implementation. Will parse headers
+ * when not done yet and otherwise return without consuming data.
+ */
+CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
+                                  const char *buf, size_t blen,
+                                  size_t *pconsumed,
+                                  bool *done)
+{
+  *done = FALSE;
+  if(!data->req.header) {
+    *pconsumed = 0;
+    return CURLE_OK;
+  }
+  else {
+    CURLcode result;
+
+    result = http_rw_headers(data, buf, blen, pconsumed);
+    if(!result && !data->req.header) {
+      /* we have successfully finished parsing the HEADERs */
+      result = Curl_http_firstwrite(data, data->conn, done);
+
+      if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+        /* leftover from parsing something that turned out not
+         * to be a header, only happens if we allow for
+         * HTTP/0.9 like responses */
+        result = Curl_client_write(data, CLIENTWRITE_BODY,
+                                   Curl_dyn_ptr(&data->state.headerb),
+                                   Curl_dyn_len(&data->state.headerb));
+      }
+      Curl_dyn_free(&data->state.headerb);
+    }
+    return result;
+  }
+}
+
+CURLcode Curl_http_write_resp(struct Curl_easy *data,
+                              const char *buf, size_t blen,
+                              bool is_eos,
+                              bool *done)
+{
+  CURLcode result;
+  size_t consumed;
+  int flags;
+
+  *done = FALSE;
+  result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
+  if(result || *done)
+    goto out;
+
+  DEBUGASSERT(consumed <= blen);
+  blen -= consumed;
+  buf += consumed;
+  /* either all was consumed in header parsing, or we have data left
+   * and are done with heders, e.g. it is BODY data */
+  DEBUGASSERT(!blen || !data->req.header);
+  if(!data->req.header && (blen || is_eos)) {
+    /* BODY data after header been parsed, write and consume */
+    flags = CLIENTWRITE_BODY;
+    if(is_eos)
+      flags |= CLIENTWRITE_EOS;
+    result = Curl_client_write(data, flags, (char *)buf, blen);
+  }
+out:
+  return result;
+}
 
 /* Decode HTTP status code string. */
 CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len)
@@ -4618,17 +4594,6 @@
   return result;
 }
 
-/* simple implementation of strndup(), which isn't portable */
-static char *my_strndup(const char *ptr, size_t len)
-{
-  char *copy = malloc(len + 1);
-  if(!copy)
-    return NULL;
-  memcpy(copy, ptr, len);
-  copy[len] = '\0';
-  return copy;
-}
-
 CURLcode Curl_http_req_make(struct httpreq **preq,
                             const char *method, size_t m_len,
                             const char *scheme, size_t s_len,
@@ -4639,7 +4604,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   DEBUGASSERT(method);
-  if(m_len + 1 >= sizeof(req->method))
+  if(m_len + 1 > sizeof(req->method))
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
   req = calloc(1, sizeof(*req));
@@ -4647,17 +4612,17 @@
     goto out;
   memcpy(req->method, method, m_len);
   if(scheme) {
-    req->scheme = my_strndup(scheme, s_len);
+    req->scheme = Curl_memdup0(scheme, s_len);
     if(!req->scheme)
       goto out;
   }
   if(authority) {
-    req->authority = my_strndup(authority, a_len);
+    req->authority = Curl_memdup0(authority, a_len);
     if(!req->authority)
       goto out;
   }
   if(path) {
-    req->path = my_strndup(path, p_len);
+    req->path = Curl_memdup0(path, p_len);
     if(!req->path)
       goto out;
   }
@@ -4795,7 +4760,7 @@
   CURLUcode uc;
 
   DEBUGASSERT(method);
-  if(m_len + 1 >= sizeof(req->method))
+  if(m_len + 1 > sizeof(req->method))
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
   req = calloc(1, sizeof(*req));
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index 9ee3c65..ad2697c 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -54,14 +54,6 @@
 extern const struct Curl_handler Curl_handler_https;
 #endif
 
-#ifdef USE_WEBSOCKETS
-extern const struct Curl_handler Curl_handler_ws;
-
-#ifdef USE_SSL
-extern const struct Curl_handler Curl_handler_wss;
-#endif
-#endif /* websockets */
-
 struct dynhds;
 
 CURLcode Curl_bump_headersize(struct Curl_easy *data,
@@ -147,9 +139,17 @@
                               bool *done);
 
 /* protocol-specific functions set up to be called by the main engine */
+CURLcode Curl_http_setup_conn(struct Curl_easy *data,
+                              struct connectdata *conn);
 CURLcode Curl_http(struct Curl_easy *data, bool *done);
 CURLcode Curl_http_done(struct Curl_easy *data, CURLcode, bool premature);
 CURLcode Curl_http_connect(struct Curl_easy *data, bool *done);
+int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
+                         curl_socket_t *socks);
+CURLcode Curl_http_write_resp(struct Curl_easy *data,
+                              const char *buf, size_t blen,
+                              bool is_eos,
+                              bool *done);
 
 /* These functions are in http.c */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
@@ -225,10 +225,10 @@
 
 CURLcode Curl_http_size(struct Curl_easy *data);
 
-CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
-                                     struct connectdata *conn,
-                                     ssize_t *nread,
-                                     bool *stop_reading);
+CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
+                                  const char *buf, size_t blen,
+                                  size_t *pconsumed,
+                                  bool *done);
 
 /**
  * Curl_http_output_auth() setups the authentication headers for the
@@ -263,7 +263,7 @@
  * All about a core HTTP request, excluding body and trailers
  */
 struct httpreq {
-  char method[12];
+  char method[24];
   char *scheme;
   char *authority;
   char *path;
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index c8b0594..c3157d1 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -107,14 +107,14 @@
   return 3;
 }
 
-static size_t populate_binsettings(uint8_t *binsettings,
-                                   struct Curl_easy *data)
+static ssize_t populate_binsettings(uint8_t *binsettings,
+                                    struct Curl_easy *data)
 {
   nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
   int ivlen;
 
   ivlen = populate_settings(iv, data);
-  /* this returns number of bytes it wrote */
+  /* this returns number of bytes it wrote or a negative number on error. */
   return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
                                        iv, ivlen);
 }
@@ -219,10 +219,10 @@
   if(!stream->send_closed &&
      (stream->upload_left || stream->upload_blocked_len))
     bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
+  if(data->state.select_bits != bits) {
+    CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
                 stream->id, bits);
-    data->state.dselect_bits = bits;
+    data->state.select_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
 }
@@ -283,13 +283,20 @@
     return;
 
   if(ctx->h2) {
+    bool flush_egress = FALSE;
+    /* returns error if stream not known, which is fine here */
+    (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
+
     if(!stream->closed && stream->id > 0) {
       /* RST_STREAM */
       CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
                   stream->id);
-      if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
-                                    stream->id, NGHTTP2_STREAM_CLOSED))
-        (void)nghttp2_session_send(ctx->h2);
+      stream->closed = TRUE;
+      stream->reset = TRUE;
+      stream->send_closed = TRUE;
+      nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+                                stream->id, NGHTTP2_STREAM_CLOSED);
+      flush_egress = TRUE;
     }
     if(!Curl_bufq_is_empty(&stream->recvbuf)) {
       /* Anything in the recvbuf is still being counted
@@ -299,19 +306,11 @@
       nghttp2_session_consume(ctx->h2, stream->id,
                               Curl_bufq_len(&stream->recvbuf));
       /* give WINDOW_UPATE a chance to be sent, but ignore any error */
-      (void)h2_progress_egress(cf, data);
+      flush_egress = TRUE;
     }
 
-    /* -1 means unassigned and 0 means cleared */
-    if(nghttp2_session_get_stream_user_data(ctx->h2, stream->id)) {
-      int rv = nghttp2_session_set_stream_user_data(ctx->h2,
-                                                    stream->id, 0);
-      if(rv) {
-        infof(data, "http/2: failed to clear user_data for stream %u",
-              stream->id);
-        DEBUGASSERT(0);
-      }
-    }
+    if(flush_egress)
+      nghttp2_session_send(ctx->h2);
   }
 
   Curl_bufq_free(&stream->sendbuf);
@@ -369,12 +368,15 @@
 {
   struct Curl_cfilter *cf = writer_ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
 
-  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
-  if(nwritten > 0)
-    CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
-  return nwritten;
+  if(data) {
+    ssize_t nwritten = Curl_conn_cf_send(cf->next, data,
+                                         (const char *)buf, buflen, err);
+    if(nwritten > 0)
+      CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
+    return nwritten;
+  }
+  return 0;
 }
 
 static ssize_t send_callback(nghttp2_session *h2,
@@ -452,9 +454,14 @@
      * in the H1 request and we upgrade from there. This stream
      * is opened implicitly as #1. */
     uint8_t binsettings[H2_BINSETTINGS_LEN];
-    size_t  binlen; /* length of the binsettings data */
+    ssize_t binlen; /* length of the binsettings data */
 
     binlen = populate_binsettings(binsettings, data);
+    if(binlen <= 0) {
+      failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
+      result = CURLE_FAILED_INIT;
+      goto out;
+    }
 
     result = http2_data_setup(cf, data, &stream);
     if(result)
@@ -1076,16 +1083,11 @@
       stream->reset = TRUE;
     }
     stream->send_closed = TRUE;
-    data->req.keepon &= ~KEEP_SEND_HOLD;
     drain_stream(cf, data, stream);
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if((data->req.keepon & KEEP_SEND_HOLD) &&
-       (data->req.keepon & KEEP_SEND)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
+    if(CURL_WANT_SEND(data)) {
       drain_stream(cf, data, stream);
-      CURL_TRC_CF(data, cf, "[%d] un-holding after win update",
-                  stream_id);
     }
     break;
   default:
@@ -1230,15 +1232,10 @@
          * window and *assume* that we treat this like a WINDOW_UPDATE. Some
          * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
          * To be safe, we UNHOLD a stream in order not to stall. */
-        if((data->req.keepon & KEEP_SEND_HOLD) &&
-           (data->req.keepon & KEEP_SEND)) {
+        if(CURL_WANT_SEND(data)) {
           struct stream_ctx *stream = H2_STREAM_CTX(data);
-          data->req.keepon &= ~KEEP_SEND_HOLD;
-          if(stream) {
+          if(stream)
             drain_stream(cf, data, stream);
-            CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
-                        stream_id);
-          }
         }
       }
       break;
@@ -1318,27 +1315,43 @@
                            uint32_t error_code, void *userp)
 {
   struct Curl_cfilter *cf = userp;
-  struct Curl_easy *data_s;
+  struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
   struct stream_ctx *stream;
   int rv;
   (void)session;
 
+  DEBUGASSERT(call_data);
   /* get the stream from the hash based on Stream ID, stream ID zero is for
      connection-oriented stuff */
   data_s = stream_id?
              nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
   if(!data_s) {
+    CURL_TRC_CF(call_data, cf,
+                "[%d] on_stream_close, no easy set on stream", stream_id);
     return 0;
   }
-  stream = H2_STREAM_CTX(data_s);
-  if(!stream)
+  if(!GOOD_EASY_HANDLE(data_s)) {
+    /* nghttp2 still has an easy registered for the stream which has
+     * been freed be libcurl. This points to a code path that does not
+     * trigger DONE or DETACH events as it must. */
+    CURL_TRC_CF(call_data, cf,
+                "[%d] on_stream_close, not a GOOD easy on stream", stream_id);
+    (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
+  }
+  stream = H2_STREAM_CTX(data_s);
+  if(!stream) {
+    CURL_TRC_CF(data_s, cf,
+                "[%d] on_stream_close, GOOD easy but no stream", stream_id);
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
+  }
 
   stream->closed = TRUE;
   stream->error = error_code;
-  if(stream->error)
+  if(stream->error) {
     stream->reset = TRUE;
-  data_s->req.keepon &= ~KEEP_SEND_HOLD;
+    stream->send_closed = TRUE;
+  }
 
   if(stream->error)
     CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
@@ -1602,10 +1615,10 @@
                           size_t len,
                           void *userp)
 {
+  struct Curl_cfilter *cf = userp;
+  struct Curl_easy *data = CF_DATA_CURRENT(cf);
   (void)session;
-  (void)msg;
-  (void)len;
-  (void)userp;
+  failf(data, "%.*s", (int)len, msg);
   return 0;
 }
 #endif
@@ -1621,7 +1634,7 @@
   size_t blen;
   struct SingleRequest *k = &data->req;
   uint8_t binsettings[H2_BINSETTINGS_LEN];
-  size_t  binlen; /* length of the binsettings data */
+  ssize_t binlen; /* length of the binsettings data */
 
   binlen = populate_binsettings(binsettings, data);
   if(binlen <= 0) {
@@ -2052,23 +2065,13 @@
   /* no longer needed */
   Curl_h1_req_parse_free(&stream->h1);
 
-  nheader = Curl_dynhds_count(&h2_headers);
-  nva = malloc(sizeof(nghttp2_nv) * nheader);
+  nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
     *err = CURLE_OUT_OF_MEMORY;
     nwritten = -1;
     goto out;
   }
 
-  for(i = 0; i < nheader; ++i) {
-    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
-    nva[i].name = (unsigned char *)e->name;
-    nva[i].namelen = e->namelen;
-    nva[i].value = (unsigned char *)e->value;
-    nva[i].valuelen = e->valuelen;
-    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
-  }
-
   h2_pri_spec(data, &pri_spec);
   if(!nghttp2_session_check_request_allowed(ctx->h2))
     CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
@@ -2272,14 +2275,6 @@
      * frame buffer or our network out buffer. */
     size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
                                                                 stream->id);
-    if(rwin == 0) {
-      /* H2 flow window exhaustion. We need to HOLD upload until we get
-       * a WINDOW_UPDATE from the server. */
-      data->req.keepon |= KEEP_SEND_HOLD;
-      CURL_TRC_CF(data, cf, "[%d] holding send as remote flow "
-                  "window is exhausted", stream->id);
-    }
-
     /* Whatever the cause, we need to return CURL_EAGAIN for this call.
      * We have unwritten state that needs us being invoked again and EAGAIN
      * is the only way to ensure that. */
@@ -2331,38 +2326,38 @@
   return nwritten;
 }
 
-static int cf_h2_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *sock)
+static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct SingleRequest *k = &data->req;
-  struct stream_ctx *stream = H2_STREAM_CTX(data);
-  int bitmap = GETSOCK_BLANK;
-  struct cf_call_data save;
+  curl_socket_t sock;
+  bool want_recv, want_send;
 
-  CF_DATA_SAVE(save, cf, data);
-  sock[0] = Curl_conn_cf_get_socket(cf, data);
+  if(!ctx->h2)
+    return;
 
-  if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD)))
-    /* Unless paused - in an HTTP/2 connection we can basically always get a
-       frame so we should always be ready for one */
-    bitmap |= GETSOCK_READSOCK(0);
+  sock = Curl_conn_cf_get_socket(cf, data);
+  Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
+  if(want_recv || want_send) {
+    struct stream_ctx *stream = H2_STREAM_CTX(data);
+    struct cf_call_data save;
+    bool c_exhaust, s_exhaust;
 
-  /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
-     there's a window to send data in */
-  if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) ||
-      nghttp2_session_want_write(ctx->h2)) &&
-     (nghttp2_session_get_remote_window_size(ctx->h2) &&
-      nghttp2_session_get_stream_remote_window_size(ctx->h2,
-                                                    stream->id)))
-    bitmap |= GETSOCK_WRITESOCK(0);
+    CF_DATA_SAVE(save, cf, data);
+    c_exhaust = want_send && !nghttp2_session_get_remote_window_size(ctx->h2);
+    s_exhaust = want_send && stream && stream->id >= 0 &&
+                !nghttp2_session_get_stream_remote_window_size(ctx->h2,
+                                                               stream->id);
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                (!c_exhaust && nghttp2_session_want_write(ctx->h2));
 
-  CF_DATA_RESTORE(cf, save);
-  return bitmap;
+    Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CF_DATA_RESTORE(cf, save);
+  }
 }
 
-
 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               bool blocking, bool *done)
@@ -2511,14 +2506,15 @@
   case CF_CTRL_DATA_PAUSE:
     result = http2_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE_SEND: {
+  case CF_CTRL_DATA_DONE_SEND:
     result = http2_data_done_send(cf, data);
     break;
-  }
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
+    http2_data_done(cf, data, TRUE);
+    break;
+  case CF_CTRL_DATA_DONE:
     http2_data_done(cf, data, arg1 != 0);
     break;
-  }
   default:
     break;
   }
@@ -2606,7 +2602,7 @@
   cf_h2_connect,
   cf_h2_close,
   Curl_cf_def_get_host,
-  cf_h2_get_select_socks,
+  cf_h2_adjust_pollset,
   cf_h2_data_pending,
   cf_h2_send,
   cf_h2_recv,
@@ -2626,7 +2622,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   DEBUGASSERT(data->conn);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 
@@ -2652,7 +2648,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 901c22f..c938291 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -247,7 +247,7 @@
   }
   else {
     char *value;
-
+    char *endp;
     value = strchr(*date_header, ':');
     if(!value) {
       *date_header = NULL;
@@ -256,8 +256,17 @@
     ++value;
     while(ISBLANK(*value))
       ++value;
-    strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
-    timestamp[TIMESTAMP_SIZE - 1] = 0;
+    endp = value;
+    while(*endp && ISALNUM(*endp))
+      ++endp;
+    /* 16 bytes => "19700101T000000Z" */
+    if((endp - value) == TIMESTAMP_SIZE - 1) {
+      memcpy(timestamp, value, TIMESTAMP_SIZE - 1);
+      timestamp[TIMESTAMP_SIZE - 1] = 0;
+    }
+    else
+      /* bad timestamp length */
+      timestamp[0] = 0;
     *date_header = NULL;
   }
 
@@ -456,6 +465,7 @@
   for(i = 0; !result && (i < entry); i++, ap++) {
     size_t len;
     const char *q = ap->p;
+    bool found_equals = false;
     if(!ap->len)
       continue;
     for(len = ap->len; len && !result; q++, len--) {
@@ -467,9 +477,13 @@
         case '.':
         case '_':
         case '~':
+          /* allowed as-is */
+          result = Curl_dyn_addn(dq, q, 1);
+          break;
         case '=':
           /* allowed as-is */
           result = Curl_dyn_addn(dq, q, 1);
+          found_equals = true;
           break;
         case '%':
           /* uppercase the following if hexadecimal */
@@ -497,7 +511,11 @@
         }
       }
     }
-    if(i < entry - 1) {
+    if(!result && !found_equals) {
+      /* queries without value still need an equals */
+      result = Curl_dyn_addn(dq, "=", 1);
+    }
+    if(!result && i < entry - 1) {
       /* insert ampersands between query pairs */
       result = Curl_dyn_addn(dq, "&", 1);
     }
@@ -596,7 +614,7 @@
       result = CURLE_URL_MALFORMAT;
       goto fail;
     }
-    strncpy(service, hostname, len);
+    memcpy(service, hostname, len);
     service[len] = '\0';
 
     infof(data, "aws_sigv4: picked service %s from host", service);
@@ -615,7 +633,7 @@
         result = CURLE_URL_MALFORMAT;
         goto fail;
       }
-      strncpy(region, reg, len);
+      memcpy(region, reg, len);
       region[len] = '\0';
       infof(data, "aws_sigv4: picked region %s from host", region);
     }
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c
index 2a401d1..039c179 100644
--- a/Utilities/cmcurl/lib/http_chunks.c
+++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -75,86 +75,110 @@
 
  */
 
-#define isxdigit_ascii(x) Curl_isxdigit(x)
-
-void Curl_httpchunk_init(struct Curl_easy *data)
+void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
+                         bool ignore_body)
 {
-  struct connectdata *conn = data->conn;
-  struct Curl_chunker *chunk = &conn->chunk;
-  chunk->hexindex = 0;      /* start at 0 */
-  chunk->state = CHUNK_HEX; /* we get hex first! */
-  Curl_dyn_init(&conn->trailer, DYN_H1_TRAILER);
+  (void)data;
+  ch->hexindex = 0;      /* start at 0 */
+  ch->state = CHUNK_HEX; /* we get hex first! */
+  ch->last_code = CHUNKE_OK;
+  Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
+  ch->ignore_body = ignore_body;
 }
 
-/*
- * chunk_read() returns a OK for normal operations, or a positive return code
- * for errors. STOP means this sequence of chunks is complete.  The 'wrote'
- * argument is set to tell the caller how many bytes we actually passed to the
- * client (for byte-counting and whatever).
- *
- * The states and the state-machine is further explained in the header file.
- *
- * This function always uses ASCII hex values to accommodate non-ASCII hosts.
- * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
- */
-CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
-                              char *datap,
-                              ssize_t datalen,
-                              ssize_t *wrote,
-                              CURLcode *extrap)
+void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
+                          bool ignore_body)
+{
+  (void)data;
+  ch->hexindex = 0;      /* start at 0 */
+  ch->state = CHUNK_HEX; /* we get hex first! */
+  ch->last_code = CHUNKE_OK;
+  Curl_dyn_reset(&ch->trailer);
+  ch->ignore_body = ignore_body;
+}
+
+void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
+{
+  (void)data;
+  Curl_dyn_free(&ch->trailer);
+}
+
+bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
+{
+  (void)data;
+  return ch->state == CHUNK_DONE;
+}
+
+static CURLcode httpchunk_readwrite(struct Curl_easy *data,
+                                    struct Curl_chunker *ch,
+                                    struct Curl_cwriter *cw_next,
+                                    const char *buf, size_t blen,
+                                    size_t *pconsumed)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct Curl_chunker *ch = &conn->chunk;
-  struct SingleRequest *k = &data->req;
   size_t piece;
-  curl_off_t length = (curl_off_t)datalen;
 
-  *wrote = 0; /* nothing's written yet */
+  *pconsumed = 0; /* nothing's written yet */
+  /* first check terminal states that will not progress anywhere */
+  if(ch->state == CHUNK_DONE)
+    return CURLE_OK;
+  if(ch->state == CHUNK_FAILED)
+    return CURLE_RECV_ERROR;
 
   /* the original data is written to the client, but we go on with the
      chunk read process, to properly calculate the content length */
-  if(data->set.http_te_skip && !k->ignorebody) {
-    result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen);
+  if(data->set.http_te_skip && !ch->ignore_body) {
+    if(cw_next)
+      result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
+    else
+      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
     if(result) {
-      *extrap = result;
-      return CHUNKE_PASSTHRU_ERROR;
+      ch->state = CHUNK_FAILED;
+      ch->last_code = CHUNKE_PASSTHRU_ERROR;
+      return result;
     }
   }
 
-  while(length) {
+  while(blen) {
     switch(ch->state) {
     case CHUNK_HEX:
-      if(ISXDIGIT(*datap)) {
-        if(ch->hexindex < CHUNK_MAXNUM_LEN) {
-          ch->hexbuffer[ch->hexindex] = *datap;
-          datap++;
-          length--;
-          ch->hexindex++;
+      if(ISXDIGIT(*buf)) {
+        if(ch->hexindex >= CHUNK_MAXNUM_LEN) {
+          failf(data, "chunk hex-length longer than %d", CHUNK_MAXNUM_LEN);
+          ch->state = CHUNK_FAILED;
+          ch->last_code = CHUNKE_TOO_LONG_HEX; /* longer than we support */
+          return CURLE_RECV_ERROR;
         }
-        else {
-          return CHUNKE_TOO_LONG_HEX; /* longer hex than we support */
-        }
+        ch->hexbuffer[ch->hexindex++] = *buf;
+        buf++;
+        blen--;
       }
       else {
         char *endptr;
-        if(0 == ch->hexindex)
+        if(0 == ch->hexindex) {
           /* This is illegal data, we received junk where we expected
              a hexadecimal digit. */
-          return CHUNKE_ILLEGAL_HEX;
+          failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf);
+          ch->state = CHUNK_FAILED;
+          ch->last_code = CHUNKE_ILLEGAL_HEX;
+          return CURLE_RECV_ERROR;
+        }
 
-        /* length and datap are unmodified */
+        /* blen and buf are unmodified */
         ch->hexbuffer[ch->hexindex] = 0;
-
-        if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
-          return CHUNKE_ILLEGAL_HEX;
+        if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize)) {
+          failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
+          ch->state = CHUNK_FAILED;
+          ch->last_code = CHUNKE_ILLEGAL_HEX;
+          return CURLE_RECV_ERROR;
+        }
         ch->state = CHUNK_LF; /* now wait for the CRLF */
       }
       break;
 
     case CHUNK_LF:
       /* waiting for the LF after a chunk size */
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         /* we're now expecting data to come, unless size was zero! */
         if(0 == ch->datasize) {
           ch->state = CHUNK_TRAILER; /* now check for trailers */
@@ -163,30 +187,37 @@
           ch->state = CHUNK_DATA;
       }
 
-      datap++;
-      length--;
+      buf++;
+      blen--;
       break;
 
     case CHUNK_DATA:
-      /* We expect 'datasize' of data. We have 'length' right now, it can be
+      /* We expect 'datasize' of data. We have 'blen' right now, it can be
          more or less than 'datasize'. Get the smallest piece.
       */
-      piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
+      piece = blen;
+      if(ch->datasize < (curl_off_t)blen)
+        piece = curlx_sotouz(ch->datasize);
 
       /* Write the data portion available */
-      if(!data->set.http_te_skip && !k->ignorebody) {
-        result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
-
+      if(!data->set.http_te_skip && !ch->ignore_body) {
+        if(cw_next)
+          result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
+                                      buf, piece);
+        else
+          result = Curl_client_write(data, CLIENTWRITE_BODY,
+                                    (char *)buf, piece);
         if(result) {
-          *extrap = result;
-          return CHUNKE_PASSTHRU_ERROR;
+          ch->state = CHUNK_FAILED;
+          ch->last_code = CHUNKE_PASSTHRU_ERROR;
+          return result;
         }
       }
 
-      *wrote += piece;
+      *pconsumed += piece;
       ch->datasize -= piece; /* decrease amount left to expect */
-      datap += piece;    /* move read pointer forward */
-      length -= piece;   /* decrease space left in this round */
+      buf += piece;    /* move read pointer forward */
+      blen -= piece;   /* decrease space left in this round */
 
       if(0 == ch->datasize)
         /* end of data this round, we now expect a trailing CRLF */
@@ -194,42 +225,55 @@
       break;
 
     case CHUNK_POSTLF:
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         /* The last one before we go back to hex state and start all over. */
-        Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */
+        Curl_httpchunk_reset(data, ch, ch->ignore_body);
       }
-      else if(*datap != 0x0d)
-        return CHUNKE_BAD_CHUNK;
-      datap++;
-      length--;
+      else if(*buf != 0x0d) {
+        ch->state = CHUNK_FAILED;
+        ch->last_code = CHUNKE_BAD_CHUNK;
+        return CURLE_RECV_ERROR;
+      }
+      buf++;
+      blen--;
       break;
 
     case CHUNK_TRAILER:
-      if((*datap == 0x0d) || (*datap == 0x0a)) {
-        char *tr = Curl_dyn_ptr(&conn->trailer);
+      if((*buf == 0x0d) || (*buf == 0x0a)) {
+        char *tr = Curl_dyn_ptr(&ch->trailer);
         /* this is the end of a trailer, but if the trailer was zero bytes
            there was no trailer and we move on */
 
         if(tr) {
           size_t trlen;
-          result = Curl_dyn_addn(&conn->trailer, (char *)STRCONST("\x0d\x0a"));
-          if(result)
-            return CHUNKE_OUT_OF_MEMORY;
-
-          tr = Curl_dyn_ptr(&conn->trailer);
-          trlen = Curl_dyn_len(&conn->trailer);
+          result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
+          if(result) {
+            ch->state = CHUNK_FAILED;
+            ch->last_code = CHUNKE_OUT_OF_MEMORY;
+            return result;
+          }
+          tr = Curl_dyn_ptr(&ch->trailer);
+          trlen = Curl_dyn_len(&ch->trailer);
           if(!data->set.http_te_skip) {
-            result = Curl_client_write(data,
-                                       CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
-                                       tr, trlen);
+            if(cw_next)
+              result = Curl_cwriter_write(data, cw_next,
+                                          CLIENTWRITE_HEADER|
+                                          CLIENTWRITE_TRAILER,
+                                          tr, trlen);
+            else
+              result = Curl_client_write(data,
+                                         CLIENTWRITE_HEADER|
+                                         CLIENTWRITE_TRAILER,
+                                         tr, trlen);
             if(result) {
-              *extrap = result;
-              return CHUNKE_PASSTHRU_ERROR;
+              ch->state = CHUNK_FAILED;
+              ch->last_code = CHUNKE_PASSTHRU_ERROR;
+              return result;
             }
           }
-          Curl_dyn_reset(&conn->trailer);
+          Curl_dyn_reset(&ch->trailer);
           ch->state = CHUNK_TRAILER_CR;
-          if(*datap == 0x0a)
+          if(*buf == 0x0a)
             /* already on the LF */
             break;
         }
@@ -240,59 +284,73 @@
         }
       }
       else {
-        result = Curl_dyn_addn(&conn->trailer, datap, 1);
-        if(result)
-          return CHUNKE_OUT_OF_MEMORY;
+        result = Curl_dyn_addn(&ch->trailer, buf, 1);
+        if(result) {
+          ch->state = CHUNK_FAILED;
+          ch->last_code = CHUNKE_OUT_OF_MEMORY;
+          return result;
+        }
       }
-      datap++;
-      length--;
+      buf++;
+      blen--;
       break;
 
     case CHUNK_TRAILER_CR:
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         ch->state = CHUNK_TRAILER_POSTCR;
-        datap++;
-        length--;
+        buf++;
+        blen--;
       }
-      else
-        return CHUNKE_BAD_CHUNK;
+      else {
+        ch->state = CHUNK_FAILED;
+        ch->last_code = CHUNKE_BAD_CHUNK;
+        return CURLE_RECV_ERROR;
+      }
       break;
 
     case CHUNK_TRAILER_POSTCR:
       /* We enter this state when a CR should arrive so we expect to
          have to first pass a CR before we wait for LF */
-      if((*datap != 0x0d) && (*datap != 0x0a)) {
+      if((*buf != 0x0d) && (*buf != 0x0a)) {
         /* not a CR then it must be another header in the trailer */
         ch->state = CHUNK_TRAILER;
         break;
       }
-      if(*datap == 0x0d) {
+      if(*buf == 0x0d) {
         /* skip if CR */
-        datap++;
-        length--;
+        buf++;
+        blen--;
       }
       /* now wait for the final LF */
       ch->state = CHUNK_STOP;
       break;
 
     case CHUNK_STOP:
-      if(*datap == 0x0a) {
-        length--;
-
+      if(*buf == 0x0a) {
+        blen--;
         /* Record the length of any data left in the end of the buffer
            even if there's no more chunks to read */
-        ch->datasize = curlx_sotouz(length);
-
-        return CHUNKE_STOP; /* return stop */
+        ch->datasize = blen;
+        ch->state = CHUNK_DONE;
+        return CURLE_OK;
       }
-      else
-        return CHUNKE_BAD_CHUNK;
+      else {
+        ch->state = CHUNK_FAILED;
+        ch->last_code = CHUNKE_BAD_CHUNK;
+        return CURLE_RECV_ERROR;
+      }
+    case CHUNK_DONE:
+      return CURLE_OK;
+
+    case CHUNK_FAILED:
+      return CURLE_RECV_ERROR;
     }
+
   }
-  return CHUNKE_OK;
+  return CURLE_OK;
 }
 
-const char *Curl_chunked_strerror(CHUNKcode code)
+static const char *Curl_chunked_strerror(CHUNKcode code)
 {
   switch(code) {
   default:
@@ -304,8 +362,7 @@
   case CHUNKE_BAD_CHUNK:
     return "Malformed encoding found";
   case CHUNKE_PASSTHRU_ERROR:
-    DEBUGASSERT(0); /* never used */
-    return "";
+    return "Error writing data to client";
   case CHUNKE_BAD_ENCODING:
     return "Bad content-encoding found";
   case CHUNKE_OUT_OF_MEMORY:
@@ -313,4 +370,86 @@
   }
 }
 
+CURLcode Curl_httpchunk_read(struct Curl_easy *data,
+                             struct Curl_chunker *ch,
+                             char *buf, size_t blen,
+                             size_t *pconsumed)
+{
+  return httpchunk_readwrite(data, ch, NULL, buf, blen, pconsumed);
+}
+
+struct chunked_writer {
+  struct Curl_cwriter super;
+  struct Curl_chunker ch;
+};
+
+static CURLcode cw_chunked_init(struct Curl_easy *data,
+                                struct Curl_cwriter *writer)
+{
+  struct chunked_writer *ctx = (struct chunked_writer *)writer;
+
+  data->req.chunk = TRUE;      /* chunks coming our way. */
+  Curl_httpchunk_init(data, &ctx->ch, FALSE);
+  return CURLE_OK;
+}
+
+static void cw_chunked_close(struct Curl_easy *data,
+                             struct Curl_cwriter *writer)
+{
+  struct chunked_writer *ctx = (struct chunked_writer *)writer;
+  Curl_httpchunk_free(data, &ctx->ch);
+}
+
+static CURLcode cw_chunked_write(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer, int type,
+                                 const char *buf, size_t blen)
+{
+  struct chunked_writer *ctx = (struct chunked_writer *)writer;
+  CURLcode result;
+  size_t consumed;
+
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, blen);
+
+  consumed = 0;
+  result = httpchunk_readwrite(data, &ctx->ch, writer->next, buf, blen,
+                               &consumed);
+
+  if(result) {
+    if(CHUNKE_PASSTHRU_ERROR == ctx->ch.last_code) {
+      failf(data, "Failed reading the chunked-encoded stream");
+    }
+    else {
+      failf(data, "%s in chunked-encoding",
+            Curl_chunked_strerror(ctx->ch.last_code));
+    }
+    return result;
+  }
+
+  blen -= consumed;
+  if(CHUNK_DONE == ctx->ch.state) {
+    /* chunks read successfully, download is complete */
+    data->req.download_done = TRUE;
+    if(blen) {
+      infof(data, "Leftovers after chunking: %zu bytes", blen);
+    }
+  }
+  else if((type & CLIENTWRITE_EOS) && !data->req.no_body) {
+    failf(data, "transfer closed with outstanding read data remaining");
+    return CURLE_PARTIAL_FILE;
+  }
+
+  return CURLE_OK;
+}
+
+/* HTTP chunked Transfer-Encoding decoder */
+const struct Curl_cwtype Curl_httpchunk_unencoder = {
+  "chunked",
+  NULL,
+  cw_chunked_init,
+  cw_chunked_write,
+  cw_chunked_close,
+  sizeof(struct chunked_writer)
+};
+
 #endif /* CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h
index ed50713..07f2984 100644
--- a/Utilities/cmcurl/lib/http_chunks.h
+++ b/Utilities/cmcurl/lib/http_chunks.h
@@ -24,6 +24,10 @@
  *
  ***************************************************************************/
 
+#ifndef CURL_DISABLE_HTTP
+
+#include "dynbuf.h"
+
 struct connectdata;
 
 /*
@@ -67,34 +71,68 @@
      signalled If this is an empty trailer CHUNKE_STOP will be signalled.
      Otherwise the trailer will be broadcasted via Curl_client_write() and the
      next state will be CHUNK_TRAILER */
-  CHUNK_TRAILER_POSTCR
+  CHUNK_TRAILER_POSTCR,
+
+  /* Successfully de-chunked everything */
+  CHUNK_DONE,
+
+  /* Failed on seeing a bad or not correctly terminated chunk */
+  CHUNK_FAILED
 } ChunkyState;
 
 typedef enum {
-  CHUNKE_STOP = -1,
   CHUNKE_OK = 0,
   CHUNKE_TOO_LONG_HEX = 1,
   CHUNKE_ILLEGAL_HEX,
   CHUNKE_BAD_CHUNK,
   CHUNKE_BAD_ENCODING,
   CHUNKE_OUT_OF_MEMORY,
-  CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */
-  CHUNKE_LAST
+  CHUNKE_PASSTHRU_ERROR /* Curl_httpchunk_read() returns a CURLcode to use */
 } CHUNKcode;
 
-const char *Curl_chunked_strerror(CHUNKcode code);
-
 struct Curl_chunker {
   curl_off_t datasize;
   ChunkyState state;
+  CHUNKcode last_code;
+  struct dynbuf trailer; /* for chunked-encoded trailer */
   unsigned char hexindex;
-  char hexbuffer[ CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */
+  char hexbuffer[CHUNK_MAXNUM_LEN + 1]; /* +1 for null-terminator */
+  BIT(ignore_body); /* never write response body data */
 };
 
 /* The following functions are defined in http_chunks.c */
-void Curl_httpchunk_init(struct Curl_easy *data);
-CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap,
-                              ssize_t length, ssize_t *wrote,
-                              CURLcode *passthru);
+void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
+                         bool ignore_body);
+void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch);
+void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
+                          bool ignore_body);
+
+/*
+ * Read BODY bytes in HTTP/1.1 chunked encoding from `buf` and return
+ * the amount of bytes consumed. The actual response bytes and trailer
+ * headers are written out to the client.
+ * On success, this will consume all bytes up to the end of the response,
+ * e.g. the last chunk, has been processed.
+ * @param data   the transfer involved
+ * @param ch     the chunker instance keeping state across calls
+ * @param buf    the response data
+ * @param blen   amount of bytes in `buf`
+ * @param pconsumed  on successful return, the number of bytes in `buf`
+ *                   consumed
+ *
+ * This function always uses ASCII hex values to accommodate non-ASCII hosts.
+ * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
+ */
+CURLcode Curl_httpchunk_read(struct Curl_easy *data, struct Curl_chunker *ch,
+                             char *buf, size_t blen, size_t *pconsumed);
+
+/**
+ * @return TRUE iff chunked decoded has finished successfully.
+ */
+bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch);
+
+extern const struct Curl_cwtype Curl_httpchunk_unencoder;
+
+#endif /* !CURL_DISABLE_HTTP */
 
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index a1d6da9..113c43a 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -131,8 +131,8 @@
       goto out;
   }
 
-  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent"))
-     && data->set.str[STRING_USERAGENT]) {
+  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) &&
+     data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
     result = Curl_dynhds_cadd(&req->headers, "User-Agent",
                               data->set.str[STRING_USERAGENT]);
     if(result)
@@ -299,7 +299,7 @@
   http_proxy_cf_connect,
   http_proxy_cf_close,
   Curl_cf_http_proxy_get_host,
-  Curl_cf_def_get_select_socks,
+  Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c
index a024691..81a177f 100644
--- a/Utilities/cmcurl/lib/idn.c
+++ b/Utilities/cmcurl/lib/idn.c
@@ -36,7 +36,7 @@
 #ifdef USE_LIBIDN2
 #include <idn2.h>
 
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 #define IDN2_LOOKUP(name, host, flags)                                  \
   idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
 #else
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index de64c2a..f9211d9 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -97,7 +97,8 @@
 static CURLcode imap_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
 static char *imap_atom(const char *str, bool escape_only);
-static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...);
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
+  CURL_PRINTF(2, 3);
 static CURLcode imap_parse_url_options(struct connectdata *conn);
 static CURLcode imap_parse_url_path(struct Curl_easy *data);
 static CURLcode imap_parse_custom_request(struct Curl_easy *data);
@@ -129,7 +130,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAP,                        /* defport */
@@ -158,7 +159,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAPS,                       /* defport */
@@ -354,8 +355,8 @@
  */
 static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = data->state.buffer;
-  size_t len = strlen(message);
+  char *message = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+  size_t len = data->conn->proto.imapc.pp.nfinal;
 
   if(len > 2) {
     /* Find the start of the message */
@@ -895,7 +896,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct imap_conn *imapc = &conn->proto.imapc;
-  const char *line = data->state.buffer;
+  const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf);
 
   (void)instate; /* no use for this yet */
 
@@ -981,7 +982,7 @@
   (void)instate; /* no use for this yet */
 
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.imapc.pp.cache_size)
+  if(data->conn->proto.imapc.pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(imapcode != IMAP_RESP_OK) {
@@ -1057,17 +1058,13 @@
                                            imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  char *line = data->state.buffer;
-  size_t len = strlen(line);
+  char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+  size_t len = data->conn->proto.imapc.pp.nfinal;
 
   (void)instate; /* No use for this yet */
 
-  if(imapcode == '*') {
-    /* Temporarily add the LF character back and send as body to the client */
-    line[len] = '\n';
-    result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
-    line[len] = '\0';
-  }
+  if(imapcode == '*')
+    result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
   else if(imapcode != IMAP_RESP_OK)
     result = CURLE_QUOTE_ERROR;
   else
@@ -1085,7 +1082,7 @@
   struct connectdata *conn = data->conn;
   struct IMAP *imap = data->req.p.imap;
   struct imap_conn *imapc = &conn->proto.imapc;
-  const char *line = data->state.buffer;
+  const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
 
   (void)instate; /* no use for this yet */
 
@@ -1144,7 +1141,8 @@
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
   struct pingpong *pp = &imapc->pp;
-  const char *ptr = data->state.buffer;
+  const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+  size_t len = data->conn->proto.imapc.pp.nfinal;
   bool parsed = FALSE;
   curl_off_t size = 0;
 
@@ -1158,16 +1156,12 @@
 
   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
      the continuation data contained within the curly brackets */
-  while(*ptr && (*ptr != '{'))
-    ptr++;
-
-  if(*ptr == '{') {
+  ptr = memchr(ptr, '{', len);
+  if(ptr) {
     char *endptr;
-    if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
-      if(endptr - ptr > 1 && endptr[0] == '}' &&
-         endptr[1] == '\r' && endptr[2] == '\0')
-        parsed = TRUE;
-    }
+    if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) &&
+       (endptr - ptr > 1 && *endptr == '}'))
+      parsed = TRUE;
   }
 
   if(parsed) {
@@ -1175,11 +1169,15 @@
           size);
     Curl_pgrsSetDownloadSize(data, size);
 
-    if(pp->cache) {
-      /* At this point there is a bunch of data in the header "cache" that is
-         actually body content, send it as body and then skip it. Do note
-         that there may even be additional "headers" after the body. */
-      size_t chunk = pp->cache_size;
+    if(pp->overflow) {
+      /* At this point there is a data in the receive buffer that is body
+         content, send it as body and then skip it. Do note that there may
+         even be additional "headers" after the body. */
+      size_t chunk = pp->overflow;
+
+      /* keep only the overflow */
+      Curl_dyn_tail(&pp->recvbuf, chunk);
+      pp->nfinal = 0; /* done */
 
       if(chunk > (size_t)size)
         /* The conversion from curl_off_t to size_t is always fine here */
@@ -1190,27 +1188,24 @@
         imap_state(data, IMAP_STOP);
         return CURLE_OK;
       }
-      result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
+      result = Curl_client_write(data, CLIENTWRITE_BODY,
+                                 Curl_dyn_ptr(&pp->recvbuf), chunk);
       if(result)
         return result;
 
-      data->req.bytecount += chunk;
-
       infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
             " bytes are left for transfer", chunk, size - chunk);
 
-      /* Have we used the entire cache or just part of it?*/
-      if(pp->cache_size > chunk) {
-        /* Only part of it so shrink the cache to fit the trailing data */
-        memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
-        pp->cache_size -= chunk;
+      /* Have we used the entire overflow or just part of it?*/
+      if(pp->overflow > chunk) {
+        /* remember the remaining trailing overflow data */
+        pp->overflow -= chunk;
+        Curl_dyn_tail(&pp->recvbuf, pp->overflow);
       }
       else {
+        pp->overflow = 0; /* handled */
         /* Free the cache */
-        Curl_safefree(pp->cache);
-
-        /* Reset the cache size */
-        pp->cache_size = 0;
+        Curl_dyn_reset(&pp->recvbuf);
       }
     }
 
@@ -1222,7 +1217,7 @@
       data->req.maxdownload = size;
       /* force a recv/send check of this connection, as the data might've been
        read off the socket already */
-      data->conn->cselect_bits = CURL_CSELECT_IN;
+      data->state.select_bits = CURL_CSELECT_IN;
       Curl_setup_transfer(data, FIRSTSOCKET, size, FALSE, -1);
     }
   }
@@ -1378,7 +1373,6 @@
       break;
 
     case IMAP_LOGOUT:
-      /* fallthrough, just stop! */
     default:
       /* internal error */
       imap_state(data, IMAP_STOP);
@@ -1430,7 +1424,7 @@
   CURLcode result = CURLE_OK;
   struct IMAP *imap;
 
-  imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1);
+  imap = data->req.p.imap = calloc(1, sizeof(struct IMAP));
   if(!imap)
     result = CURLE_OUT_OF_MEMORY;
 
@@ -1474,9 +1468,7 @@
   Curl_sasl_init(&imapc->sasl, data, &saslimap);
 
   Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
-  /* Initialise the pingpong layer */
-  Curl_pp_setup(pp);
-  Curl_pp_init(data, pp);
+  Curl_pp_init(pp);
 
   /* Parse the URL options */
   result = imap_parse_url_options(conn);
@@ -1797,7 +1789,14 @@
   if(!result) {
     va_list ap;
     va_start(ap, fmt);
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
     result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
     va_end(ap);
   }
   return result;
diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c
index 7d3c698..176cc95 100644
--- a/Utilities/cmcurl/lib/inet_pton.c
+++ b/Utilities/cmcurl/lib/inet_pton.c
@@ -112,7 +112,8 @@
 
     pch = strchr(digits, ch);
     if(pch) {
-      unsigned int val = *tp * 10 + (unsigned int)(pch - digits);
+      unsigned int val = (unsigned int)(*tp * 10) +
+                         (unsigned int)(pch - digits);
 
       if(saw_digit && *tp == 0)
         return (0);
diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h
index 82fde7e..f8562fa 100644
--- a/Utilities/cmcurl/lib/inet_pton.h
+++ b/Utilities/cmcurl/lib/inet_pton.h
@@ -31,9 +31,6 @@
 #ifdef HAVE_INET_PTON
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
-#elif defined(HAVE_WS2TCPIP_H)
-/* inet_pton() exists in Vista or later */
-#include <ws2tcpip.h>
 #endif
 #define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
 #endif
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c
index a1102e5..4db19fb 100644
--- a/Utilities/cmcurl/lib/krb5.c
+++ b/Utilities/cmcurl/lib/krb5.c
@@ -1,6 +1,6 @@
 /* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
  *
- * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
  * (Royal Institute of Technology, Stockholm, Sweden).
  * Copyright (C) Daniel Stenberg
  * All rights reserved.
@@ -75,8 +75,7 @@
   unsigned char data_sec = conn->data_prot;
 #endif
 
-  if(!cmd)
-    return CURLE_BAD_FUNCTION_ARGUMENT;
+  DEBUGASSERT(cmd);
 
   write_len = strlen(cmd);
   if(!write_len || write_len > (sizeof(s) -3))
@@ -236,9 +235,12 @@
 
       if(Curl_GetFTPResponse(data, &nread, NULL))
         return -1;
-
-      if(data->state.buffer[0] != '3')
-        return -1;
+      else {
+        struct pingpong *pp = &conn->proto.ftpc.pp;
+        char *line = Curl_dyn_ptr(&pp->recvbuf);
+        if(line[0] != '3')
+          return -1;
+      }
     }
 
     stringp = aprintf("%s@%s", service, host);
@@ -322,15 +324,19 @@
           ret = -1;
           break;
         }
-
-        if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
-          infof(data, "Server didn't accept auth data");
-          ret = AUTH_ERROR;
-          break;
+        else {
+          struct pingpong *pp = &conn->proto.ftpc.pp;
+          size_t len = Curl_dyn_len(&pp->recvbuf);
+          p = Curl_dyn_ptr(&pp->recvbuf);
+          if((len < 4) || (p[0] != '2' && p[0] != '3')) {
+            infof(data, "Server didn't accept auth data");
+            ret = AUTH_ERROR;
+            break;
+          }
         }
 
         _gssresp.value = NULL; /* make sure it is initialized */
-        p = data->state.buffer + 4;
+        p += 4; /* over '789 ' */
         p = strstr(p, "ADAT=");
         if(p) {
           result = Curl_base64_decode(p + 5,
@@ -417,7 +423,6 @@
   case PROT_PRIVATE:
     return 'P';
   case PROT_CMD:
-    /* Fall through */
   default:
     /* Those 2 cases should not be reached! */
     break;
@@ -430,6 +435,9 @@
 /* Send an FTP command defined by |message| and the optional arguments. The
    function returns the ftp_code. If an error occurs, -1 is returned. */
 static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
+  CURL_PRINTF(2, 3);
+
+static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
 {
   int ftp_code;
   ssize_t nread = 0;
@@ -750,6 +758,8 @@
   if(level) {
     char *pbsz;
     unsigned int buffer_size = 1 << 20; /* 1048576 */
+    struct pingpong *pp = &conn->proto.ftpc.pp;
+    char *line;
 
     code = ftp_send_command(data, "PBSZ %u", buffer_size);
     if(code < 0)
@@ -761,10 +771,11 @@
     }
     conn->buffer_size = buffer_size;
 
-    pbsz = strstr(data->state.buffer, "PBSZ=");
+    line = Curl_dyn_ptr(&pp->recvbuf);
+    pbsz = strstr(line, "PBSZ=");
     if(pbsz) {
       /* stick to default value if the check fails */
-      if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5]))
+      if(ISDIGIT(pbsz[5]))
         buffer_size = atoi(&pbsz[5]);
       if(buffer_size < conn->buffer_size)
         conn->buffer_size = buffer_size;
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index 239d3fb..4c04647 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -137,7 +137,7 @@
                             _ldap_trace x; \
                           } while(0)
 
-  static void _ldap_trace(const char *fmt, ...);
+  static void _ldap_trace(const char *fmt, ...) CURL_PRINTF(1, 2);
 #else
   #define LDAP_TRACE(x)   Curl_nop_stmt
 #endif
@@ -177,7 +177,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
@@ -205,7 +205,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
@@ -313,7 +313,6 @@
   int ldap_ssl = 0;
   char *val_b64 = NULL;
   size_t val_b64_sz = 0;
-  curl_off_t dlsize = 0;
 #ifdef LDAP_OPT_NETWORK_TIMEOUT
   struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
 #endif
@@ -327,7 +326,7 @@
 
   *done = TRUE; /* unconditionally */
   infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
-          LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
+        LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
   infof(data, "LDAP local: %s", data->state.url);
 
 #ifdef HAVE_LDAP_URL_PARSE
@@ -345,7 +344,7 @@
   if(conn->given->flags & PROTOPT_SSL)
     ldap_ssl = 1;
   infof(data, "LDAP local: trying to establish %s connection",
-          ldap_ssl ? "encrypted" : "cleartext");
+        ldap_ssl ? "encrypted" : "cleartext");
 
 #if defined(USE_WIN32_LDAP)
   host = curlx_convert_UTF8_to_tchar(conn->host.name);
@@ -535,6 +534,7 @@
     goto quit;
   }
 
+  Curl_pgrsSetDownloadCounter(data, 0);
   rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
                      ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
 
@@ -596,8 +596,6 @@
         goto quit;
       }
 
-      dlsize += name_len + 5;
-
       FREE_ON_WINLDAP(name);
       ldap_memfree(dn);
     }
@@ -659,8 +657,6 @@
             goto quit;
           }
 
-          dlsize += attr_len + 3;
-
           if((attr_len > 7) &&
              (strcmp(";binary", attr + (attr_len - 7)) == 0)) {
             /* Binary attribute, encode to base64. */
@@ -689,8 +685,6 @@
 
                 goto quit;
               }
-
-              dlsize += val_b64_sz;
             }
           }
           else {
@@ -705,8 +699,6 @@
 
               goto quit;
             }
-
-            dlsize += vals[i]->bv_len;
           }
 
           result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
@@ -719,8 +711,6 @@
 
             goto quit;
           }
-
-          dlsize++;
         }
 
         /* Free memory used to store values */
@@ -734,10 +724,6 @@
       result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
       if(result)
         goto quit;
-      dlsize++;
-      result = Curl_pgrsSetDownloadCounter(data, dlsize);
-      if(result)
-        goto quit;
     }
 
     if(ber)
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index 30ab62e..067c211 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -32,9 +32,8 @@
 #include "warnless.h"
 
 #ifdef USE_OPENSSL
-#include <openssl/opensslconf.h>
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \
-   !defined(USE_AMISSL)
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL)
 /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
 #define OPENSSL_NO_MD4
 #endif
@@ -195,11 +194,9 @@
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
 {
   if(!ctx->data) {
-    ctx->data = malloc(size);
-    if(ctx->data) {
-      memcpy(ctx->data, data, size);
+    ctx->data = Curl_memdup(data, size);
+    if(ctx->data)
       ctx->size = size;
-    }
   }
 }
 
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c
index d6952a0..fce933a 100644
--- a/Utilities/cmcurl/lib/memdebug.c
+++ b/Utilities/cmcurl/lib/memdebug.c
@@ -208,7 +208,7 @@
   return mem;
 }
 
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
                                     int line, const char *source)
 {
@@ -304,12 +304,6 @@
 curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
                              int line, const char *source)
 {
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-    "FD %s:%d socket() = %d\n" :
-    (sizeof(curl_socket_t) == sizeof(long)) ?
-    "FD %s:%d socket() = %ld\n" :
-    "FD %s:%d socket() = %zd\n";
-
   curl_socket_t sockfd;
 
   if(countcheck("socket", line, source))
@@ -318,7 +312,8 @@
   sockfd = socket(domain, type, protocol);
 
   if(source && (sockfd != CURL_SOCKET_BAD))
-    curl_dbg_log(fmt, source, line, sockfd);
+    curl_dbg_log("FD %s:%d socket() = %" CURL_FORMAT_SOCKET_T "\n",
+                 source, line, sockfd);
 
   return sockfd;
 }
@@ -357,16 +352,12 @@
                        curl_socket_t socket_vector[2],
                        int line, const char *source)
 {
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-    "FD %s:%d socketpair() = %d %d\n" :
-    (sizeof(curl_socket_t) == sizeof(long)) ?
-    "FD %s:%d socketpair() = %ld %ld\n" :
-    "FD %s:%d socketpair() = %zd %zd\n";
-
   int res = socketpair(domain, type, protocol, socket_vector);
 
   if(source && (0 == res))
-    curl_dbg_log(fmt, source, line, socket_vector[0], socket_vector[1]);
+    curl_dbg_log("FD %s:%d socketpair() = "
+      "%" CURL_FORMAT_SOCKET_T " %" CURL_FORMAT_SOCKET_T "\n",
+      source, line, socket_vector[0], socket_vector[1]);
 
   return res;
 }
@@ -375,19 +366,14 @@
 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
                              int line, const char *source)
 {
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-    "FD %s:%d accept() = %d\n" :
-    (sizeof(curl_socket_t) == sizeof(long)) ?
-    "FD %s:%d accept() = %ld\n" :
-    "FD %s:%d accept() = %zd\n";
-
   struct sockaddr *addr = (struct sockaddr *)saddr;
   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
 
   curl_socket_t sockfd = accept(s, addr, addrlen);
 
   if(source && (sockfd != CURL_SOCKET_BAD))
-    curl_dbg_log(fmt, source, line, sockfd);
+    curl_dbg_log("FD %s:%d accept() = %" CURL_FORMAT_SOCKET_T "\n",
+                 source, line, sockfd);
 
   return sockfd;
 }
@@ -395,14 +381,9 @@
 /* separate function to allow libcurl to mark a "faked" close */
 void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
 {
-  const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ?
-    "FD %s:%d sclose(%d)\n":
-    (sizeof(curl_socket_t) == sizeof(long)) ?
-    "FD %s:%d sclose(%ld)\n":
-    "FD %s:%d sclose(%zd)\n";
-
   if(source)
-    curl_dbg_log(fmt, source, line, sockfd);
+    curl_dbg_log("FD %s:%d sclose(%" CURL_FORMAT_SOCKET_T ")\n",
+                 source, line, sockfd);
 }
 
 /* this is our own defined way to close sockets on *ALL* platforms */
diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h
index c9eb5dc..51147cd 100644
--- a/Utilities/cmcurl/lib/memdebug.h
+++ b/Utilities/cmcurl/lib/memdebug.h
@@ -64,7 +64,7 @@
 CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
 CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
                                              const char *src);
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
                                                 int line,
                                                 const char *source);
@@ -72,7 +72,7 @@
 
 CURL_EXTERN void curl_dbg_memdebug(const char *logname);
 CURL_EXTERN void curl_dbg_memlimit(long limit);
-CURL_EXTERN void curl_dbg_log(const char *format, ...);
+CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2);
 
 /* file descriptor manipulators */
 CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
@@ -121,7 +121,7 @@
 #define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
 #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index 3b27e59..d712331 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -30,6 +30,7 @@
 #include "warnless.h"
 #include "urldata.h"
 #include "sendf.h"
+#include "strdup.h"
 
 #if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
                                     !defined(CURL_DISABLE_SMTP) ||      \
@@ -48,7 +49,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 # ifndef R_OK
 #  define R_OK 4
 # endif
@@ -311,8 +312,7 @@
   table = formtable;
   /* data can be NULL when this function is called indirectly from
      curl_formget(). */
-  if(strategy == MIMESTRATEGY_MAIL ||
-     (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE)))
+  if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape)))
     table = mimetable;
 
   Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
@@ -818,7 +818,7 @@
     case MIMEKIND_FILE:
       if(part->fp && feof(part->fp))
         break;  /* At EOF. */
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     default:
       if(part->readfunc) {
         if(!(part->flags & MIME_FAST_READ)) {
@@ -937,7 +937,7 @@
         mimesetstate(&part->state, MIMESTATE_USERHEADERS, hdr->next);
         break;
       }
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case MIMESTATE_CURLHEADERS:
       if(!hdr)
         mimesetstate(&part->state, MIMESTATE_USERHEADERS, part->userheaders);
@@ -971,7 +971,7 @@
           fclose(part->fp);
           part->fp = NULL;
         }
-        /* FALLTHROUGH */
+        FALLTHROUGH();
       case CURL_READFUNC_ABORT:
       case CURL_READFUNC_PAUSE:
       case READ_ERROR:
@@ -1236,6 +1236,7 @@
     }
     break;
   default:  /* Invalid kind: should not occur. */
+    DEBUGF(infof(data, "invalid MIMEKIND* attempt"));
     res = CURLE_BAD_FUNCTION_ARGUMENT;  /* Internal error? */
     break;
   }
@@ -1371,27 +1372,22 @@
 
 /* Set mime part content from memory data. */
 CURLcode curl_mime_data(curl_mimepart *part,
-                        const char *data, size_t datasize)
+                        const char *ptr, size_t datasize)
 {
   if(!part)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
   cleanup_part_content(part);
 
-  if(data) {
+  if(ptr) {
     if(datasize == CURL_ZERO_TERMINATED)
-      datasize = strlen(data);
+      datasize = strlen(ptr);
 
-    part->data = malloc(datasize + 1);
+    part->data = Curl_memdup0(ptr, datasize);
     if(!part->data)
       return CURLE_OUT_OF_MEMORY;
 
     part->datasize = datasize;
-
-    if(datasize)
-      memcpy(part->data, data, datasize);
-    part->data[datasize] = '\0';    /* Set a null terminator as sentinel. */
-
     part->readfunc = mime_mem_read;
     part->seekfunc = mime_mem_seek;
     part->freefunc = mime_mem_free;
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h
index 0a05c2a..a64f41d 100644
--- a/Utilities/cmcurl/lib/mime.h
+++ b/Utilities/cmcurl/lib/mime.h
@@ -130,7 +130,8 @@
   size_t lastreadstatus;           /* Last read callback returned status. */
 };
 
-CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+  CURL_PRINTF(2, 3);
 
 #if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
                                     !defined(CURL_DISABLE_SMTP) ||      \
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index af5d753..63f7f24 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -20,25 +20,11 @@
  *
  * SPDX-License-Identifier: curl
  *
- *
- * Purpose:
- *  A merge of Bjorn Reese's format() function and Daniel's dsprintf()
- *  1.0. A full blooded printf() clone with full support for <num>$
- *  everywhere (parameters, widths and precisions) including variabled
- *  sized parameters (like doubles, long longs, long doubles and even
- *  void * in 64-bit architectures).
- *
- * Current restrictions:
- * - Max 128 parameters
- * - No 'long double' support.
- *
- * If you ever want truly portable and good *printf() clones, the project that
- * took on from here is named 'Trio' and you find more details on the trio web
- * page at https://daniel.haxx.se/projects/trio/
  */
 
 #include "curl_setup.h"
 #include "dynbuf.h"
+#include "curl_printf.h"
 #include <curl/mprintf.h>
 
 #include "curl_memory.h"
@@ -66,9 +52,7 @@
  * Non-ANSI integer extensions
  */
 
-#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
-    (defined(__POCC__) && defined(_MSC_VER)) || \
-    (defined(_WIN32_WCE)) || \
+#if (defined(_WIN32_WCE)) || \
     (defined(__MINGW32__)) || \
     (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
 #  define MP_HAVE_INT_EXTENSIONS
@@ -88,7 +72,8 @@
 
 #define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
                         fit negative DBL_MAX (317 letters) */
-#define MAX_PARAMETERS 128 /* lame static limit */
+#define MAX_PARAMETERS 128 /* number of input arguments */
+#define MAX_SEGMENTS   128 /* number of output segments */
 
 #ifdef __AMIGA__
 # undef FORMAT_INT
@@ -100,31 +85,33 @@
 /* Upper-case digits.  */
 static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
-#define OUTCHAR(x)                                     \
-  do {                                                 \
-    if(stream((unsigned char)(x), (FILE *)data) != -1) \
-      done++;                                          \
-    else                                               \
-      return done; /* return immediately on failure */ \
+#define OUTCHAR(x)                                      \
+  do {                                                  \
+    if(!stream(x, userp))                               \
+      done++;                                           \
+    else                                                \
+      return done; /* return on failure */              \
   } while(0)
 
 /* Data type to read from the arglist */
 typedef enum {
-  FORMAT_UNKNOWN = 0,
   FORMAT_STRING,
   FORMAT_PTR,
-  FORMAT_INT,
   FORMAT_INTPTR,
+  FORMAT_INT,
   FORMAT_LONG,
   FORMAT_LONGLONG,
+  FORMAT_INTU,
+  FORMAT_LONGU,
+  FORMAT_LONGLONGU,
   FORMAT_DOUBLE,
   FORMAT_LONGDOUBLE,
-  FORMAT_WIDTH /* For internal use */
+  FORMAT_WIDTH,
+  FORMAT_PRECISION
 } FormatType;
 
 /* conversion and display flags */
 enum {
-  FLAGS_NEW        = 0,
   FLAGS_SPACE      = 1<<0,
   FLAGS_SHOWSIGN   = 1<<1,
   FLAGS_LEFT       = 1<<2,
@@ -144,23 +131,40 @@
   FLAGS_PRECPARAM  = 1<<16, /* precision PARAMETER was specified */
   FLAGS_CHAR       = 1<<17, /* %c story */
   FLAGS_FLOATE     = 1<<18, /* %e or %E */
-  FLAGS_FLOATG     = 1<<19  /* %g or %G */
+  FLAGS_FLOATG     = 1<<19, /* %g or %G */
+  FLAGS_SUBSTR     = 1<<20  /* no input, only substring */
 };
 
-struct va_stack {
-  FormatType type;
-  int flags;
-  long width;     /* width OR width parameter number */
-  long precision; /* precision OR precision parameter number */
+enum {
+  DOLLAR_UNKNOWN,
+  DOLLAR_NOPE,
+  DOLLAR_USE
+};
+
+/*
+ * Describes an input va_arg type and hold its value.
+ */
+struct va_input {
+  FormatType type; /* FormatType */
   union {
     char *str;
     void *ptr;
-    union {
-      mp_intmax_t as_signed;
-      mp_uintmax_t as_unsigned;
-    } num;
+    mp_intmax_t nums; /* signed */
+    mp_uintmax_t numu; /* unsigned */
     double dnum;
-  } data;
+  } val;
+};
+
+/*
+ * Describes an output segment.
+ */
+struct outsegment {
+  int width;     /* width OR width parameter number */
+  int precision; /* precision OR precision parameter number */
+  unsigned int flags;
+  unsigned int input; /* input argument array index */
+  char *start;      /* format string start to output */
+  size_t outlen;     /* number of bytes from the format string to output */
 };
 
 struct nsprintf {
@@ -171,118 +175,123 @@
 
 struct asprintf {
   struct dynbuf *b;
-  bool fail; /* if an alloc has failed and thus the output is not the complete
-                data */
+  char merr;
 };
 
-static long dprintf_DollarString(char *input, char **end)
+/* the provided input number is 1-based but this returns the number 0-based.
+
+   returns -1 if no valid number was provided.
+*/
+static int dollarstring(char *input, char **end)
 {
-  int number = 0;
-  while(ISDIGIT(*input)) {
-    if(number < MAX_PARAMETERS) {
-      number *= 10;
-      number += *input - '0';
+  if(ISDIGIT(*input)) {
+    int number = 0;
+    do {
+      if(number < MAX_PARAMETERS) {
+        number *= 10;
+        number += *input - '0';
+      }
+      input++;
+    } while(ISDIGIT(*input));
+
+    if(number && (number <= MAX_PARAMETERS) && ('$' == *input)) {
+      *end = ++input;
+      return number - 1;
     }
-    input++;
   }
-  if(number <= MAX_PARAMETERS && ('$' == *input)) {
-    *end = ++input;
-    return number;
-  }
-  return 0;
+  return -1;
 }
 
-static bool dprintf_IsQualifierNoDollar(const char *fmt)
-{
-#if defined(MP_HAVE_INT_EXTENSIONS)
-  if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
-    return TRUE;
-  }
-#endif
-
-  switch(*fmt) {
-  case '-': case '+': case ' ': case '#': case '.':
-  case '0': case '1': case '2': case '3': case '4':
-  case '5': case '6': case '7': case '8': case '9':
-  case 'h': case 'l': case 'L': case 'z': case 'q':
-  case '*': case 'O':
-#if defined(MP_HAVE_INT_EXTENSIONS)
-  case 'I':
-#endif
-    return TRUE;
-
-  default:
-    return FALSE;
-  }
-}
-
-/******************************************************************
+/*
+ * Parse the format string.
  *
- * Pass 1:
- * Create an index with the type of each parameter entry and its
- * value (may vary in size)
+ * Create two arrays. One describes the inputs, one describes the outputs.
  *
  * Returns zero on success.
- *
- ******************************************************************/
+ */
 
-static int dprintf_Pass1(const char *format, struct va_stack *vto,
-                         char **endpos, va_list arglist)
+#define PFMT_OK          0
+#define PFMT_DOLLAR      1 /* bad dollar for main param */
+#define PFMT_DOLLARWIDTH 2 /* bad dollar use for width */
+#define PFMT_DOLLARPREC  3 /* bad dollar use for precision */
+#define PFMT_MANYARGS    4 /* too many input arguments used */
+#define PFMT_PREC        5 /* precision overflow */
+#define PFMT_PRECMIX     6 /* bad mix of precision specifiers */
+#define PFMT_WIDTH       7 /* width overflow */
+#define PFMT_INPUTGAP    8 /* gap in arguments */
+#define PFMT_WIDTHARG    9 /* attempted to use same arg twice, for width */
+#define PFMT_PRECARG    10 /* attempted to use same arg twice, for prec */
+#define PFMT_MANYSEGS   11 /* maxed out output segments */
+
+static int parsefmt(const char *format,
+                    struct outsegment *out,
+                    struct va_input *in,
+                    int *opieces,
+                    int *ipieces, va_list arglist)
 {
   char *fmt = (char *)format;
   int param_num = 0;
-  long this_param;
-  long width;
-  long precision;
-  int flags;
-  long max_param = 0;
-  long i;
+  int param;
+  int width;
+  int precision;
+  unsigned int flags;
+  FormatType type;
+  int max_param = -1;
+  int i;
+  int ocount = 0;
+  unsigned char usedinput[MAX_PARAMETERS/8];
+  size_t outlen = 0;
+  struct outsegment *optr;
+  int use_dollar = DOLLAR_UNKNOWN;
+  char *start = fmt;
+
+  /* clear, set a bit for each used input */
+  memset(usedinput, 0, sizeof(usedinput));
 
   while(*fmt) {
-    if(*fmt++ == '%') {
+    if(*fmt == '%') {
+      struct va_input *iptr;
+      bool loopit = TRUE;
+      fmt++;
+      outlen = fmt - start - 1;
       if(*fmt == '%') {
+        /* this means a %% that should be output only as %. Create an output
+           segment. */
+        if(outlen) {
+          optr = &out[ocount++];
+          if(ocount > MAX_SEGMENTS)
+            return PFMT_MANYSEGS;
+          optr->input = 0;
+          optr->flags = FLAGS_SUBSTR;
+          optr->start = start;
+          optr->outlen = outlen;
+        }
+        start = fmt;
         fmt++;
         continue; /* while */
       }
 
-      flags = FLAGS_NEW;
+      flags = width = precision = 0;
 
-      /* Handle the positional case (N$) */
+      if(use_dollar != DOLLAR_NOPE) {
+        param = dollarstring(fmt, &fmt);
+        if(param < 0) {
+          if(use_dollar == DOLLAR_USE)
+            /* illegal combo */
+            return PFMT_DOLLAR;
 
-      param_num++;
-
-      this_param = dprintf_DollarString(fmt, &fmt);
-      if(0 == this_param)
-        /* we got no positional, get the next counter */
-        this_param = param_num;
-
-      if(this_param > max_param)
-        max_param = this_param;
-
-      /*
-       * The parameter with number 'i' should be used. Next, we need
-       * to get SIZE and TYPE of the parameter. Add the information
-       * to our array.
-       */
-
-      width = 0;
-      precision = 0;
-
-      /* Handle the flags */
-
-      while(dprintf_IsQualifierNoDollar(fmt)) {
-#if defined(MP_HAVE_INT_EXTENSIONS)
-        if(!strncmp(fmt, "I32", 3)) {
-          flags |= FLAGS_LONG;
-          fmt += 3;
-        }
-        else if(!strncmp(fmt, "I64", 3)) {
-          flags |= FLAGS_LONGLONG;
-          fmt += 3;
+          /* we got no positional, just get the next arg */
+          param = -1;
+          use_dollar = DOLLAR_NOPE;
         }
         else
-#endif
+          use_dollar = DOLLAR_USE;
+      }
+      else
+        param = -1;
 
+      /* Handle the flags */
+      while(loopit) {
         switch(*fmt++) {
         case ' ':
           flags |= FLAGS_SPACE;
@@ -300,40 +309,63 @@
         case '.':
           if('*' == *fmt) {
             /* The precision is picked from a specified parameter */
-
             flags |= FLAGS_PRECPARAM;
             fmt++;
-            param_num++;
 
-            i = dprintf_DollarString(fmt, &fmt);
-            if(i)
-              precision = i;
+            if(use_dollar == DOLLAR_USE) {
+              precision = dollarstring(fmt, &fmt);
+              if(precision < 0)
+                /* illegal combo */
+                return PFMT_DOLLARPREC;
+            }
             else
-              precision = param_num;
-
-            if(precision > max_param)
-              max_param = precision;
+              /* get it from the next argument */
+              precision = -1;
           }
           else {
+            bool is_neg = FALSE;
             flags |= FLAGS_PREC;
-            precision = strtol(fmt, &fmt, 10);
+            precision = 0;
+            if('-' == *fmt) {
+              is_neg = TRUE;
+              fmt++;
+            }
+            while(ISDIGIT(*fmt)) {
+              if(precision > INT_MAX/10)
+                return PFMT_PREC;
+              precision *= 10;
+              precision += *fmt - '0';
+              fmt++;
+            }
+            if(is_neg)
+              precision = -precision;
           }
           if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
              (FLAGS_PREC | FLAGS_PRECPARAM))
             /* it is not permitted to use both kinds of precision for the same
                argument */
-            return 1;
+            return PFMT_PRECMIX;
           break;
         case 'h':
           flags |= FLAGS_SHORT;
           break;
 #if defined(MP_HAVE_INT_EXTENSIONS)
         case 'I':
+          if((fmt[0] == '3') && (fmt[1] == '2')) {
+            flags |= FLAGS_LONG;
+            fmt += 2;
+          }
+          else if((fmt[0] == '6') && (fmt[1] == '4')) {
+            flags |= FLAGS_LONGLONG;
+            fmt += 2;
+          }
+          else {
 #if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
-          flags |= FLAGS_LONGLONG;
+            flags |= FLAGS_LONGLONG;
 #else
-          flags |= FLAGS_LONG;
+            flags |= FLAGS_LONG;
 #endif
+          }
           break;
 #endif
         case 'l':
@@ -367,401 +399,421 @@
         case '0':
           if(!(flags & FLAGS_LEFT))
             flags |= FLAGS_PAD_NIL;
-          /* FALLTHROUGH */
+          FALLTHROUGH();
         case '1': case '2': case '3': case '4':
         case '5': case '6': case '7': case '8': case '9':
           flags |= FLAGS_WIDTH;
-          width = strtol(fmt-1, &fmt, 10);
-          break;
-        case '*':  /* Special case */
-          flags |= FLAGS_WIDTHPARAM;
-          param_num++;
-
-          i = dprintf_DollarString(fmt, &fmt);
-          if(i)
-            width = i;
-          else
-            width = param_num;
-          if(width > max_param)
-            max_param = width;
-          break;
-        case '\0':
+          width = 0;
           fmt--;
-        default:
+          do {
+            if(width > INT_MAX/10)
+              return PFMT_WIDTH;
+            width *= 10;
+            width += *fmt - '0';
+            fmt++;
+          } while(ISDIGIT(*fmt));
           break;
-        }
-      } /* switch */
-
-      /* Handle the specifier */
-
-      i = this_param - 1;
-
-      if((i < 0) || (i >= MAX_PARAMETERS))
-        /* out of allowed range */
-        return 1;
+        case '*':  /* read width from argument list */
+          flags |= FLAGS_WIDTHPARAM;
+          if(use_dollar == DOLLAR_USE) {
+            width = dollarstring(fmt, &fmt);
+            if(width < 0)
+              /* illegal combo */
+              return PFMT_DOLLARWIDTH;
+          }
+          else
+            /* pick from the next argument */
+            width = -1;
+          break;
+        default:
+          loopit = FALSE;
+          fmt--;
+          break;
+        } /* switch */
+      } /* while */
 
       switch(*fmt) {
       case 'S':
         flags |= FLAGS_ALT;
-        /* FALLTHROUGH */
+        FALLTHROUGH();
       case 's':
-        vto[i].type = FORMAT_STRING;
+        type = FORMAT_STRING;
         break;
       case 'n':
-        vto[i].type = FORMAT_INTPTR;
+        type = FORMAT_INTPTR;
         break;
       case 'p':
-        vto[i].type = FORMAT_PTR;
+        type = FORMAT_PTR;
         break;
-      case 'd': case 'i':
-        vto[i].type = FORMAT_INT;
+      case 'd':
+      case 'i':
+        if(flags & FLAGS_LONGLONG)
+          type = FORMAT_LONGLONG;
+        else if(flags & FLAGS_LONG)
+          type = FORMAT_LONG;
+        else
+          type = FORMAT_INT;
         break;
       case 'u':
-        vto[i].type = FORMAT_INT;
+        if(flags & FLAGS_LONGLONG)
+          type = FORMAT_LONGLONGU;
+        else if(flags & FLAGS_LONG)
+          type = FORMAT_LONGU;
+        else
+          type = FORMAT_INTU;
         flags |= FLAGS_UNSIGNED;
         break;
       case 'o':
-        vto[i].type = FORMAT_INT;
+        type = FORMAT_INT;
         flags |= FLAGS_OCTAL;
         break;
       case 'x':
-        vto[i].type = FORMAT_INT;
+        type = FORMAT_INTU;
         flags |= FLAGS_HEX|FLAGS_UNSIGNED;
         break;
       case 'X':
-        vto[i].type = FORMAT_INT;
+        type = FORMAT_INTU;
         flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED;
         break;
       case 'c':
-        vto[i].type = FORMAT_INT;
+        type = FORMAT_INT;
         flags |= FLAGS_CHAR;
         break;
       case 'f':
-        vto[i].type = FORMAT_DOUBLE;
+        type = FORMAT_DOUBLE;
         break;
       case 'e':
-        vto[i].type = FORMAT_DOUBLE;
+        type = FORMAT_DOUBLE;
         flags |= FLAGS_FLOATE;
         break;
       case 'E':
-        vto[i].type = FORMAT_DOUBLE;
+        type = FORMAT_DOUBLE;
         flags |= FLAGS_FLOATE|FLAGS_UPPER;
         break;
       case 'g':
-        vto[i].type = FORMAT_DOUBLE;
+        type = FORMAT_DOUBLE;
         flags |= FLAGS_FLOATG;
         break;
       case 'G':
-        vto[i].type = FORMAT_DOUBLE;
+        type = FORMAT_DOUBLE;
         flags |= FLAGS_FLOATG|FLAGS_UPPER;
         break;
       default:
-        vto[i].type = FORMAT_UNKNOWN;
-        break;
+        /* invalid instruction, disregard and continue */
+        continue;
       } /* switch */
 
-      vto[i].flags = flags;
-      vto[i].width = width;
-      vto[i].precision = precision;
-
       if(flags & FLAGS_WIDTHPARAM) {
-        /* we have the width specified from a parameter, so we make that
-           parameter's info setup properly */
-        long k = width - 1;
-        if((k < 0) || (k >= MAX_PARAMETERS))
-          /* out of allowed range */
-          return 1;
-        vto[i].width = k;
-        vto[k].type = FORMAT_WIDTH;
-        vto[k].flags = FLAGS_NEW;
-        /* can't use width or precision of width! */
-        vto[k].width = 0;
-        vto[k].precision = 0;
+        if(width < 0)
+          width = param_num++;
+        else {
+          /* if this identifies a parameter already used, this
+             is illegal */
+          if(usedinput[width/8] & (1 << (width&7)))
+            return PFMT_WIDTHARG;
+        }
+        if(width >= MAX_PARAMETERS)
+          return PFMT_MANYARGS;
+        if(width >= max_param)
+          max_param = width;
+
+        in[width].type = FORMAT_WIDTH;
+        /* mark as used */
+        usedinput[width/8] |= (unsigned char)(1 << (width&7));
       }
+
       if(flags & FLAGS_PRECPARAM) {
-        /* we have the precision specified from a parameter, so we make that
-           parameter's info setup properly */
-        long k = precision - 1;
-        if((k < 0) || (k >= MAX_PARAMETERS))
-          /* out of allowed range */
-          return 1;
-        vto[i].precision = k;
-        vto[k].type = FORMAT_WIDTH;
-        vto[k].flags = FLAGS_NEW;
-        /* can't use width or precision of width! */
-        vto[k].width = 0;
-        vto[k].precision = 0;
+        if(precision < 0)
+          precision = param_num++;
+        else {
+          /* if this identifies a parameter already used, this
+             is illegal */
+          if(usedinput[precision/8] & (1 << (precision&7)))
+            return PFMT_PRECARG;
+        }
+        if(precision >= MAX_PARAMETERS)
+          return PFMT_MANYARGS;
+        if(precision >= max_param)
+          max_param = precision;
+
+        in[precision].type = FORMAT_PRECISION;
+        usedinput[precision/8] |= (unsigned char)(1 << (precision&7));
       }
-      *endpos++ = fmt + ((*fmt == '\0') ? 0 : 1); /* end of this sequence */
+
+      /* Handle the specifier */
+      if(param < 0)
+        param = param_num++;
+      if(param >= MAX_PARAMETERS)
+        return PFMT_MANYARGS;
+      if(param >= max_param)
+        max_param = param;
+
+      iptr = &in[param];
+      iptr->type = type;
+
+      /* mark this input as used */
+      usedinput[param/8] |= (unsigned char)(1 << (param&7));
+
+      fmt++;
+      optr = &out[ocount++];
+      if(ocount > MAX_SEGMENTS)
+        return PFMT_MANYSEGS;
+      optr->input = param;
+      optr->flags = flags;
+      optr->width = width;
+      optr->precision = precision;
+      optr->start = start;
+      optr->outlen = outlen;
+      start = fmt;
     }
+    else
+      fmt++;
+  }
+
+  /* is there a trailing piece */
+  outlen = fmt - start;
+  if(outlen) {
+    optr = &out[ocount++];
+    if(ocount > MAX_SEGMENTS)
+      return PFMT_MANYSEGS;
+    optr->input = 0;
+    optr->flags = FLAGS_SUBSTR;
+    optr->start = start;
+    optr->outlen = outlen;
   }
 
   /* Read the arg list parameters into our data list */
-  for(i = 0; i<max_param; i++) {
-    /* Width/precision arguments must be read before the main argument
-       they are attached to */
-    if(vto[i].flags & FLAGS_WIDTHPARAM) {
-      vto[vto[i].width].data.num.as_signed =
-        (mp_intmax_t)va_arg(arglist, int);
-    }
-    if(vto[i].flags & FLAGS_PRECPARAM) {
-      vto[vto[i].precision].data.num.as_signed =
-        (mp_intmax_t)va_arg(arglist, int);
-    }
+  for(i = 0; i < max_param + 1; i++) {
+    struct va_input *iptr = &in[i];
+    if(!(usedinput[i/8] & (1 << (i&7))))
+      /* bad input */
+      return PFMT_INPUTGAP;
 
-    switch(vto[i].type) {
+    /* based on the type, read the correct argument */
+    switch(iptr->type) {
     case FORMAT_STRING:
-      vto[i].data.str = va_arg(arglist, char *);
+      iptr->val.str = va_arg(arglist, char *);
       break;
 
     case FORMAT_INTPTR:
-    case FORMAT_UNKNOWN:
     case FORMAT_PTR:
-      vto[i].data.ptr = va_arg(arglist, void *);
+      iptr->val.ptr = va_arg(arglist, void *);
+      break;
+
+    case FORMAT_LONGLONGU:
+      iptr->val.numu = (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
+      break;
+
+    case FORMAT_LONGLONG:
+      iptr->val.nums = (mp_intmax_t)va_arg(arglist, mp_intmax_t);
+      break;
+
+    case FORMAT_LONGU:
+      iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned long);
+      break;
+
+    case FORMAT_LONG:
+      iptr->val.nums = (mp_intmax_t)va_arg(arglist, long);
+      break;
+
+    case FORMAT_INTU:
+      iptr->val.numu = (mp_uintmax_t)va_arg(arglist, unsigned int);
       break;
 
     case FORMAT_INT:
-#ifdef HAVE_LONG_LONG_TYPE
-      if((vto[i].flags & FLAGS_LONGLONG) && (vto[i].flags & FLAGS_UNSIGNED))
-        vto[i].data.num.as_unsigned =
-          (mp_uintmax_t)va_arg(arglist, mp_uintmax_t);
-      else if(vto[i].flags & FLAGS_LONGLONG)
-        vto[i].data.num.as_signed =
-          (mp_intmax_t)va_arg(arglist, mp_intmax_t);
-      else
-#endif
-      {
-        if((vto[i].flags & FLAGS_LONG) && (vto[i].flags & FLAGS_UNSIGNED))
-          vto[i].data.num.as_unsigned =
-            (mp_uintmax_t)va_arg(arglist, unsigned long);
-        else if(vto[i].flags & FLAGS_LONG)
-          vto[i].data.num.as_signed =
-            (mp_intmax_t)va_arg(arglist, long);
-        else if(vto[i].flags & FLAGS_UNSIGNED)
-          vto[i].data.num.as_unsigned =
-            (mp_uintmax_t)va_arg(arglist, unsigned int);
-        else
-          vto[i].data.num.as_signed =
-            (mp_intmax_t)va_arg(arglist, int);
-      }
+    case FORMAT_WIDTH:
+    case FORMAT_PRECISION:
+      iptr->val.nums = (mp_intmax_t)va_arg(arglist, int);
       break;
 
     case FORMAT_DOUBLE:
-      vto[i].data.dnum = va_arg(arglist, double);
-      break;
-
-    case FORMAT_WIDTH:
-      /* Argument has been read. Silently convert it into an integer
-       * for later use
-       */
-      vto[i].type = FORMAT_INT;
+      iptr->val.dnum = va_arg(arglist, double);
       break;
 
     default:
+      DEBUGASSERT(NULL); /* unexpected */
       break;
     }
   }
+  *ipieces = max_param + 1;
+  *opieces = ocount;
 
-  return 0;
-
+  return PFMT_OK;
 }
 
-static int dprintf_formatf(
-  void *data, /* untouched by format(), just sent to the stream() function in
-                 the second argument */
+/*
+ * formatf() - the general printf function.
+ *
+ * It calls parsefmt() to parse the format string. It populates two arrays;
+ * one that describes the input arguments and one that describes a number of
+ * output segments.
+ *
+ * On success, the input array describes the type of all arguments and their
+ * values.
+ *
+ * The function then iterates over the output sengments and outputs them one
+ * by one until done. Using the appropriate input arguments (if any).
+ *
+ * All output is sent to the 'stream()' callback, one byte at a time.
+ */
+
+static int formatf(
+  void *userp, /* untouched by format(), just sent to the stream() function in
+                  the second argument */
   /* function pointer called for each output character */
-  int (*stream)(int, FILE *),
+  int (*stream)(unsigned char, void *),
   const char *format,    /* %-formatted string */
   va_list ap_save) /* list of parameters */
 {
-  /* Base-36 digits for numbers.  */
-  const char *digits = lower_digits;
+  static const char nilstr[] = "(nil)";
+  const char *digits = lower_digits;   /* Base-36 digits for numbers.  */
+  int done = 0;   /* number of characters written  */
+  int i;
+  int ocount = 0; /* number of output segments */
+  int icount = 0; /* number of input arguments */
 
-  /* Pointer into the format string.  */
-  char *f;
-
-  /* Number of characters written.  */
-  int done = 0;
-
-  long param; /* current parameter to read */
-  long param_num = 0; /* parameter counter */
-
-  struct va_stack vto[MAX_PARAMETERS];
-  char *endpos[MAX_PARAMETERS];
-  char **end;
+  struct outsegment output[MAX_SEGMENTS];
+  struct va_input input[MAX_PARAMETERS];
   char work[BUFFSIZE];
-  struct va_stack *p;
 
   /* 'workend' points to the final buffer byte position, but with an extra
      byte as margin to avoid the (false?) warning Coverity gives us
      otherwise */
   char *workend = &work[sizeof(work) - 2];
 
-  /* Do the actual %-code parsing */
-  if(dprintf_Pass1(format, vto, endpos, ap_save))
+  /* Parse the format string */
+  if(parsefmt(format, output, input, &ocount, &icount, ap_save))
     return 0;
 
-  end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
-                       created for us */
-
-  f = (char *)format;
-  while(*f != '\0') {
-    /* Format spec modifiers.  */
-    int is_alt;
-
-    /* Width of a field.  */
-    long width;
-
-    /* Precision of a field.  */
-    long prec;
-
-    /* Decimal integer is negative.  */
-    int is_neg;
-
-    /* Base of a number to be written.  */
-    unsigned long base;
-
-    /* Integral values to be written.  */
-    mp_uintmax_t num;
-
-    /* Used to convert negative in positive.  */
-    mp_intmax_t signed_num;
-
+  for(i = 0; i < ocount; i++) {
+    struct outsegment *optr = &output[i];
+    struct va_input *iptr;
+    bool is_alt;            /* Format spec modifiers.  */
+    int width;              /* Width of a field.  */
+    int prec;               /* Precision of a field.  */
+    bool is_neg;            /* Decimal integer is negative.  */
+    unsigned long base;     /* Base of a number to be written.  */
+    mp_uintmax_t num;       /* Integral values to be written.  */
+    mp_intmax_t signed_num; /* Used to convert negative in positive.  */
     char *w;
+    size_t outlen = optr->outlen;
+    int flags = optr->flags;
 
-    if(*f != '%') {
-      /* This isn't a format spec, so write everything out until the next one
-         OR end of string is reached.  */
-      do {
-        OUTCHAR(*f);
-      } while(*++f && ('%' != *f));
-      continue;
+    if(outlen) {
+      char *str = optr->start;
+      for(; outlen && *str; outlen--)
+        OUTCHAR(*str++);
+      if(optr->flags & FLAGS_SUBSTR)
+        /* this is just a substring */
+        continue;
     }
 
-    ++f;
-
-    /* Check for "%%".  Note that although the ANSI standard lists
-       '%' as a conversion specifier, it says "The complete format
-       specification shall be `%%'," so we can avoid all the width
-       and precision processing.  */
-    if(*f == '%') {
-      ++f;
-      OUTCHAR('%');
-      continue;
-    }
-
-    /* If this is a positional parameter, the position must follow immediately
-       after the %, thus create a %<num>$ sequence */
-    param = dprintf_DollarString(f, &f);
-
-    if(!param)
-      param = param_num;
-    else
-      --param;
-
-    param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
-                    third %s will pick the 3rd argument */
-
-    p = &vto[param];
-
     /* pick up the specified width */
-    if(p->flags & FLAGS_WIDTHPARAM) {
-      width = (long)vto[p->width].data.num.as_signed;
-      param_num++; /* since the width is extracted from a parameter, we
-                      must skip that to get to the next one properly */
+    if(flags & FLAGS_WIDTHPARAM) {
+      width = (int)input[optr->width].val.nums;
       if(width < 0) {
         /* "A negative field width is taken as a '-' flag followed by a
            positive field width." */
-        width = -width;
-        p->flags |= FLAGS_LEFT;
-        p->flags &= ~FLAGS_PAD_NIL;
+        if(width == INT_MIN)
+          width = INT_MAX;
+        else
+          width = -width;
+        flags |= FLAGS_LEFT;
+        flags &= ~FLAGS_PAD_NIL;
       }
     }
     else
-      width = p->width;
+      width = optr->width;
 
     /* pick up the specified precision */
-    if(p->flags & FLAGS_PRECPARAM) {
-      prec = (long)vto[p->precision].data.num.as_signed;
-      param_num++; /* since the precision is extracted from a parameter, we
-                      must skip that to get to the next one properly */
+    if(flags & FLAGS_PRECPARAM) {
+      prec = (int)input[optr->precision].val.nums;
       if(prec < 0)
         /* "A negative precision is taken as if the precision were
            omitted." */
         prec = -1;
     }
-    else if(p->flags & FLAGS_PREC)
-      prec = p->precision;
+    else if(flags & FLAGS_PREC)
+      prec = optr->precision;
     else
       prec = -1;
 
-    is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
+    is_alt = (flags & FLAGS_ALT) ? 1 : 0;
+    iptr = &input[optr->input];
 
-    switch(p->type) {
+    switch(iptr->type) {
+    case FORMAT_INTU:
+    case FORMAT_LONGU:
+    case FORMAT_LONGLONGU:
+      flags |= FLAGS_UNSIGNED;
+      FALLTHROUGH();
     case FORMAT_INT:
-      num = p->data.num.as_unsigned;
-      if(p->flags & FLAGS_CHAR) {
+    case FORMAT_LONG:
+    case FORMAT_LONGLONG:
+      num = iptr->val.numu;
+      if(flags & FLAGS_CHAR) {
         /* Character.  */
-        if(!(p->flags & FLAGS_LEFT))
+        if(!(flags & FLAGS_LEFT))
           while(--width > 0)
             OUTCHAR(' ');
         OUTCHAR((char) num);
-        if(p->flags & FLAGS_LEFT)
+        if(flags & FLAGS_LEFT)
           while(--width > 0)
             OUTCHAR(' ');
         break;
       }
-      if(p->flags & FLAGS_OCTAL) {
-        /* Octal unsigned integer.  */
+      if(flags & FLAGS_OCTAL) {
+        /* Octal unsigned integer */
         base = 8;
-        goto unsigned_number;
+        is_neg = FALSE;
       }
-      else if(p->flags & FLAGS_HEX) {
-        /* Hexadecimal unsigned integer.  */
-
-        digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
+      else if(flags & FLAGS_HEX) {
+        /* Hexadecimal unsigned integer */
+        digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits;
         base = 16;
-        goto unsigned_number;
+        is_neg = FALSE;
       }
-      else if(p->flags & FLAGS_UNSIGNED) {
-        /* Decimal unsigned integer.  */
+      else if(flags & FLAGS_UNSIGNED) {
+        /* Decimal unsigned integer */
         base = 10;
-        goto unsigned_number;
+        is_neg = FALSE;
       }
+      else {
+        /* Decimal integer.  */
+        base = 10;
 
-      /* Decimal integer.  */
-      base = 10;
-
-      is_neg = (p->data.num.as_signed < (mp_intmax_t)0) ? 1 : 0;
-      if(is_neg) {
-        /* signed_num might fail to hold absolute negative minimum by 1 */
-        signed_num = p->data.num.as_signed + (mp_intmax_t)1;
-        signed_num = -signed_num;
-        num = (mp_uintmax_t)signed_num;
-        num += (mp_uintmax_t)1;
+        is_neg = (iptr->val.nums < (mp_intmax_t)0);
+        if(is_neg) {
+          /* signed_num might fail to hold absolute negative minimum by 1 */
+          signed_num = iptr->val.nums + (mp_intmax_t)1;
+          signed_num = -signed_num;
+          num = (mp_uintmax_t)signed_num;
+          num += (mp_uintmax_t)1;
+        }
       }
-
-      goto number;
-
-unsigned_number:
-      /* Unsigned number of base BASE.  */
-      is_neg = 0;
-
 number:
-      /* Number of base BASE.  */
-
       /* Supply a default precision if none was given.  */
       if(prec == -1)
         prec = 1;
 
       /* Put the number in WORK.  */
       w = workend;
-      while(num > 0) {
-        *w-- = digits[num % base];
-        num /= base;
+      switch(base) {
+      case 10:
+        while(num > 0) {
+          *w-- = (char)('0' + (num % 10));
+          num /= 10;
+        }
+        break;
+      default:
+        while(num > 0) {
+          *w-- = digits[num % base];
+          num /= base;
+        }
+        break;
       }
-      width -= (long)(workend - w);
-      prec -= (long)(workend - w);
+      width -= (int)(workend - w);
+      prec -= (int)(workend - w);
 
       if(is_alt && base == 8 && prec <= 0) {
         *w-- = '0';
@@ -777,29 +829,29 @@
       if(is_alt && base == 16)
         width -= 2;
 
-      if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
+      if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
         --width;
 
-      if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
+      if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
         while(width-- > 0)
           OUTCHAR(' ');
 
       if(is_neg)
         OUTCHAR('-');
-      else if(p->flags & FLAGS_SHOWSIGN)
+      else if(flags & FLAGS_SHOWSIGN)
         OUTCHAR('+');
-      else if(p->flags & FLAGS_SPACE)
+      else if(flags & FLAGS_SPACE)
         OUTCHAR(' ');
 
       if(is_alt && base == 16) {
         OUTCHAR('0');
-        if(p->flags & FLAGS_UPPER)
+        if(flags & FLAGS_UPPER)
           OUTCHAR('X');
         else
           OUTCHAR('x');
       }
 
-      if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
+      if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
         while(width-- > 0)
           OUTCHAR('0');
 
@@ -808,219 +860,199 @@
         OUTCHAR(*w);
       }
 
-      if(p->flags & FLAGS_LEFT)
+      if(flags & FLAGS_LEFT)
         while(width-- > 0)
           OUTCHAR(' ');
       break;
 
-    case FORMAT_STRING:
-            /* String.  */
-      {
-        static const char null[] = "(nil)";
-        const char *str;
-        size_t len;
+    case FORMAT_STRING: {
+      const char *str;
+      size_t len;
 
-        str = (char *) p->data.str;
-        if(!str) {
-          /* Write null[] if there's space.  */
-          if(prec == -1 || prec >= (long) sizeof(null) - 1) {
-            str = null;
-            len = sizeof(null) - 1;
-            /* Disable quotes around (nil) */
-            p->flags &= (~FLAGS_ALT);
-          }
-          else {
-            str = "";
-            len = 0;
-          }
+      str = (char *)iptr->val.str;
+      if(!str) {
+        /* Write null string if there's space.  */
+        if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
+          str = nilstr;
+          len = sizeof(nilstr) - 1;
+          /* Disable quotes around (nil) */
+          flags &= (~FLAGS_ALT);
         }
-        else if(prec != -1)
-          len = (size_t)prec;
-        else if(*str == '\0')
+        else {
+          str = "";
           len = 0;
-        else
-          len = strlen(str);
-
-        width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
-
-        if(p->flags & FLAGS_ALT)
-          OUTCHAR('"');
-
-        if(!(p->flags&FLAGS_LEFT))
-          while(width-- > 0)
-            OUTCHAR(' ');
-
-        for(; len && *str; len--)
-          OUTCHAR(*str++);
-        if(p->flags&FLAGS_LEFT)
-          while(width-- > 0)
-            OUTCHAR(' ');
-
-        if(p->flags & FLAGS_ALT)
-          OUTCHAR('"');
+        }
       }
+      else if(prec != -1)
+        len = (size_t)prec;
+      else if(*str == '\0')
+        len = 0;
+      else
+        len = strlen(str);
+
+      width -= (len > INT_MAX) ? INT_MAX : (int)len;
+
+      if(flags & FLAGS_ALT)
+        OUTCHAR('"');
+
+      if(!(flags&FLAGS_LEFT))
+        while(width-- > 0)
+          OUTCHAR(' ');
+
+      for(; len && *str; len--)
+        OUTCHAR(*str++);
+      if(flags&FLAGS_LEFT)
+        while(width-- > 0)
+          OUTCHAR(' ');
+
+      if(flags & FLAGS_ALT)
+        OUTCHAR('"');
       break;
+    }
 
     case FORMAT_PTR:
       /* Generic pointer.  */
-      {
-        void *ptr;
-        ptr = (void *) p->data.ptr;
-        if(ptr) {
-          /* If the pointer is not NULL, write it as a %#x spec.  */
-          base = 16;
-          digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
-          is_alt = 1;
-          num = (size_t) ptr;
-          is_neg = 0;
-          goto number;
-        }
-        else {
-          /* Write "(nil)" for a nil pointer.  */
-          static const char strnil[] = "(nil)";
-          const char *point;
+      if(iptr->val.ptr) {
+        /* If the pointer is not NULL, write it as a %#x spec.  */
+        base = 16;
+        digits = (flags & FLAGS_UPPER)? upper_digits : lower_digits;
+        is_alt = TRUE;
+        num = (size_t) iptr->val.ptr;
+        is_neg = FALSE;
+        goto number;
+      }
+      else {
+        /* Write "(nil)" for a nil pointer.  */
+        const char *point;
 
-          width -= (long)(sizeof(strnil) - 1);
-          if(p->flags & FLAGS_LEFT)
-            while(width-- > 0)
-              OUTCHAR(' ');
-          for(point = strnil; *point != '\0'; ++point)
-            OUTCHAR(*point);
-          if(!(p->flags & FLAGS_LEFT))
-            while(width-- > 0)
-              OUTCHAR(' ');
-        }
+        width -= (int)(sizeof(nilstr) - 1);
+        if(flags & FLAGS_LEFT)
+          while(width-- > 0)
+            OUTCHAR(' ');
+        for(point = nilstr; *point != '\0'; ++point)
+          OUTCHAR(*point);
+        if(!(flags & FLAGS_LEFT))
+          while(width-- > 0)
+            OUTCHAR(' ');
       }
       break;
 
-    case FORMAT_DOUBLE:
-      {
-        char formatbuf[32]="%";
-        char *fptr = &formatbuf[1];
-        size_t left = sizeof(formatbuf)-strlen(formatbuf);
-        int len;
+    case FORMAT_DOUBLE: {
+      char formatbuf[32]="%";
+      char *fptr = &formatbuf[1];
+      size_t left = sizeof(formatbuf)-strlen(formatbuf);
+      int len;
 
-        width = -1;
-        if(p->flags & FLAGS_WIDTH)
-          width = p->width;
-        else if(p->flags & FLAGS_WIDTHPARAM)
-          width = (long)vto[p->width].data.num.as_signed;
+      if(flags & FLAGS_WIDTH)
+        width = optr->width;
 
-        prec = -1;
-        if(p->flags & FLAGS_PREC)
-          prec = p->precision;
-        else if(p->flags & FLAGS_PRECPARAM)
-          prec = (long)vto[p->precision].data.num.as_signed;
+      if(flags & FLAGS_PREC)
+        prec = optr->precision;
 
-        if(p->flags & FLAGS_LEFT)
-          *fptr++ = '-';
-        if(p->flags & FLAGS_SHOWSIGN)
-          *fptr++ = '+';
-        if(p->flags & FLAGS_SPACE)
-          *fptr++ = ' ';
-        if(p->flags & FLAGS_ALT)
-          *fptr++ = '#';
+      if(flags & FLAGS_LEFT)
+        *fptr++ = '-';
+      if(flags & FLAGS_SHOWSIGN)
+        *fptr++ = '+';
+      if(flags & FLAGS_SPACE)
+        *fptr++ = ' ';
+      if(flags & FLAGS_ALT)
+        *fptr++ = '#';
 
-        *fptr = 0;
+      *fptr = 0;
 
-        if(width >= 0) {
-          if(width >= (long)sizeof(work))
-            width = sizeof(work)-1;
-          /* RECURSIVE USAGE */
-          len = curl_msnprintf(fptr, left, "%ld", width);
-          fptr += len;
-          left -= len;
+      if(width >= 0) {
+        if(width >= (int)sizeof(work))
+          width = sizeof(work)-1;
+        /* RECURSIVE USAGE */
+        len = curl_msnprintf(fptr, left, "%d", width);
+        fptr += len;
+        left -= len;
+      }
+      if(prec >= 0) {
+        /* for each digit in the integer part, we can have one less
+           precision */
+        size_t maxprec = sizeof(work) - 2;
+        double val = iptr->val.dnum;
+        if(width > 0 && prec <= width)
+          maxprec -= width;
+        while(val >= 10.0) {
+          val /= 10;
+          maxprec--;
         }
-        if(prec >= 0) {
-          /* for each digit in the integer part, we can have one less
-             precision */
-          size_t maxprec = sizeof(work) - 2;
-          double val = p->data.dnum;
-          if(width > 0 && prec <= width)
-            maxprec -= width;
-          while(val >= 10.0) {
-            val /= 10;
-            maxprec--;
-          }
 
-          if(prec > (long)maxprec)
-            prec = (long)maxprec-1;
-          if(prec < 0)
-            prec = 0;
-          /* RECURSIVE USAGE */
-          len = curl_msnprintf(fptr, left, ".%ld", prec);
-          fptr += len;
-        }
-        if(p->flags & FLAGS_LONG)
-          *fptr++ = 'l';
+        if(prec > (int)maxprec)
+          prec = (int)maxprec-1;
+        if(prec < 0)
+          prec = 0;
+        /* RECURSIVE USAGE */
+        len = curl_msnprintf(fptr, left, ".%d", prec);
+        fptr += len;
+      }
+      if(flags & FLAGS_LONG)
+        *fptr++ = 'l';
 
-        if(p->flags & FLAGS_FLOATE)
-          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'E':'e');
-        else if(p->flags & FLAGS_FLOATG)
-          *fptr++ = (char)((p->flags & FLAGS_UPPER) ? 'G' : 'g');
-        else
-          *fptr++ = 'f';
+      if(flags & FLAGS_FLOATE)
+        *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E':'e');
+      else if(flags & FLAGS_FLOATG)
+        *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
+      else
+        *fptr++ = 'f';
 
-        *fptr = 0; /* and a final null-termination */
+      *fptr = 0; /* and a final null-termination */
 
 #ifdef __clang__
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wformat-nonliteral"
 #endif
-        /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
-           output characters */
+      /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
+         output characters */
 #ifdef HAVE_SNPRINTF
-        (snprintf)(work, sizeof(work), formatbuf, p->data.dnum);
+      (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum);
 #else
-        (sprintf)(work, formatbuf, p->data.dnum);
+      (sprintf)(work, formatbuf, iptr->val.dnum);
 #endif
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
-        DEBUGASSERT(strlen(work) <= sizeof(work));
-        for(fptr = work; *fptr; fptr++)
-          OUTCHAR(*fptr);
-      }
+      DEBUGASSERT(strlen(work) <= sizeof(work));
+      for(fptr = work; *fptr; fptr++)
+        OUTCHAR(*fptr);
       break;
+    }
 
     case FORMAT_INTPTR:
       /* Answer the count of characters written.  */
 #ifdef HAVE_LONG_LONG_TYPE
-      if(p->flags & FLAGS_LONGLONG)
-        *(LONG_LONG_TYPE *) p->data.ptr = (LONG_LONG_TYPE)done;
+      if(flags & FLAGS_LONGLONG)
+        *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done;
       else
 #endif
-        if(p->flags & FLAGS_LONG)
-          *(long *) p->data.ptr = (long)done;
-      else if(!(p->flags & FLAGS_SHORT))
-        *(int *) p->data.ptr = (int)done;
+        if(flags & FLAGS_LONG)
+          *(long *) iptr->val.ptr = (long)done;
+      else if(!(flags & FLAGS_SHORT))
+        *(int *) iptr->val.ptr = (int)done;
       else
-        *(short *) p->data.ptr = (short)done;
+        *(short *) iptr->val.ptr = (short)done;
       break;
 
     default:
       break;
     }
-    f = *end++; /* goto end of %-code */
-
   }
   return done;
 }
 
 /* fputc() look-alike */
-static int addbyter(int output, FILE *data)
+static int addbyter(unsigned char outc, void *f)
 {
-  struct nsprintf *infop = (struct nsprintf *)data;
-  unsigned char outc = (unsigned char)output;
-
+  struct nsprintf *infop = f;
   if(infop->length < infop->max) {
     /* only do this if we haven't reached max length yet */
-    infop->buffer[0] = outc; /* store */
-    infop->buffer++; /* increase pointer */
+    *infop->buffer++ = outc; /* store */
     infop->length++; /* we are now one byte larger */
-    return outc;     /* fputc() returns like this on success */
+    return 0;     /* fputc() returns like this on success */
   }
-  return -1;
+  return 1;
 }
 
 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
@@ -1033,7 +1065,7 @@
   info.length = 0;
   info.max = maxlength;
 
-  retcode = dprintf_formatf(&info, addbyter, format, ap_save);
+  retcode = formatf(&info, addbyter, format, ap_save);
   if(info.max) {
     /* we terminate this with a zero byte */
     if(info.max == info.length) {
@@ -1059,32 +1091,28 @@
 }
 
 /* fputc() look-alike */
-static int alloc_addbyter(int output, FILE *data)
+static int alloc_addbyter(unsigned char outc, void *f)
 {
-  struct asprintf *infop = (struct asprintf *)data;
-  unsigned char outc = (unsigned char)output;
-
-  if(Curl_dyn_addn(infop->b, &outc, 1)) {
-    infop->fail = 1;
-    return -1; /* fail */
+  struct asprintf *infop = f;
+  CURLcode result = Curl_dyn_addn(infop->b, &outc, 1);
+  if(result) {
+    infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
+    return 1 ; /* fail */
   }
-  return outc; /* fputc() returns like this on success */
+  return 0;
 }
 
-extern int Curl_dyn_vprintf(struct dynbuf *dyn,
-                            const char *format, va_list ap_save);
-
-/* appends the formatted string, returns 0 on success, 1 on error */
+/* appends the formatted string, returns MERR error code */
 int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 {
   struct asprintf info;
   info.b = dyn;
-  info.fail = 0;
+  info.merr = MERR_OK;
 
-  (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  if(info.fail) {
+  (void)formatf(&info, alloc_addbyter, format, ap_save);
+  if(info.merr) {
     Curl_dyn_free(info.b);
-    return 1;
+    return info.merr;
   }
   return 0;
 }
@@ -1095,10 +1123,10 @@
   struct dynbuf dyn;
   info.b = &dyn;
   Curl_dyn_init(info.b, DYN_APRINTF);
-  info.fail = 0;
+  info.merr = MERR_OK;
 
-  (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  if(info.fail) {
+  (void)formatf(&info, alloc_addbyter, format, ap_save);
+  if(info.merr) {
     Curl_dyn_free(info.b);
     return NULL;
   }
@@ -1117,13 +1145,12 @@
   return s;
 }
 
-static int storebuffer(int output, FILE *data)
+static int storebuffer(unsigned char outc, void *f)
 {
-  char **buffer = (char **)data;
-  unsigned char outc = (unsigned char)output;
+  char **buffer = f;
   **buffer = outc;
   (*buffer)++;
-  return outc; /* act like fputc() ! */
+  return 0;
 }
 
 int curl_msprintf(char *buffer, const char *format, ...)
@@ -1131,19 +1158,29 @@
   va_list ap_save; /* argument pointer */
   int retcode;
   va_start(ap_save, format);
-  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+  retcode = formatf(&buffer, storebuffer, format, ap_save);
   va_end(ap_save);
   *buffer = 0; /* we terminate this with a zero byte */
   return retcode;
 }
 
+static int fputc_wrapper(unsigned char outc, void *f)
+{
+  int out = outc;
+  FILE *s = f;
+  int rc = fputc(out, s);
+  if(rc == out)
+    return 0;
+  return 1;
+}
+
 int curl_mprintf(const char *format, ...)
 {
   int retcode;
   va_list ap_save; /* argument pointer */
   va_start(ap_save, format);
 
-  retcode = dprintf_formatf(stdout, fputc, format, ap_save);
+  retcode = formatf(stdout, fputc_wrapper, format, ap_save);
   va_end(ap_save);
   return retcode;
 }
@@ -1153,25 +1190,24 @@
   int retcode;
   va_list ap_save; /* argument pointer */
   va_start(ap_save, format);
-  retcode = dprintf_formatf(whereto, fputc, format, ap_save);
+  retcode = formatf(whereto, fputc_wrapper, format, ap_save);
   va_end(ap_save);
   return retcode;
 }
 
 int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
 {
-  int retcode;
-  retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
+  int retcode = formatf(&buffer, storebuffer, format, ap_save);
   *buffer = 0; /* we terminate this with a zero byte */
   return retcode;
 }
 
 int curl_mvprintf(const char *format, va_list ap_save)
 {
-  return dprintf_formatf(stdout, fputc, format, ap_save);
+  return formatf(stdout, fputc_wrapper, format, ap_save);
 }
 
 int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
 {
-  return dprintf_formatf(whereto, fputc, format, ap_save);
+  return formatf(whereto, fputc_wrapper, format, ap_save);
 }
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index 54f8882..5a9d6d0 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -88,7 +88,7 @@
   ZERO_NULL,                          /* domore_getsock */
   ZERO_NULL,                          /* perform_getsock */
   ZERO_NULL,                          /* disconnect */
-  ZERO_NULL,                          /* readwrite */
+  ZERO_NULL,                          /* write_resp */
   ZERO_NULL,                          /* connection_check */
   ZERO_NULL,                          /* attach connection */
   PORT_MQTT,                          /* defport */
@@ -524,8 +524,10 @@
   char encodedbytes[4];
   curl_off_t postfieldsize = data->set.postfieldsize;
 
-  if(!payload)
+  if(!payload) {
+    DEBUGF(infof(data, "mqtt_publish without payload, return bad arg"));
     return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
   if(postfieldsize < 0)
     payloadlen = strlen(payload);
   else
@@ -616,16 +618,12 @@
 }
 
 
-/* for the publish packet */
-#define MQTT_HEADER_LEN 5    /* max 5 bytes */
-
 static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   ssize_t nread;
-  unsigned char *pkt = (unsigned char *)data->state.buffer;
   size_t remlen;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
   struct MQTT *mq = data->req.p.mqtt;
@@ -674,14 +672,14 @@
     data->req.bytecount = 0;
     data->req.size = remlen;
     mq->npacket = remlen; /* get this many bytes */
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case MQTT_PUB_REMAIN: {
     /* read rest of packet, but no more. Cap to buffer size */
-    struct SingleRequest *k = &data->req;
+    char buffer[4*1024];
     size_t rest = mq->npacket;
-    if(rest > (size_t)data->set.buffer_size)
-      rest = (size_t)data->set.buffer_size;
-    result = Curl_read(data, sockfd, (char *)pkt, rest, &nread);
+    if(rest > sizeof(buffer))
+      rest = sizeof(buffer);
+    result = Curl_read(data, sockfd, buffer, rest, &nread);
     if(result) {
       if(CURLE_AGAIN == result) {
         infof(data, "EEEE AAAAGAIN");
@@ -693,20 +691,13 @@
       result = CURLE_PARTIAL_FILE;
       goto end;
     }
-    Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread);
-
-    mq->npacket -= nread;
-    k->bytecount += nread;
-    result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
-    if(result)
-      goto end;
 
     /* if QoS is set, message contains packet id */
-
-    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)pkt, nread);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
     if(result)
       goto end;
 
+    mq->npacket -= nread;
     if(!mq->npacket)
       /* no more PUBLISH payload, back to subscribe wait state */
       mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
@@ -754,7 +745,6 @@
   struct MQTT *mq = data->req.p.mqtt;
   ssize_t nread;
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-  unsigned char *pkt = (unsigned char *)data->state.buffer;
   unsigned char byte;
 
   *done = FALSE;
@@ -785,14 +775,14 @@
     /* remember the first byte */
     mq->npacket = 0;
     mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case MQTT_REMAINING_LENGTH:
     do {
       result = Curl_read(data, sockfd, (char *)&byte, 1, &nread);
       if(!nread)
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
-      pkt[mq->npacket++] = byte;
+      mq->pkt_hd[mq->npacket++] = byte;
     } while((byte & 0x80) && (mq->npacket < 4));
     if(nread && (byte & 0x80))
       /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
@@ -800,7 +790,7 @@
       result = CURLE_WEIRD_SERVER_REPLY;
     if(result)
       break;
-    mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL);
+    mq->remaining_length = mqtt_decode_len(mq->pkt_hd, mq->npacket, NULL);
     mq->npacket = 0;
     if(mq->remaining_length) {
       mqstate(data, mqtt->nextstate, MQTT_NOSTATE);
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h
index 84f1770..99ab12a 100644
--- a/Utilities/cmcurl/lib/mqtt.h
+++ b/Utilities/cmcurl/lib/mqtt.h
@@ -57,6 +57,7 @@
   unsigned char firstbyte;
   size_t remaining_length;
   struct dynbuf recvbuf;
+  unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
 };
 
 #endif /* HEADER_CURL_MQTT_H */
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index ff753ac..0926b0d 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -55,22 +55,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef __APPLE__
-
-#define wakeup_write  write
-#define wakeup_read   read
-#define wakeup_close  close
-#define wakeup_create pipe
-
-#else /* __APPLE__ */
-
-#define wakeup_write     swrite
-#define wakeup_read      sread
-#define wakeup_close     sclose
-#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
-
-#endif /* __APPLE__ */
-
 /*
   CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
   to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes.  Still, every
@@ -231,10 +215,6 @@
   unsigned int readers; /* this many transfers want to read */
   unsigned int writers; /* this many transfers want to write */
 };
-/* bits for 'action' having no bits means this socket is not expecting any
-   action */
-#define SH_READ  1
-#define SH_WRITE 2
 
 /* look up a given socket in the socket hash, skip invalid sockets */
 static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh,
@@ -416,9 +396,6 @@
   Curl_llist_init(&multi->msgsent, NULL);
 
   multi->multiplexing = TRUE;
-
-  /* -1 means it not set by user, use the default value */
-  multi->maxconnects = -1;
   multi->max_concurrent_streams = 100;
 
 #ifdef USE_WINSOCK
@@ -695,6 +672,7 @@
        many callbacks and protocols work differently, we could potentially do
        this more fine-grained in the future. */
     premature = TRUE;
+    FALLTHROUGH();
   default:
     break;
   }
@@ -1016,73 +994,162 @@
   Curl_conn_ev_data_attach(conn, data);
 }
 
-static int domore_getsock(struct Curl_easy *data,
-                          struct connectdata *conn,
-                          curl_socket_t *socks)
-{
-  if(conn && conn->handler->domore_getsock)
-    return conn->handler->domore_getsock(data, conn, socks);
-  return GETSOCK_BLANK;
-}
-
-static int doing_getsock(struct Curl_easy *data,
-                         struct connectdata *conn,
-                         curl_socket_t *socks)
-{
-  if(conn && conn->handler->doing_getsock)
-    return conn->handler->doing_getsock(data, conn, socks);
-  return GETSOCK_BLANK;
-}
-
-static int protocol_getsock(struct Curl_easy *data,
-                            struct connectdata *conn,
-                            curl_socket_t *socks)
-{
-  if(conn->handler->proto_getsock)
-    return conn->handler->proto_getsock(data, conn, socks);
-  return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
-}
-
-/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
-   array contains MAX_SOCKSPEREASYHANDLE entries. */
-static int multi_getsock(struct Curl_easy *data,
-                         curl_socket_t *socks)
+static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
 {
   struct connectdata *conn = data->conn;
+  (void)socks;
+  /* Not using `conn->sockfd` as `Curl_setup_transfer()` initializes
+   * that *after* the connect. */
+  if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) {
+    /* Default is to wait to something from the server */
+    socks[0] = conn->sock[FIRSTSOCKET];
+    return GETSOCK_READSOCK(0);
+  }
+  return GETSOCK_BLANK;
+}
+
+static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+  struct connectdata *conn = data->conn;
+  if(conn && conn->handler->proto_getsock)
+    return conn->handler->proto_getsock(data, conn, socks);
+  else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+    /* Default is to wait to something from the server */
+    socks[0] = conn->sockfd;
+    return GETSOCK_READSOCK(0);
+  }
+  return GETSOCK_BLANK;
+}
+
+static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+  struct connectdata *conn = data->conn;
+  if(conn && conn->handler->domore_getsock)
+    return conn->handler->domore_getsock(data, conn, socks);
+  else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+    /* Default is that we want to send something to the server */
+    socks[0] = conn->sockfd;
+    return GETSOCK_WRITESOCK(0);
+  }
+  return GETSOCK_BLANK;
+}
+
+static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+  struct connectdata *conn = data->conn;
+  if(conn && conn->handler->doing_getsock)
+    return conn->handler->doing_getsock(data, conn, socks);
+  else if(conn && conn->sockfd != CURL_SOCKET_BAD) {
+    /* Default is that we want to send something to the server */
+    socks[0] = conn->sockfd;
+    return GETSOCK_WRITESOCK(0);
+  }
+  return GETSOCK_BLANK;
+}
+
+static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
+{
+  struct connectdata *conn = data->conn;
+
+  if(!conn)
+    return GETSOCK_BLANK;
+  else if(conn->handler->perform_getsock)
+    return conn->handler->perform_getsock(data, conn, sock);
+  else {
+    /* Default is to obey the data->req.keepon flags for send/recv */
+    int bitmap = GETSOCK_BLANK;
+    unsigned sockindex = 0;
+    if(CURL_WANT_RECV(data)) {
+      DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
+      bitmap |= GETSOCK_READSOCK(sockindex);
+      sock[sockindex] = conn->sockfd;
+    }
+
+    if(CURL_WANT_SEND(data)) {
+      if((conn->sockfd != conn->writesockfd) ||
+         bitmap == GETSOCK_BLANK) {
+        /* only if they are not the same socket and we have a readable
+           one, we increase index */
+        if(bitmap != GETSOCK_BLANK)
+          sockindex++; /* increase index if we need two entries */
+
+        DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
+        sock[sockindex] = conn->writesockfd;
+      }
+      bitmap |= GETSOCK_WRITESOCK(sockindex);
+    }
+    return bitmap;
+  }
+}
+
+/* Initializes `poll_set` with the current socket poll actions needed
+ * for transfer `data`. */
+static void multi_getsock(struct Curl_easy *data,
+                          struct easy_pollset *ps)
+{
   /* The no connection case can happen when this is called from
      curl_multi_remove_handle() => singlesocket() => multi_getsock().
   */
-  if(!conn)
-    return 0;
+  Curl_pollset_reset(data, ps);
+  if(!data->conn)
+    return;
 
   switch(data->mstate) {
-  default:
-    return 0;
+  case MSTATE_INIT:
+  case MSTATE_PENDING:
+  case MSTATE_CONNECT:
+    /* nothing to poll for yet */
+    break;
 
   case MSTATE_RESOLVING:
-    return Curl_resolv_getsock(data, socks);
+    Curl_pollset_add_socks(data, ps, Curl_resolv_getsock);
+    /* connection filters are not involved in this phase */
+    break;
 
-  case MSTATE_PROTOCONNECTING:
+  case MSTATE_CONNECTING:
+  case MSTATE_TUNNELING:
+    Curl_pollset_add_socks(data, ps, connecting_getsock);
+    Curl_conn_adjust_pollset(data, ps);
+    break;
+
   case MSTATE_PROTOCONNECT:
-    return protocol_getsock(data, conn, socks);
+  case MSTATE_PROTOCONNECTING:
+    Curl_pollset_add_socks(data, ps, protocol_getsock);
+    Curl_conn_adjust_pollset(data, ps);
+    break;
 
   case MSTATE_DO:
   case MSTATE_DOING:
-    return doing_getsock(data, conn, socks);
-
-  case MSTATE_TUNNELING:
-  case MSTATE_CONNECTING:
-    return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
+    Curl_pollset_add_socks(data, ps, doing_getsock);
+    Curl_conn_adjust_pollset(data, ps);
+    break;
 
   case MSTATE_DOING_MORE:
-    return domore_getsock(data, conn, socks);
+    Curl_pollset_add_socks(data, ps, domore_getsock);
+    Curl_conn_adjust_pollset(data, ps);
+    break;
 
-  case MSTATE_DID: /* since is set after DO is completed, we switch to
-                        waiting for the same as the PERFORMING state */
+  case MSTATE_DID: /* same as PERFORMING in regard to polling */
   case MSTATE_PERFORMING:
-    return Curl_single_getsock(data, conn, socks);
-  }
+    Curl_pollset_add_socks(data, ps, perform_getsock);
+    Curl_conn_adjust_pollset(data, ps);
+    break;
 
+  case MSTATE_RATELIMITING:
+    /* we need to let time pass, ignore socket(s) */
+    break;
+
+  case MSTATE_DONE:
+  case MSTATE_COMPLETED:
+  case MSTATE_MSGSENT:
+    /* nothing more to poll for */
+    break;
+
+  default:
+    failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
+    DEBUGASSERT(0);
+    break;
+  }
 }
 
 CURLMcode curl_multi_fdset(struct Curl_multi *multi,
@@ -1094,8 +1161,8 @@
      and then we must make sure that is done. */
   struct Curl_easy *data;
   int this_max_fd = -1;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int i;
+  struct easy_pollset ps;
+  unsigned int i;
   (void)exc_fd_set; /* not used */
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -1104,29 +1171,20 @@
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
+  memset(&ps, 0, sizeof(ps));
   for(data = multi->easyp; data; data = data->next) {
-    int bitmap;
-#ifdef __clang_analyzer_
-    /* to prevent "The left operand of '>=' is a garbage value" warnings */
-    memset(sockbunch, 0, sizeof(sockbunch));
-#endif
-    bitmap = multi_getsock(data, sockbunch);
+    multi_getsock(data, &ps);
 
-    for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-        if(!FDSET_SOCK(sockbunch[i]))
-          /* pretend it doesn't exist */
-          continue;
-        if(bitmap & GETSOCK_READSOCK(i))
-          FD_SET(sockbunch[i], read_fd_set);
-        if(bitmap & GETSOCK_WRITESOCK(i))
-          FD_SET(sockbunch[i], write_fd_set);
-        if((int)sockbunch[i] > this_max_fd)
-          this_max_fd = (int)sockbunch[i];
-      }
-      else {
-        break;
-      }
+    for(i = 0; i < ps.num; i++) {
+      if(!FDSET_SOCK(ps.sockets[i]))
+        /* pretend it doesn't exist */
+        continue;
+      if(ps.actions[i] & CURL_POLL_IN)
+        FD_SET(ps.sockets[i], read_fd_set);
+      if(ps.actions[i] & CURL_POLL_OUT)
+        FD_SET(ps.sockets[i], write_fd_set);
+      if((int)ps.sockets[i] > this_max_fd)
+        this_max_fd = (int)ps.sockets[i];
     }
   }
 
@@ -1162,9 +1220,8 @@
                             bool use_wakeup)
 {
   struct Curl_easy *data;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int bitmap;
-  unsigned int i;
+  struct easy_pollset ps;
+  size_t i;
   unsigned int nfds = 0;
   unsigned int curlfds;
   long timeout_internal;
@@ -1190,17 +1247,10 @@
     return CURLM_BAD_FUNCTION_ARGUMENT;
 
   /* Count up how many fds we have from the multi handle */
+  memset(&ps, 0, sizeof(ps));
   for(data = multi->easyp; data; data = data->next) {
-    bitmap = multi_getsock(data, sockbunch);
-
-    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-        ++nfds;
-      }
-      else {
-        break;
-      }
-    }
+    multi_getsock(data, &ps);
+    nfds += ps.num;
   }
 
   /* If the internally desired timeout is actually shorter than requested from
@@ -1241,40 +1291,35 @@
   if(curlfds) {
     /* Add the curl handles to our pollfds first */
     for(data = multi->easyp; data; data = data->next) {
-      bitmap = multi_getsock(data, sockbunch);
+      multi_getsock(data, &ps);
 
-      for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-        if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-          struct pollfd *ufd = &ufds[nfds++];
+      for(i = 0; i < ps.num; i++) {
+        struct pollfd *ufd = &ufds[nfds++];
 #ifdef USE_WINSOCK
-          long mask = 0;
+        long mask = 0;
 #endif
-          ufd->fd = sockbunch[i];
-          ufd->events = 0;
-          if(bitmap & GETSOCK_READSOCK(i)) {
+        ufd->fd = ps.sockets[i];
+        ufd->events = 0;
+        if(ps.actions[i] & CURL_POLL_IN) {
 #ifdef USE_WINSOCK
-            mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+          mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
 #endif
-            ufd->events |= POLLIN;
-          }
-          if(bitmap & GETSOCK_WRITESOCK(i)) {
-#ifdef USE_WINSOCK
-            mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
-            reset_socket_fdwrite(sockbunch[i]);
-#endif
-            ufd->events |= POLLOUT;
-          }
-#ifdef USE_WINSOCK
-          if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) {
-            if(ufds_malloc)
-              free(ufds);
-            return CURLM_INTERNAL_ERROR;
-          }
-#endif
+          ufd->events |= POLLIN;
         }
-        else {
-          break;
+        if(ps.actions[i] & CURL_POLL_OUT) {
+#ifdef USE_WINSOCK
+          mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+          reset_socket_fdwrite(ps.sockets[i]);
+#endif
+          ufd->events |= POLLOUT;
         }
+#ifdef USE_WINSOCK
+        if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
+          if(ufds_malloc)
+            free(ufds);
+          return CURLM_INTERNAL_ERROR;
+        }
+#endif
       }
     }
   }
@@ -1386,21 +1431,16 @@
       if(curlfds) {
 
         for(data = multi->easyp; data; data = data->next) {
-          bitmap = multi_getsock(data, sockbunch);
+          multi_getsock(data, &ps);
 
-          for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-            if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
-              wsa_events.lNetworkEvents = 0;
-              if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) {
-                if(ret && !pollrc && wsa_events.lNetworkEvents)
-                  retcode++;
-              }
-              WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
+          for(i = 0; i < ps.num; i++) {
+            wsa_events.lNetworkEvents = 0;
+            if(WSAEnumNetworkEvents(ps.sockets[i], NULL,
+                                    &wsa_events) == 0) {
+              if(ret && !pollrc && wsa_events.lNetworkEvents)
+                retcode++;
             }
-            else {
-              /* break on entry not checked for being readable or writable */
-              break;
-            }
+            WSAEventSelect(ps.sockets[i], multi->wsa_event, 0);
           }
         }
       }
@@ -1980,6 +2020,7 @@
       }
 
       if(!result) {
+        *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE);
         if(async)
           /* We're now waiting for an asynchronous name lookup */
           multistate(data, MSTATE_RESOLVING);
@@ -2409,7 +2450,6 @@
     {
       char *newurl = NULL;
       bool retry = FALSE;
-      bool comeback = FALSE;
       DEBUGASSERT(data->state.buffer);
       /* check if over send speed */
       send_timeout_ms = 0;
@@ -2440,7 +2480,7 @@
       }
 
       /* read/write data if it is ready to do so */
-      result = Curl_readwrite(data->conn, data, &done, &comeback);
+      result = Curl_readwrite(data, &done);
 
       if(done || (result == CURLE_RECV_ERROR)) {
         /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
@@ -2550,7 +2590,7 @@
           }
         }
       }
-      else if(comeback) {
+      else if(data->state.select_bits) {
         /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer
            won't get stuck on this transfer at the expense of other concurrent
            transfers */
@@ -2895,53 +2935,36 @@
 static CURLMcode singlesocket(struct Curl_multi *multi,
                               struct Curl_easy *data)
 {
-  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
-  int i;
+  struct easy_pollset cur_poll;
+  unsigned int i;
   struct Curl_sh_entry *entry;
   curl_socket_t s;
-  int num;
-  unsigned int curraction;
-  unsigned char actions[MAX_SOCKSPEREASYHANDLE];
   int rc;
 
-  for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
-    socks[i] = CURL_SOCKET_BAD;
-
   /* Fill in the 'current' struct with the state as it is now: what sockets to
      supervise and for what actions */
-  curraction = multi_getsock(data, socks);
+  multi_getsock(data, &cur_poll);
 
   /* We have 0 .. N sockets already and we get to know about the 0 .. M
      sockets we should have from now on. Detect the differences, remove no
      longer supervised ones and add new ones */
 
   /* walk over the sockets we got right now */
-  for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
-      (curraction & GETSOCK_MASK_RW(i));
-      i++) {
-    unsigned char action = CURL_POLL_NONE;
-    unsigned char prevaction = 0;
+  for(i = 0; i < cur_poll.num; i++) {
+    unsigned char cur_action = cur_poll.actions[i];
+    unsigned char last_action = 0;
     int comboaction;
-    bool sincebefore = FALSE;
 
-    s = socks[i];
+    s = cur_poll.sockets[i];
 
     /* get it from the hash */
     entry = sh_getentry(&multi->sockhash, s);
-
-    if(curraction & GETSOCK_READSOCK(i))
-      action |= CURL_POLL_IN;
-    if(curraction & GETSOCK_WRITESOCK(i))
-      action |= CURL_POLL_OUT;
-
-    actions[i] = action;
     if(entry) {
       /* check if new for this transfer */
-      int j;
-      for(j = 0; j< data->numsocks; j++) {
-        if(s == data->sockets[j]) {
-          prevaction = data->actions[j];
-          sincebefore = TRUE;
+      unsigned int j;
+      for(j = 0; j< data->last_poll.num; j++) {
+        if(s == data->last_poll.sockets[j]) {
+          last_action = data->last_poll.actions[j];
           break;
         }
       }
@@ -2953,23 +2976,23 @@
         /* fatal */
         return CURLM_OUT_OF_MEMORY;
     }
-    if(sincebefore && (prevaction != action)) {
+    if(last_action && (last_action != cur_action)) {
       /* Socket was used already, but different action now */
-      if(prevaction & CURL_POLL_IN)
+      if(last_action & CURL_POLL_IN)
         entry->readers--;
-      if(prevaction & CURL_POLL_OUT)
+      if(last_action & CURL_POLL_OUT)
         entry->writers--;
-      if(action & CURL_POLL_IN)
+      if(cur_action & CURL_POLL_IN)
         entry->readers++;
-      if(action & CURL_POLL_OUT)
+      if(cur_action & CURL_POLL_OUT)
         entry->writers++;
     }
-    else if(!sincebefore) {
-      /* a new user */
+    else if(!last_action) {
+      /* a new transfer using this socket */
       entry->users++;
-      if(action & CURL_POLL_IN)
+      if(cur_action & CURL_POLL_IN)
         entry->readers++;
-      if(action & CURL_POLL_OUT)
+      if(cur_action & CURL_POLL_OUT)
         entry->writers++;
 
       /* add 'data' to the transfer hash on this socket! */
@@ -2980,11 +3003,11 @@
       }
     }
 
-    comboaction = (entry->writers? CURL_POLL_OUT : 0) |
+    comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
                    (entry->readers ? CURL_POLL_IN : 0);
 
     /* socket existed before and has the same action set as before */
-    if(sincebefore && ((int)entry->action == comboaction))
+    if(last_action && ((int)entry->action == comboaction))
       /* same, continue */
       continue;
 
@@ -2992,6 +3015,7 @@
       set_in_callback(multi, TRUE);
       rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
                             entry->socketp);
+
       set_in_callback(multi, FALSE);
       if(rc == -1) {
         multi->dead = TRUE;
@@ -3002,16 +3026,15 @@
     entry->action = comboaction; /* store the current action state */
   }
 
-  num = i; /* number of sockets */
-
-  /* when we've walked over all the sockets we should have right now, we must
-     make sure to detect sockets that are removed */
-  for(i = 0; i< data->numsocks; i++) {
-    int j;
+  /* Check for last_poll.sockets that no longer appear in cur_poll.sockets.
+   * Need to remove the easy handle from the multi->sockhash->transfers and
+   * remove multi->sockhash entry when this was the last transfer */
+  for(i = 0; i< data->last_poll.num; i++) {
+    unsigned int j;
     bool stillused = FALSE;
-    s = data->sockets[i];
-    for(j = 0; j < num; j++) {
-      if(s == socks[j]) {
+    s = data->last_poll.sockets[i];
+    for(j = 0; j < cur_poll.num; j++) {
+      if(s == cur_poll.sockets[j]) {
         /* this is still supervised */
         stillused = TRUE;
         break;
@@ -3024,7 +3047,7 @@
     /* if this is NULL here, the socket has been closed and notified so
        already by Curl_multi_closed() */
     if(entry) {
-      unsigned char oldactions = data->actions[i];
+      unsigned char oldactions = data->last_poll.actions[i];
       /* this socket has been removed. Decrease user count */
       entry->users--;
       if(oldactions & CURL_POLL_OUT)
@@ -3052,11 +3075,10 @@
         }
       }
     }
-  } /* for loop over numsocks */
+  } /* for loop over num */
 
-  memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
-  memcpy(data->actions, actions, num*sizeof(char));
-  data->numsocks = num;
+  /* Remember for next time */
+  memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll));
   return CURLM_OK;
 }
 
@@ -3220,7 +3242,7 @@
 
         if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
           /* set socket event bitmask if they're not locked */
-          data->conn->cselect_bits = (unsigned char)ev_bitmask;
+          data->state.select_bits = (unsigned char)ev_bitmask;
 
         Curl_expire(data, 0, EXPIRE_RUN_NOW);
       }
@@ -3296,6 +3318,7 @@
 {
   CURLMcode res = CURLM_OK;
   va_list param;
+  unsigned long uarg;
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -3328,7 +3351,9 @@
     multi->timer_userp = va_arg(param, void *);
     break;
   case CURLMOPT_MAXCONNECTS:
-    multi->maxconnects = va_arg(param, long);
+    uarg = va_arg(param, unsigned long);
+    if(uarg <= UINT_MAX)
+      multi->maxconnects = (unsigned int)uarg;
     break;
   case CURLMOPT_MAX_HOST_CONNECTIONS:
     multi->max_host_connections = va_arg(param, long);
@@ -3350,9 +3375,9 @@
   case CURLMOPT_MAX_CONCURRENT_STREAMS:
     {
       long streams = va_arg(param, long);
-      if(streams < 1)
+      if((streams < 1) || (streams > INT_MAX))
         streams = 100;
-      multi->max_concurrent_streams = curlx_sltoui(streams);
+      multi->max_concurrent_streams = (unsigned int)streams;
     }
     break;
   default:
@@ -3782,11 +3807,11 @@
   struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) *
                                 (multi->num_easy + 1));
   if(a) {
-    int i = 0;
+    unsigned int i = 0;
     struct Curl_easy *e = multi->easyp;
     while(e) {
       DEBUGASSERT(i < multi->num_easy);
-      if(!e->internal)
+      if(!e->state.internal)
         a[i++] = e;
       e = e->next;
     }
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index 5b16bb6..e03e382 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -93,9 +93,9 @@
   struct Curl_easy *easyp;
   struct Curl_easy *easylp; /* last node */
 
-  int num_easy; /* amount of entries in the linked list above. */
-  int num_alive; /* amount of easy handles that are added but have not yet
-                    reached COMPLETE state */
+  unsigned int num_easy; /* amount of entries in the linked list above. */
+  unsigned int num_alive; /* amount of easy handles that are added but have
+                             not yet reached COMPLETE state */
 
   struct Curl_llist msglist; /* a list of messages from completed transfers */
 
@@ -136,9 +136,6 @@
   /* Shared connection cache (bundles)*/
   struct conncache conn_cache;
 
-  long maxconnects; /* if >0, a fixed limit of the maximum number of entries
-                       we're allowed to grow the connection cache to */
-
   long max_host_connections; /* if >0, a fixed limit of the maximum number
                                 of connections per host */
 
@@ -150,8 +147,6 @@
   void *timer_userp;
   struct curltime timer_lastcall; /* the fixed time for the timeout for the
                                     previous callback */
-  unsigned int max_concurrent_streams;
-
 #ifdef USE_WINSOCK
   WSAEVENT wsa_event; /* winsock event used for waits */
 #else
@@ -160,6 +155,10 @@
                                    0 is used for read, 1 is used for write */
 #endif
 #endif
+  unsigned int max_concurrent_streams;
+  unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of
+                               entries we're allowed to grow the connection
+                               cache to */
 #define IPV6_UNKNOWN 0
 #define IPV6_DEAD    1
 #define IPV6_WORKS   2
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index e6a09b1..038c6dc 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -327,7 +327,7 @@
     }
     retcode = parsenetrc(host, loginp, passwordp, filealloc);
     free(filealloc);
-#ifdef WIN32
+#ifdef _WIN32
     if(retcode == NETRC_FILE_MISSING) {
       /* fallback to the old-style "_netrc" file */
       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c
index 2b9908d..5241640 100644
--- a/Utilities/cmcurl/lib/noproxy.c
+++ b/Utilities/cmcurl/lib/noproxy.c
@@ -216,7 +216,6 @@
           /* case C passes through, not a match */
           break;
         case TYPE_IPV4:
-          /* FALLTHROUGH */
         case TYPE_IPV6: {
           const char *check = token;
           char *slash;
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index 3aff306..1e60ff7 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -130,7 +130,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
@@ -158,7 +158,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
@@ -319,31 +319,12 @@
 {
   CURLcode result;
   LDAPURLDesc *lud;
-  struct ldapconninfo *li;
+  (void)conn;
 
   /* Early URL syntax check. */
   result = oldap_url_parse(data, &lud);
   ldap_free_urldesc(lud);
 
-  if(!result) {
-    li = calloc(1, sizeof(struct ldapconninfo));
-    if(!li)
-      result = CURLE_OUT_OF_MEMORY;
-    else {
-      li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
-      conn->proto.ldapc = li;
-      connkeep(conn, "OpenLDAP default");
-
-      /* Initialize the SASL storage */
-      Curl_sasl_init(&li->sasl, data, &saslldap);
-
-      /* Clear the TLS upgraded flag */
-      conn->bits.tls_upgraded = FALSE;
-
-      result = oldap_parse_login_options(conn);
-    }
-  }
-
   return result;
 }
 
@@ -537,7 +518,7 @@
 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li;
   static const int version = LDAP_VERSION3;
   int rc;
   char *hosturl;
@@ -547,6 +528,26 @@
 
   (void)done;
 
+  DEBUGASSERT(!conn->proto.ldapc);
+  li = calloc(1, sizeof(struct ldapconninfo));
+  if(!li)
+    return CURLE_OUT_OF_MEMORY;
+  else {
+    CURLcode result;
+    li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
+    conn->proto.ldapc = li;
+
+    /* Initialize the SASL storage */
+    Curl_sasl_init(&li->sasl, data, &saslldap);
+
+    /* Clear the TLS upgraded flag */
+    conn->bits.tls_upgraded = FALSE;
+
+    result = oldap_parse_login_options(conn);
+    if(result)
+      return result;
+  }
+
   hosturl = aprintf("ldap%s://%s:%d",
                     conn->handler->flags & PROTOPT_SSL? "s": "",
                     conn->host.name, conn->remote_port);
@@ -644,7 +645,7 @@
     switch(code) {
     case LDAP_SIZELIMIT_EXCEEDED:
       infof(data, "Too many authentication mechanisms\n");
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case LDAP_SUCCESS:
     case LDAP_NO_RESULTS_RETURNED:
       if(Curl_sasl_can_authenticate(&li->sasl, data))
@@ -792,10 +793,13 @@
         result = oldap_perform_bind(data, OLDAP_BIND);
       break;
     }
-    /* FALLTHROUGH */
+    result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
+    if(result)
+      break;
+    FALLTHROUGH();
   case OLDAP_TLS:
     result = oldap_ssl_connect(data, OLDAP_TLS);
-    if(result && data->set.use_ssl != CURLUSESSL_TRY)
+    if(result)
       result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
     else if(ssl_installed(conn)) {
       conn->bits.tls_upgraded = TRUE;
@@ -886,6 +890,15 @@
 
   result = oldap_url_parse(data, &lud);
   if(!result) {
+#ifdef USE_SSL
+    if(ssl_installed(conn)) {
+      Sockbuf *sb;
+      /* re-install the libcurl SSL handlers into the sockbuf. */
+      ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+      ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+    }
+#endif
+
     rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
                          lud->lud_filter, lud->lud_attrs, 0,
                          NULL, NULL, NULL, 0, &msgid);
@@ -947,18 +960,12 @@
     if(!len && plen && prefix[plen - 1] == ' ')
       plen--;
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
-    if(!result)
-      data->req.bytecount += plen;
   }
   if(!result && value) {
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
-    if(!result)
-      data->req.bytecount += len;
   }
   if(!result && suffix) {
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
-    if(!result)
-      data->req.bytecount += slen;
   }
   return result;
 }
@@ -1014,7 +1021,7 @@
     switch(code) {
     case LDAP_SIZELIMIT_EXCEEDED:
       infof(data, "There are more than %d entries", lr->nument);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case LDAP_SUCCESS:
       data->req.size = data->req.bytecount;
       break;
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 0081c9c..b976ffb 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -36,6 +36,7 @@
 #include "pingpong.h"
 #include "multiif.h"
 #include "vtls/vtls.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -105,7 +106,7 @@
 
   if(Curl_conn_data_pending(data, FIRSTSOCKET))
     rc = 1;
-  else if(Curl_pp_moredata(pp))
+  else if(pp->overflow)
     /* We are receiving and there is data in the cache so just read it */
     rc = 1;
   else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET))
@@ -139,19 +140,13 @@
 }
 
 /* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp)
+void Curl_pp_init(struct pingpong *pp)
 {
-  DEBUGASSERT(data);
   pp->nread_resp = 0;
-  pp->linestart_resp = data->state.buffer;
-  pp->pending_resp = TRUE;
   pp->response = Curl_now(); /* start response time-out now! */
-}
-
-/* setup for the coming transfer */
-void Curl_pp_setup(struct pingpong *pp)
-{
+  pp->pending_resp = TRUE;
   Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
+  Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
 }
 
 /***********************************************************************
@@ -197,9 +192,9 @@
   if(result)
     return result;
 
+  pp->pending_resp = TRUE;
   write_len = Curl_dyn_len(&pp->sendbuf);
   s = Curl_dyn_ptr(&pp->sendbuf);
-  Curl_pp_init(data, pp);
 
 #ifdef HAVE_GSSAPI
   conn->data_prot = PROT_CMD;
@@ -255,6 +250,25 @@
   return result;
 }
 
+static CURLcode pingpong_read(struct Curl_easy *data,
+                              curl_socket_t sockfd,
+                              char *buffer,
+                              size_t buflen,
+                              ssize_t *nread)
+{
+  CURLcode result;
+#ifdef HAVE_GSSAPI
+  enum protection_level prot = data->conn->data_prot;
+  data->conn->data_prot = PROT_CLEAR;
+#endif
+  result = Curl_read(data, sockfd, buffer, buflen, nread);
+#ifdef HAVE_GSSAPI
+  DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
+  data->conn->data_prot = (unsigned char)prot;
+#endif
+  return result;
+}
+
 /*
  * Curl_pp_readresp()
  *
@@ -266,181 +280,96 @@
                           int *code, /* return the server code if done */
                           size_t *size) /* size of the response */
 {
-  ssize_t perline; /* count bytes per line */
-  bool keepon = TRUE;
-  ssize_t gotbytes;
-  char *ptr;
   struct connectdata *conn = data->conn;
-  char * const buf = data->state.buffer;
   CURLcode result = CURLE_OK;
 
   *code = 0; /* 0 for errors or not done */
   *size = 0;
 
-  ptr = buf + pp->nread_resp;
+  if(pp->nfinal) {
+    /* a previous call left this many bytes in the beginning of the buffer as
+       that was the final line; now ditch that */
+    size_t full = Curl_dyn_len(&pp->recvbuf);
 
-  /* number of bytes in the current line, so far */
-  perline = (ssize_t)(ptr-pp->linestart_resp);
+    /* trim off the "final" leading part */
+    Curl_dyn_tail(&pp->recvbuf, full -  pp->nfinal);
 
-  while((pp->nread_resp < (size_t)data->set.buffer_size) &&
-        (keepon && !result)) {
+    pp->nfinal = 0; /* now gone */
+  }
+  if(!pp->overflow) {
+    ssize_t gotbytes = 0;
+    char buffer[900];
 
-    if(pp->cache) {
-      /* we had data in the "cache", copy that instead of doing an actual
-       * read
-       *
-       * pp->cache_size is cast to ssize_t here.  This should be safe, because
-       * it would have been populated with something of size int to begin
-       * with, even though its datatype may be larger than an int.
-       */
-      if((ptr + pp->cache_size) > (buf + data->set.buffer_size + 1)) {
-        failf(data, "cached response data too big to handle");
-        return CURLE_WEIRD_SERVER_REPLY;
-      }
-      memcpy(ptr, pp->cache, pp->cache_size);
-      gotbytes = (ssize_t)pp->cache_size;
-      free(pp->cache);    /* free the cache */
-      pp->cache = NULL;   /* clear the pointer */
-      pp->cache_size = 0; /* zero the size just in case */
-    }
-    else {
-#ifdef HAVE_GSSAPI
-      enum protection_level prot = conn->data_prot;
-      conn->data_prot = PROT_CLEAR;
-#endif
-      DEBUGASSERT((ptr + data->set.buffer_size - pp->nread_resp) <=
-                  (buf + data->set.buffer_size + 1));
-      result = Curl_read(data, sockfd, ptr,
-                         data->set.buffer_size - pp->nread_resp,
-                         &gotbytes);
-#ifdef HAVE_GSSAPI
-      DEBUGASSERT(prot  > PROT_NONE && prot < PROT_LAST);
-      conn->data_prot = (unsigned char)prot;
-#endif
-      if(result == CURLE_AGAIN)
-        return CURLE_OK; /* return */
+    result = pingpong_read(data, sockfd, buffer, sizeof(buffer), &gotbytes);
+    if(result == CURLE_AGAIN)
+      return CURLE_OK;
 
-      if(result)
-        /* Set outer result variable to this error. */
-        keepon = FALSE;
-    }
+    if(result)
+      return result;
 
-    if(!keepon)
-      ;
-    else if(gotbytes <= 0) {
-      keepon = FALSE;
-      result = CURLE_RECV_ERROR;
+    if(gotbytes <= 0) {
       failf(data, "response reading failed (errno: %d)", SOCKERRNO);
+      return CURLE_RECV_ERROR;
+    }
+
+    result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
+    if(result)
+      return result;
+
+    data->req.headerbytecount += (unsigned int)gotbytes;
+
+    pp->nread_resp += gotbytes;
+  }
+
+  do {
+    char *line = Curl_dyn_ptr(&pp->recvbuf);
+    char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
+    if(nl) {
+      /* a newline is CRLF in pp-talk, so the CR is ignored as
+         the line isn't really terminated until the LF comes */
+      size_t length = nl - line + 1;
+
+      /* output debug output if that is requested */
+#ifdef HAVE_GSSAPI
+      if(!conn->sec_complete)
+#endif
+        Curl_debug(data, CURLINFO_HEADER_IN, line, length);
+
+      /*
+       * Pass all response-lines to the callback function registered for
+       * "headers". The response lines can be seen as a kind of headers.
+       */
+      result = Curl_client_write(data, CLIENTWRITE_INFO, line, length);
+      if(result)
+        return result;
+
+      if(pp->endofresp(data, conn, line, length, code)) {
+        /* When at "end of response", keep the endofresp line first in the
+           buffer since it will be accessed outside (by pingpong
+           parsers). Store the overflow counter to inform about additional
+           data in this buffer after the endofresp line. */
+        pp->nfinal = length;
+        if(Curl_dyn_len(&pp->recvbuf) > length)
+          pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
+        else
+          pp->overflow = 0;
+        *size = pp->nread_resp; /* size of the response */
+        pp->nread_resp = 0; /* restart */
+        break;
+      }
+      if(Curl_dyn_len(&pp->recvbuf) > length)
+        /* keep the remaining piece */
+        Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
+      else
+        Curl_dyn_reset(&pp->recvbuf);
     }
     else {
-      /* we got a whole chunk of data, which can be anything from one
-       * byte to a set of lines and possible just a piece of the last
-       * line */
-      ssize_t i;
-      ssize_t clipamount = 0;
-      bool restart = FALSE;
+      /* without a newline, there is no overflow */
+      pp->overflow = 0;
+      break;
+    }
 
-      data->req.headerbytecount += (unsigned int)gotbytes;
-
-      pp->nread_resp += gotbytes;
-      for(i = 0; i < gotbytes; ptr++, i++) {
-        perline++;
-        if(*ptr == '\n') {
-          /* a newline is CRLF in pp-talk, so the CR is ignored as
-             the line isn't really terminated until the LF comes */
-
-          /* output debug output if that is requested */
-#ifdef HAVE_GSSAPI
-          if(!conn->sec_complete)
-#endif
-            Curl_debug(data, CURLINFO_HEADER_IN,
-                       pp->linestart_resp, (size_t)perline);
-
-          /*
-           * We pass all response-lines to the callback function registered
-           * for "headers". The response lines can be seen as a kind of
-           * headers.
-           */
-          result = Curl_client_write(data, CLIENTWRITE_INFO,
-                                     pp->linestart_resp, perline);
-          if(result)
-            return result;
-
-          if(pp->endofresp(data, conn, pp->linestart_resp, perline, code)) {
-            /* This is the end of the last line, copy the last line to the
-               start of the buffer and null-terminate, for old times sake */
-            size_t n = ptr - pp->linestart_resp;
-            memmove(buf, pp->linestart_resp, n);
-            buf[n] = 0; /* null-terminate */
-            keepon = FALSE;
-            pp->linestart_resp = ptr + 1; /* advance pointer */
-            i++; /* skip this before getting out */
-
-            *size = pp->nread_resp; /* size of the response */
-            pp->nread_resp = 0; /* restart */
-            break;
-          }
-          perline = 0; /* line starts over here */
-          pp->linestart_resp = ptr + 1;
-        }
-      }
-
-      if(!keepon && (i != gotbytes)) {
-        /* We found the end of the response lines, but we didn't parse the
-           full chunk of data we have read from the server. We therefore need
-           to store the rest of the data to be checked on the next invoke as
-           it may actually contain another end of response already! */
-        clipamount = gotbytes - i;
-        restart = TRUE;
-        DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
-                     "server response left",
-                     (int)clipamount));
-      }
-      else if(keepon) {
-
-        if((perline == gotbytes) &&
-           (gotbytes > (ssize_t)data->set.buffer_size/2)) {
-          /* We got an excessive line without newlines and we need to deal
-             with it. We keep the first bytes of the line then we throw
-             away the rest. */
-          infof(data, "Excessive server response line length received, "
-                "%zd bytes. Stripping", gotbytes);
-          restart = TRUE;
-
-          /* we keep 40 bytes since all our pingpong protocols are only
-             interested in the first piece */
-          clipamount = 40;
-        }
-        else if(pp->nread_resp > (size_t)data->set.buffer_size/2) {
-          /* We got a large chunk of data and there's potentially still
-             trailing data to take care of, so we put any such part in the
-             "cache", clear the buffer to make space and restart. */
-          clipamount = perline;
-          restart = TRUE;
-        }
-      }
-      else if(i == gotbytes)
-        restart = TRUE;
-
-      if(clipamount) {
-        pp->cache_size = clipamount;
-        pp->cache = malloc(pp->cache_size);
-        if(pp->cache)
-          memcpy(pp->cache, pp->linestart_resp, pp->cache_size);
-        else
-          return CURLE_OUT_OF_MEMORY;
-      }
-      if(restart) {
-        /* now reset a few variables to start over nicely from the start of
-           the big buffer */
-        pp->nread_resp = 0; /* start over from scratch in the buffer */
-        ptr = pp->linestart_resp = buf;
-        perline = 0;
-      }
-
-    } /* there was data */
-
-  } /* while there's buffer left and loop is requested */
+  } while(1); /* while there's buffer left to scan */
 
   pp->pending_resp = FALSE;
 
@@ -488,14 +417,13 @@
 CURLcode Curl_pp_disconnect(struct pingpong *pp)
 {
   Curl_dyn_free(&pp->sendbuf);
-  Curl_safefree(pp->cache);
+  Curl_dyn_free(&pp->recvbuf);
   return CURLE_OK;
 }
 
 bool Curl_pp_moredata(struct pingpong *pp)
 {
-  return (!pp->sendleft && pp->cache && pp->nread_resp < pp->cache_size) ?
-    TRUE : FALSE;
+  return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf));
 }
 
 #endif
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h
index 80d3f77..006b9c5 100644
--- a/Utilities/cmcurl/lib/pingpong.h
+++ b/Utilities/cmcurl/lib/pingpong.h
@@ -47,16 +47,11 @@
  * It holds response cache and non-blocking sending data.
  */
 struct pingpong {
-  char *cache;     /* data cache between getresponse()-calls */
-  size_t cache_size;  /* size of cache in bytes */
   size_t nread_resp;  /* number of bytes currently read of a server response */
-  char *linestart_resp; /* line start pointer for the server response
-                           reader function */
   bool pending_resp;  /* set TRUE when a server response is pending or in
                          progress, and is cleared once the last response is
                          read */
-  char *sendthis; /* allocated pointer to a buffer that is to be sent to the
-                     server */
+  char *sendthis; /* pointer to a buffer that is to be sent to the server */
   size_t sendleft; /* number of bytes left to send from the sendthis buffer */
   size_t sendsize; /* total size of the sendthis buffer */
   struct curltime response; /* set to Curl_now() when a command has been sent
@@ -64,6 +59,10 @@
   timediff_t response_time; /* When no timeout is given, this is the amount of
                                milliseconds we await for a server response. */
   struct dynbuf sendbuf;
+  struct dynbuf recvbuf;
+  size_t overflow; /* number of bytes left after a final response line */
+  size_t nfinal;   /* number of bytes in the final response line, which
+                      after a match is first in the receice buffer */
 
   /* Function pointers the protocols MUST implement and provide for the
      pingpong layer to function */
@@ -90,10 +89,7 @@
                            bool block, bool disconnecting);
 
 /* initialize stuff to prepare for reading a fresh new response */
-void Curl_pp_init(struct Curl_easy *data, struct pingpong *pp);
-
-/* setup for the transfer */
-void Curl_pp_setup(struct pingpong *pp);
+void Curl_pp_init(struct pingpong *pp);
 
 /* Returns timeout in ms. 0 or negative number means the timeout has already
    triggered */
@@ -113,7 +109,7 @@
  */
 CURLcode Curl_pp_sendf(struct Curl_easy *data,
                        struct pingpong *pp,
-                       const char *fmt, ...);
+                       const char *fmt, ...) CURL_PRINTF(3, 4);
 
 /***********************************************************************
  *
@@ -128,7 +124,7 @@
 CURLcode Curl_pp_vsendf(struct Curl_easy *data,
                         struct pingpong *pp,
                         const char *fmt,
-                        va_list args);
+                        va_list args) CURL_PRINTF(3, 0);
 
 /*
  * Curl_pp_readresp()
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index a9d5fdd6..cf25192 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -77,6 +77,7 @@
 #include "curl_sasl.h"
 #include "curl_md5.h"
 #include "warnless.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -124,7 +125,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3,                        /* defport */
@@ -153,7 +154,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3S,                       /* defport */
@@ -251,8 +252,8 @@
  */
 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = data->state.buffer;
-  size_t len = strlen(message);
+  char *message = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+  size_t len = data->conn->proto.pop3c.pp.nfinal;
 
   if(len > 2) {
     /* Find the start of the message */
@@ -648,8 +649,8 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *line = data->state.buffer;
-  size_t len = strlen(line);
+  const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+  size_t len = data->conn->proto.pop3c.pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
@@ -657,44 +658,35 @@
     failf(data, "Got unexpected pop3-server response");
     result = CURLE_WEIRD_SERVER_REPLY;
   }
-  else {
+  else if(len > 3) {
     /* Does the server support APOP authentication? */
-    if(len >= 4 && line[len - 2] == '>') {
-      /* Look for the APOP timestamp */
-      size_t i;
-      for(i = 3; i < len - 2; ++i) {
-        if(line[i] == '<') {
-          /* Calculate the length of the timestamp */
-          size_t timestamplen = len - 1 - i;
-          char *at;
-          if(!timestamplen)
-            break;
+    char *lt;
+    char *gt = NULL;
 
-          /* Allocate some memory for the timestamp */
-          pop3c->apoptimestamp = (char *)calloc(1, timestamplen + 1);
-
-          if(!pop3c->apoptimestamp)
-            break;
-
-          /* Copy the timestamp */
-          memcpy(pop3c->apoptimestamp, line + i, timestamplen);
-          pop3c->apoptimestamp[timestamplen] = '\0';
-
-          /* If the timestamp does not contain '@' it is not (as required by
-             RFC-1939) conformant to the RFC-822 message id syntax, and we
-             therefore do not use APOP authentication. */
-          at = strchr(pop3c->apoptimestamp, '@');
-          if(!at)
-            Curl_safefree(pop3c->apoptimestamp);
-          else
-            /* Store the APOP capability */
-            pop3c->authtypes |= POP3_TYPE_APOP;
-          break;
-        }
+    /* Look for the APOP timestamp */
+    lt = memchr(line, '<', len);
+    if(lt)
+      /* search the remainder for '>' */
+      gt = memchr(lt, '>', len - (lt - line));
+    if(gt) {
+      /* the length of the timestamp, including the brackets */
+      size_t timestamplen = gt - lt + 1;
+      char *at = memchr(lt, '@', timestamplen);
+      /* If the timestamp does not contain '@' it is not (as required by
+         RFC-1939) conformant to the RFC-822 message id syntax, and we
+         therefore do not use APOP authentication. */
+      if(at) {
+        /* dupe the timestamp */
+        pop3c->apoptimestamp = Curl_memdup0(lt, timestamplen);
+        if(!pop3c->apoptimestamp)
+          return CURLE_OUT_OF_MEMORY;
+        /* Store the APOP capability */
+        pop3c->authtypes |= POP3_TYPE_APOP;
       }
     }
 
-    result = pop3_perform_capa(data, conn);
+    if(!result)
+      result = pop3_perform_capa(data, conn);
   }
 
   return result;
@@ -707,8 +699,8 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *line = data->state.buffer;
-  size_t len = strlen(line);
+  const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
+  size_t len = data->conn->proto.pop3c.pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
@@ -795,7 +787,7 @@
   (void)instate; /* no use for this yet */
 
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.pop3c.pp.cache_size)
+  if(data->conn->proto.pop3c.pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(pop3code != '+') {
@@ -944,24 +936,29 @@
     /* POP3 download */
     Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
 
-    if(pp->cache) {
-      /* The header "cache" contains a bunch of data that is actually body
-         content so send it as such. Note that there may even be additional
-         "headers" after the body */
+    if(pp->overflow) {
+      /* The recv buffer contains data that is actually body content so send
+         it as such. Note that there may even be additional "headers" after
+         the body */
+
+      /* keep only the overflow */
+      Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+      pp->nfinal = 0; /* done */
 
       if(!data->req.no_body) {
-        result = Curl_pop3_write(data, pp->cache, pp->cache_size);
+        result = Curl_pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
+                                 Curl_dyn_len(&pp->recvbuf));
         if(result)
           return result;
       }
 
-      /* Free the cache */
-      Curl_safefree(pp->cache);
-
-      /* Reset the cache size */
-      pp->cache_size = 0;
+      /* reset the buffer */
+      Curl_dyn_reset(&pp->recvbuf);
+      pp->overflow = 0;
     }
   }
+  else
+    pp->overflow = 0;
 
   /* End of DO phase */
   pop3_state(data, POP3_STOP);
@@ -1088,7 +1085,7 @@
   CURLcode result = CURLE_OK;
   struct POP3 *pop3;
 
-  pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1);
+  pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
   if(!pop3)
     result = CURLE_OUT_OF_MEMORY;
 
@@ -1131,8 +1128,7 @@
   Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
 
   /* Initialise the pingpong layer */
-  Curl_pp_setup(pp);
-  Curl_pp_init(data, pp);
+  Curl_pp_init(pp);
 
   /* Parse the URL options */
   result = pop3_parse_url_options(conn);
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index e783a9c..d05fcc3 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -174,10 +174,18 @@
     data->progress.t_startop = timestamp;
     break;
   case TIMER_STARTSINGLE:
-    /* This is set at the start of each single fetch */
+    /* This is set at the start of each single transfer */
     data->progress.t_startsingle = timestamp;
     data->progress.is_t_startransfer_set = false;
     break;
+  case TIMER_POSTQUEUE:
+    /* Set when the transfer starts (after potentially having been brought
+       back from the waiting queue). It needs to count from t_startop and not
+       t_startsingle since the latter is reset when a connection is brought
+       back from the pending queue. */
+    data->progress.t_postqueue =
+      Curl_timediff_us(timestamp, data->progress.t_startop);
+    break;
   case TIMER_STARTACCEPT:
     data->progress.t_acceptdata = timestamp;
     break;
@@ -304,7 +312,7 @@
    * 'actual' is the time in milliseconds it took to actually download the
    * last 'size' bytes.
    */
-  actual = Curl_timediff(now, start);
+  actual = Curl_timediff_ceil(now, start);
   if(actual < minimum) {
     /* if it downloaded the data faster than the limit, make it wait the
        difference */
@@ -319,12 +327,6 @@
  */
 CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
 {
-  if(data->set.max_filesize && (size > data->set.max_filesize)) {
-    failf(data, "Exceeded the maximum allowed file size "
-          "(%" CURL_FORMAT_CURL_OFF_T ")",
-          data->set.max_filesize);
-    return CURLE_FILESIZE_EXCEEDED;
-  }
   data->progress.downloaded = size;
   return CURLE_OK;
 }
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h
index fc39e34..7374941 100644
--- a/Utilities/cmcurl/lib/progress.h
+++ b/Utilities/cmcurl/lib/progress.h
@@ -30,7 +30,8 @@
 typedef enum {
   TIMER_NONE,
   TIMER_STARTOP,
-  TIMER_STARTSINGLE,
+  TIMER_STARTSINGLE, /* start of transfer, might get queued */
+  TIMER_POSTQUEUE,   /* start, immediately after dequeue */
   TIMER_NAMELOOKUP,
   TIMER_CONNECT,
   TIMER_APPCONNECT,
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index 6bd9613..c62b1a4 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -32,10 +32,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_ARC4RANDOM
-/* Some platforms might have the prototype missing (ubuntu + libressl) */
-uint32_t arc4random(void);
-#endif
 
 #include <curl/curl.h>
 #include "urldata.h"
@@ -50,7 +46,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 
 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
 #  define HAVE_WIN_BCRYPTGENRANDOM
@@ -105,7 +101,6 @@
 
 static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 {
-  unsigned int r;
   CURLcode result = CURLE_OK;
   static unsigned int randseed;
   static bool seeded = FALSE;
@@ -138,7 +133,7 @@
 
   /* ---- non-cryptographic version following ---- */
 
-#ifdef WIN32
+#ifdef _WIN32
   if(!seeded) {
     result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
     if(result != CURLE_NOT_BUILT_IN)
@@ -146,12 +141,14 @@
   }
 #endif
 
-#ifdef HAVE_ARC4RANDOM
-  *rnd = (unsigned int)arc4random();
-  return CURLE_OK;
+#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL)
+  if(!seeded) {
+    *rnd = (unsigned int)arc4random();
+    return CURLE_OK;
+  }
 #endif
 
-#if defined(RANDOM_FILE) && !defined(WIN32)
+#if defined(RANDOM_FILE) && !defined(_WIN32)
   if(!seeded) {
     /* if there's a random file to read a seed from, use it */
     int fd = open(RANDOM_FILE, O_RDONLY);
@@ -175,9 +172,12 @@
     seeded = TRUE;
   }
 
-  /* Return an unsigned 32-bit pseudo-random number. */
-  r = randseed = randseed * 1103515245 + 12345;
-  *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
+  {
+    unsigned int r;
+    /* Return an unsigned 32-bit pseudo-random number. */
+    r = randseed = randseed * 1103515245 + 12345;
+    *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
+  }
   return CURLE_OK;
 }
 
@@ -201,7 +201,7 @@
 {
   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
 
-  DEBUGASSERT(num > 0);
+  DEBUGASSERT(num);
 
   while(num) {
     unsigned int r;
@@ -241,9 +241,11 @@
   memset(buffer, 0, sizeof(buffer));
 #endif
 
-  if((num/2 >= sizeof(buffer)) || !(num&1))
+  if((num/2 >= sizeof(buffer)) || !(num&1)) {
     /* make sure it fits in the local buffer and that it is an odd number! */
+    DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex"));
     return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
 
   num--; /* save one for null-termination */
 
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index 1d009f5..bc05239 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -41,7 +41,7 @@
 CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
                          size_t num);
 
-#ifdef WIN32
+#ifdef _WIN32
 /* Random generator shared between the Schannel vtls and Curl_rand*()
    functions */
 CURLcode Curl_win32_random(unsigned char *entropy, size_t length);
diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c
index 97a66e9..4c88698 100644
--- a/Utilities/cmcurl/lib/rename.c
+++ b/Utilities/cmcurl/lib/rename.c
@@ -40,7 +40,7 @@
 /* return 0 on success, 1 on error */
 int Curl_rename(const char *oldpath, const char *newpath)
 {
-#ifdef WIN32
+#ifdef _WIN32
   /* rename() on Windows doesn't overwrite, so we can't use it here.
      MoveFileEx() will overwrite and is usually atomic, however it fails
      when there are open handles to the file. */
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index ccd7264..26f4735 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -45,8 +45,8 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define RTP_PKT_LENGTH(p)  ((((int)((unsigned char)((p)[2]))) << 8) | \
-                             ((int)((unsigned char)((p)[3]))))
+#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
+                            ((unsigned int)((unsigned char)((p)[3]))))
 
 /* protocol-specific functions set up to be called by the main engine */
 static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
@@ -58,16 +58,20 @@
                            struct connectdata *conn, curl_socket_t *socks);
 
 /*
- * Parse and write out any available RTP data.
- *
- * nread: amount of data left after k->str. will be modified if RTP
- *        data is parsed and k->str is moved up
- * readmore: whether or not the RTP parser needs more data right away
+ * Parse and write out an RTSP response.
+ * @param data     the transfer
+ * @param conn     the connection
+ * @param buf      data read from connection
+ * @param blen     amount of data in buf
+ * @param is_eos   TRUE iff this is the last write
+ * @param readmore out, TRUE iff complete buf was consumed and more data
+ *                 is needed
  */
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
-                                   struct connectdata *conn,
-                                   ssize_t *nread,
-                                   bool *readmore);
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+                                    const char *buf,
+                                    size_t blen,
+                                    bool is_eos,
+                                    bool *done);
 
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
@@ -88,7 +92,7 @@
 }
 
 static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len);
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
 static
 CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
 
@@ -110,7 +114,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtsp_disconnect,                      /* disconnect */
-  rtsp_rtp_readwrite,                   /* readwrite */
+  rtsp_rtp_write_resp,                  /* write_resp */
   rtsp_conncheck,                       /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_RTSP,                            /* defport */
@@ -585,153 +589,281 @@
   return result;
 }
 
+/**
+ * write any BODY bytes missing to the client, ignore the rest.
+ */
+static CURLcode rtp_write_body_junk(struct Curl_easy *data,
+                                    const char *buf,
+                                    size_t blen)
+{
+  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+  curl_off_t body_remain;
+  bool in_body;
 
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
-                                   struct connectdata *conn,
-                                   ssize_t *nread,
-                                   bool *readmore) {
-  struct SingleRequest *k = &data->req;
-  struct rtsp_conn *rtspc = &(conn->proto.rtspc);
-  unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
-
-  char *rtp; /* moving pointer to rtp data */
-  ssize_t rtp_dataleft; /* how much data left to parse in this round */
-  CURLcode result;
-  bool interleaved = false;
-  size_t skip_size = 0;
-
-  if(Curl_dyn_len(&rtspc->buf)) {
-    /* There was some leftover data the last time. Append new buffers */
-    if(Curl_dyn_addn(&rtspc->buf, k->str, *nread))
-      return CURLE_OUT_OF_MEMORY;
-    rtp = Curl_dyn_ptr(&rtspc->buf);
-    rtp_dataleft = Curl_dyn_len(&rtspc->buf);
+  in_body = (data->req.headerline && !rtspc->in_header) &&
+            (data->req.size >= 0) &&
+            (data->req.bytecount < data->req.size);
+  body_remain = in_body? (data->req.size - data->req.bytecount) : 0;
+  DEBUGASSERT(body_remain >= 0);
+  if(body_remain) {
+    if((curl_off_t)blen > body_remain)
+      blen = (size_t)body_remain;
+    return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
   }
-  else {
-    /* Just parse the request buffer directly */
-    rtp = k->str;
-    rtp_dataleft = *nread;
-  }
-
-  while(rtp_dataleft > 0) {
-    if(rtp[0] == '$') {
-      if(rtp_dataleft > 4) {
-        unsigned char rtp_channel;
-        int rtp_length;
-        int idx;
-        int off;
-
-        /* Parse the header */
-        /* The channel identifier immediately follows and is 1 byte */
-        rtp_channel = (unsigned char)rtp[1];
-        idx = rtp_channel / 8;
-        off = rtp_channel % 8;
-        if(!(rtp_channel_mask[idx] & (1 << off))) {
-          /* invalid channel number, maybe not an RTP packet */
-          rtp++;
-          rtp_dataleft--;
-          skip_size++;
-          continue;
-        }
-        if(skip_size > 0) {
-          DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
-                       "bytes", skip_size));
-        }
-        skip_size = 0;
-        rtspc->rtp_channel = rtp_channel;
-
-        /* The length is two bytes */
-        rtp_length = RTP_PKT_LENGTH(rtp);
-
-        if(rtp_dataleft < rtp_length + 4) {
-          /* Need more - incomplete payload */
-          *readmore = TRUE;
-          break;
-        }
-        interleaved = true;
-        /* We have the full RTP interleaved packet
-         * Write out the header including the leading '$' */
-        DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
-                     rtspc->rtp_channel, rtp_length));
-        result = rtp_client_write(data, &rtp[0], rtp_length + 4);
-        if(result) {
-          *readmore = FALSE;
-          return result;
-        }
-
-        /* Move forward in the buffer */
-        rtp_dataleft -= rtp_length + 4;
-        rtp += rtp_length + 4;
-
-        if(data->set.rtspreq == RTSPREQ_RECEIVE) {
-          /* If we are in a passive receive, give control back
-           * to the app as often as we can.
-           */
-          k->keepon &= ~KEEP_RECV;
-        }
-      }
-      else {
-        /* Need more - incomplete header */
-        *readmore = TRUE;
-        break;
-      }
-    }
-    else {
-      /* If the following data begins with 'RTSP/', which might be an RTSP
-         message, we should stop skipping the data. */
-      /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the
-         middle of an RTSP message. It is difficult to determine this, so we
-         stop skipping. */
-      size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5;
-      if((k->headerline > 0 && !interleaved) ||
-         strncmp(rtp, "RTSP/", prefix_len) == 0) {
-        if(skip_size > 0) {
-          DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
-                       "bytes", skip_size));
-        }
-        break; /* maybe is an RTSP message */
-      }
-      /* Skip incorrect data util the next RTP packet or RTSP message */
-      do {
-        rtp++;
-        rtp_dataleft--;
-        skip_size++;
-      } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R');
-    }
-  }
-
-  if(rtp_dataleft && rtp[0] == '$') {
-    DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
-                 *readmore ? "(READMORE)" : ""));
-
-    /* Store the incomplete RTP packet for a "rewind" */
-    if(!Curl_dyn_len(&rtspc->buf)) {
-      /* nothing was stored, add this data */
-      if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft))
-        return CURLE_OUT_OF_MEMORY;
-    }
-    else {
-      /* keep the remainder */
-      Curl_dyn_tail(&rtspc->buf, rtp_dataleft);
-    }
-
-    /* As far as the transfer is concerned, this data is consumed */
-    *nread = 0;
-    return CURLE_OK;
-  }
-  /* Fix up k->str to point just after the last RTP packet */
-  k->str += *nread - rtp_dataleft;
-
-  *nread = rtp_dataleft;
-
-  /* If we get here, we have finished with the leftover/merge buffer */
-  Curl_dyn_free(&rtspc->buf);
-
   return CURLE_OK;
 }
 
+static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
+                                     const char *buf,
+                                     size_t blen,
+                                     size_t *pconsumed)
+{
+  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+  CURLcode result = CURLE_OK;
+  size_t skip_len = 0;
+
+  *pconsumed = 0;
+  while(blen) {
+    bool in_body = (data->req.headerline && !rtspc->in_header) &&
+                   (data->req.size >= 0) &&
+                   (data->req.bytecount < data->req.size);
+    switch(rtspc->state) {
+
+    case RTP_PARSE_SKIP: {
+      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
+      while(blen && buf[0] != '$') {
+        if(!in_body && buf[0] == 'R' &&
+           data->set.rtspreq != RTSPREQ_RECEIVE) {
+          if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) {
+            /* This could be the next response, no consume and return */
+            if(*pconsumed) {
+              DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, "
+                           "skipping %zd bytes of junk", *pconsumed));
+            }
+            rtspc->state = RTP_PARSE_SKIP;
+            rtspc->in_header = TRUE;
+            goto out;
+          }
+        }
+        /* junk/BODY, consume without buffering */
+        *pconsumed += 1;
+        ++buf;
+        --blen;
+        ++skip_len;
+      }
+      if(blen && buf[0] == '$') {
+        /* possible start of an RTP message, buffer */
+        if(skip_len) {
+          /* end of junk/BODY bytes, flush */
+          result = rtp_write_body_junk(data,
+                                       (char *)(buf - skip_len), skip_len);
+          skip_len = 0;
+          if(result)
+            goto out;
+        }
+        if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
+        }
+        *pconsumed += 1;
+        ++buf;
+        --blen;
+        rtspc->state = RTP_PARSE_CHANNEL;
+      }
+      break;
+    }
+
+    case RTP_PARSE_CHANNEL: {
+      int idx = ((unsigned char)buf[0]) / 8;
+      int off = ((unsigned char)buf[0]) % 8;
+      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
+      if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
+        /* invalid channel number, junk or BODY data */
+        rtspc->state = RTP_PARSE_SKIP;
+        DEBUGASSERT(skip_len == 0);
+        /* we do not consume this byte, it is BODY data */
+        DEBUGF(infof(data, "RTSP: invalid RTP channel %d, skipping", idx));
+        if(*pconsumed == 0) {
+          /* We did not consume the initial '$' in our buffer, but had
+           * it from an earlier call. We cannot un-consume it and have
+           * to write it directly as BODY data */
+          result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
+          if(result)
+            goto out;
+        }
+        else {
+          /* count the '$' as skip and continue */
+          skip_len = 1;
+        }
+        Curl_dyn_free(&rtspc->buf);
+        break;
+      }
+      /* a valid channel, so we expect this to be a real RTP message */
+      rtspc->rtp_channel = (unsigned char)buf[0];
+      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+      *pconsumed += 1;
+      ++buf;
+      --blen;
+      rtspc->state = RTP_PARSE_LEN;
+      break;
+    }
+
+    case RTP_PARSE_LEN: {
+      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      const char *rtp_buf;
+      DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
+      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+      *pconsumed += 1;
+      ++buf;
+      --blen;
+      if(rtp_len == 2)
+        break;
+      rtp_buf = Curl_dyn_ptr(&rtspc->buf);
+      rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
+      rtspc->state = RTP_PARSE_DATA;
+      break;
+    }
+
+    case RTP_PARSE_DATA: {
+      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      size_t needed;
+      DEBUGASSERT(rtp_len < rtspc->rtp_len);
+      needed = rtspc->rtp_len - rtp_len;
+      if(needed <= blen) {
+        if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
+        }
+        *pconsumed += needed;
+        buf += needed;
+        blen -= needed;
+        /* complete RTP message in buffer */
+        DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
+                     rtspc->rtp_channel, rtspc->rtp_len));
+        result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
+                                  rtspc->rtp_len);
+        Curl_dyn_free(&rtspc->buf);
+        rtspc->state = RTP_PARSE_SKIP;
+        if(result)
+          goto out;
+      }
+      else {
+        if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
+        }
+        *pconsumed += blen;
+        buf += blen;
+        blen = 0;
+      }
+      break;
+    }
+
+    default:
+      DEBUGASSERT(0);
+      return CURLE_RECV_ERROR;
+    }
+  }
+out:
+  if(!result && skip_len)
+    result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
+  return result;
+}
+
+static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
+                                    const char *buf,
+                                    size_t blen,
+                                    bool is_eos,
+                                    bool *done)
+{
+  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+  CURLcode result = CURLE_OK;
+  size_t consumed = 0;
+
+  if(!data->req.header)
+    rtspc->in_header = FALSE;
+  *done = FALSE;
+  if(!blen) {
+    goto out;
+  }
+
+  DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
+               blen, rtspc->in_header, is_eos));
+
+  /* If header parsing is not onging, extract RTP messages */
+  if(!rtspc->in_header) {
+    result = rtsp_filter_rtp(data, buf, blen, &consumed);
+    if(result)
+      goto out;
+    buf += consumed;
+    blen -= consumed;
+    /* either we consumed all or are at the start of header parsing */
+    if(blen && !data->req.header)
+      DEBUGF(infof(data, "RTSP: %zu bytes, possibly excess in response body",
+                   blen));
+  }
+
+  /* we want to parse headers, do so */
+  if(data->req.header && blen) {
+    rtspc->in_header = TRUE;
+    result = Curl_http_write_resp_hds(data, buf, blen, &consumed, done);
+    if(result)
+      goto out;
+
+    buf += consumed;
+    blen -= consumed;
+
+    if(!data->req.header)
+      rtspc->in_header = FALSE;
+
+    if(!rtspc->in_header) {
+      /* If header parsing is done, extract interleaved RTP messages */
+      if(data->req.size <= -1) {
+        /* Respect section 4.4 of rfc2326: If the Content-Length header is
+           absent, a length 0 must be assumed. */
+        data->req.size = 0;
+        data->req.download_done = TRUE;
+      }
+      result = rtsp_filter_rtp(data, buf, blen, &consumed);
+      if(result)
+        goto out;
+      blen -= consumed;
+    }
+  }
+
+  if(rtspc->state != RTP_PARSE_SKIP)
+    *done = FALSE;
+  /* we SHOULD have consumed all bytes, unless the response is borked.
+   * In which case we write out the left over bytes, letting the client
+   * writer deal with it (it will report EXCESS and fail the transfer). */
+  DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, done=%d "
+               " rtspc->state=%d, req.size=%" CURL_FORMAT_CURL_OFF_T ")",
+               blen, rtspc->in_header, *done, rtspc->state, data->req.size));
+  if(!result && (is_eos || blen)) {
+    result = Curl_client_write(data, CLIENTWRITE_BODY|
+                               (is_eos? CLIENTWRITE_EOS:0),
+                               (char *)buf, blen);
+  }
+
+out:
+  if((data->set.rtspreq == RTSPREQ_RECEIVE) &&
+     (rtspc->state == RTP_PARSE_SKIP)) {
+    /* In special mode RECEIVE, we just process one chunk of network
+     * data, so we stop the transfer here, if we have no incomplete
+     * RTP message pending. */
+    data->req.download_done = TRUE;
+  }
+  return result;
+}
+
 static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
 {
   size_t wrote;
   curl_write_callback writeit;
@@ -756,7 +888,7 @@
   }
 
   Curl_set_in_callback(data, true);
-  wrote = writeit(ptr, 1, len, user_ptr);
+  wrote = writeit((char *)ptr, 1, len, user_ptr);
   Curl_set_in_callback(data, false);
 
   if(CURL_WRITEFUNC_PAUSE == wrote) {
@@ -821,7 +953,7 @@
 
       /* If the Session ID is set, then compare */
       if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen ||
-         strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen) != 0) {
+         strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) {
         failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
               start, data->set.str[STRING_RTSP_SESSION_ID]);
         return CURLE_RTSP_SESSION_ERROR;
@@ -833,11 +965,9 @@
        */
 
       /* Copy the id substring into a new buffer */
-      data->set.str[STRING_RTSP_SESSION_ID] = malloc(idlen + 1);
+      data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen);
       if(!data->set.str[STRING_RTSP_SESSION_ID])
         return CURLE_OUT_OF_MEMORY;
-      memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, idlen);
-      (data->set.str[STRING_RTSP_SESSION_ID])[idlen] = '\0';
     }
   }
   else if(checkprefix("Transport:", header)) {
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h
index 111bac2..237b80f 100644
--- a/Utilities/cmcurl/lib/rtsp.h
+++ b/Utilities/cmcurl/lib/rtsp.h
@@ -39,6 +39,12 @@
 
 #endif /* CURL_DISABLE_RTSP */
 
+typedef enum {
+  RTP_PARSE_SKIP,
+  RTP_PARSE_CHANNEL,
+  RTP_PARSE_LEN,
+  RTP_PARSE_DATA
+} rtp_parse_st;
 /*
  * RTSP Connection data
  *
@@ -47,6 +53,9 @@
 struct rtsp_conn {
   struct dynbuf buf;
   int rtp_channel;
+  size_t rtp_len;
+  rtp_parse_st state;
+  BIT(in_header);
 };
 
 /****************************************************************************
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c
index cae9beb..d92e745 100644
--- a/Utilities/cmcurl/lib/select.c
+++ b/Utilities/cmcurl/lib/select.c
@@ -76,7 +76,7 @@
   }
 #if defined(MSDOS)
   delay(timeout_ms);
-#elif defined(WIN32)
+#elif defined(_WIN32)
   /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */
 #if TIMEDIFF_T_MAX >= ULONG_MAX
   if(timeout_ms >= ULONG_MAX)
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index 0482c5d..db3189a 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -50,6 +50,7 @@
 #include "strdup.h"
 #include "http2.h"
 #include "headers.h"
+#include "progress.h"
 #include "ws.h"
 
 /* The last 3 #include files should be in this order */
@@ -57,6 +58,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+static CURLcode do_init_stack(struct Curl_easy *data);
+
 #if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
 /*
  * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
@@ -292,13 +296,6 @@
   if(!skip_body_write &&
      ((type & CLIENTWRITE_BODY) ||
       ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
-#ifdef USE_WEBSOCKETS
-    if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
-      writebody = Curl_ws_writecb;
-      writebody_ptr = data;
-    }
-    else
-#endif
     writebody = data->set.fwrite_func;
   }
   if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
@@ -341,7 +338,7 @@
     len -= chunklen;
   }
 
-#ifndef CURL_DISABLE_HTTP
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
   /* HTTP header, but not status-line */
   if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
      (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
@@ -385,34 +382,36 @@
    the future to leave the original data alone.
  */
 CURLcode Curl_client_write(struct Curl_easy *data,
-                           int type,
-                           char *ptr,
-                           size_t len)
+                           int type, char *buf, size_t blen)
 {
+  CURLcode result;
+
 #if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
   /* FTP data may need conversion. */
   if((type & CLIENTWRITE_BODY) &&
      (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
      data->conn->proto.ftpc.transfertype == 'A') {
     /* convert end-of-line markers */
-    len = convert_lineends(data, ptr, len);
+    blen = convert_lineends(data, buf, blen);
   }
 #endif
   /* it is one of those, at least */
   DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
-  /* BODY is only BODY */
-  DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY));
-  /* INFO is only INFO */
-  DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
+  /* BODY is only BODY (with optional EOS) */
+  DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
+              ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
+  /* INFO is only INFO (with optional EOS) */
+  DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
+              ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
 
-  if(type == CLIENTWRITE_BODY) {
-    if(data->req.ignorebody)
-      return CURLE_OK;
-
-    if(data->req.writer_stack && !data->set.http_ce_skip)
-      return Curl_unencode_write(data, data->req.writer_stack, ptr, len);
+  if(!data->req.writer_stack) {
+    result = do_init_stack(data);
+    if(result)
+      return result;
+    DEBUGASSERT(data->req.writer_stack);
   }
-  return chop_write(data, type, FALSE, ptr, len);
+
+  return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
 }
 
 CURLcode Curl_client_unpause(struct Curl_easy *data)
@@ -449,12 +448,12 @@
 
 void Curl_client_cleanup(struct Curl_easy *data)
 {
-  struct contenc_writer *writer = data->req.writer_stack;
+  struct Curl_cwriter *writer = data->req.writer_stack;
   size_t i;
 
   while(writer) {
-    data->req.writer_stack = writer->downstream;
-    writer->handler->close_writer(data, writer);
+    data->req.writer_stack = writer->next;
+    writer->cwt->do_close(data, writer);
     free(writer);
     writer = data->req.writer_stack;
   }
@@ -463,61 +462,231 @@
     Curl_dyn_free(&data->state.tempwrite[i].b);
   }
   data->state.tempcount = 0;
-
+  data->req.bytecount = 0;
+  data->req.headerline = 0;
 }
 
-/* Real client writer: no downstream. */
-static CURLcode client_cew_init(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+/* Write data using an unencoding writer stack. "nbytes" is not
+   allowed to be 0. */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+                             struct Curl_cwriter *writer, int type,
+                             const char *buf, size_t nbytes)
 {
-  (void) data;
+  if(!writer)
+    return CURLE_WRITE_ERROR;
+  return writer->cwt->do_write(data, writer, type, buf, nbytes);
+}
+
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+                               struct Curl_cwriter *writer)
+{
+  (void)data;
   (void)writer;
   return CURLE_OK;
 }
 
-static CURLcode client_cew_write(struct Curl_easy *data,
-                                 struct contenc_writer *writer,
-                                 const char *buf, size_t nbytes)
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes)
 {
-  (void)writer;
-  if(!nbytes || data->req.ignorebody)
-    return CURLE_OK;
-  return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes);
+  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 }
 
-static void client_cew_close(struct Curl_easy *data,
-                             struct contenc_writer *writer)
+void Curl_cwriter_def_close(struct Curl_easy *data,
+                            struct Curl_cwriter *writer)
 {
   (void) data;
   (void) writer;
 }
 
-static const struct content_encoding client_cew = {
+/* Real client writer to installed callbacks. */
+static CURLcode cw_client_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes)
+{
+  (void)writer;
+  if(!nbytes)
+    return CURLE_OK;
+  return chop_write(data, type, FALSE, (char *)buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_client = {
+  "client",
   NULL,
+  Curl_cwriter_def_init,
+  cw_client_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
+};
+
+static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
+{
+  if(limit != -1) {
+    /* How much more are we allowed to write? */
+    curl_off_t remain_diff;
+    remain_diff = limit - data->req.bytecount;
+    if(remain_diff < 0) {
+      /* already written too much! */
+      return 0;
+    }
+#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
+    else if(remain_diff > SSIZE_T_MAX) {
+      return SIZE_T_MAX;
+    }
+#endif
+    else {
+      return (size_t)remain_diff;
+    }
+  }
+  return SIZE_T_MAX;
+}
+
+/* Download client writer in phase CURL_CW_PROTOCOL that
+ * sees the "real" download body data. */
+static CURLcode cw_download_write(struct Curl_easy *data,
+                                  struct Curl_cwriter *writer, int type,
+                                  const char *buf, size_t nbytes)
+{
+  CURLcode result;
+  size_t nwrite, excess_len = 0;
+
+  if(!(type & CLIENTWRITE_BODY)) {
+    if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
+      return CURLE_OK;
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+  }
+
+  if(!data->req.bytecount) {
+    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+    if(data->req.exp100 > EXP100_SEND_DATA)
+      /* set time stamp to compare with when waiting for the 100 */
+      data->req.start100 = Curl_now();
+  }
+
+  /* Here, we deal with REAL BODY bytes. All filtering and transfer
+   * encodings have been applied and only the true content, e.g. BODY,
+   * bytes are passed here.
+   * This allows us to check sizes, update stats, etc. independent
+   * from the protocol in play. */
+
+  if(data->req.no_body && nbytes > 0) {
+    /* BODY arrives although we want none, bail out */
+    streamclose(data->conn, "ignoring body");
+    DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
+                 nbytes));
+    data->req.download_done = TRUE;
+    return CURLE_WEIRD_SERVER_REPLY;
+  }
+
+  /* Determine if we see any bytes in excess to what is allowed.
+   * We write the allowed bytes and handle excess further below.
+   * This gives deterministic BODY writes on varying buffer receive
+   * lengths. */
+  nwrite = nbytes;
+  if(-1 != data->req.maxdownload) {
+    size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
+    if(nwrite > wmax) {
+      excess_len = nbytes - wmax;
+      nwrite = wmax;
+    }
+
+    if(nwrite == wmax) {
+      data->req.download_done = TRUE;
+    }
+  }
+
+  /* Error on too large filesize is handled below, after writing
+   * the permitted bytes */
+  if(data->set.max_filesize) {
+    size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
+    if(nwrite > wmax) {
+      nwrite = wmax;
+    }
+  }
+
+  /* Update stats, write and report progress */
+  data->req.bytecount += nwrite;
+  ++data->req.bodywrites;
+  if(!data->req.ignorebody && nwrite) {
+    result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
+    if(result)
+      return result;
+  }
+  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+  if(result)
+    return result;
+
+  if(excess_len) {
+    if(!data->req.ignorebody) {
+      infof(data,
+            "Excess found writing body:"
+            " excess = %zu"
+            ", size = %" CURL_FORMAT_CURL_OFF_T
+            ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+            ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
+            excess_len, data->req.size, data->req.maxdownload,
+            data->req.bytecount);
+      connclose(data->conn, "excess found in a read");
+    }
+  }
+  else if(nwrite < nbytes) {
+    failf(data, "Exceeded the maximum allowed file size "
+          "(%" CURL_FORMAT_CURL_OFF_T ") with %"
+          CURL_FORMAT_CURL_OFF_T " bytes",
+          data->set.max_filesize, data->req.bytecount);
+    return CURLE_FILESIZE_EXCEEDED;
+  }
+
+  return CURLE_OK;
+}
+
+static const struct Curl_cwtype cw_download = {
+  "download",
   NULL,
-  client_cew_init,
-  client_cew_write,
-  client_cew_close,
-  sizeof(struct contenc_writer)
+  Curl_cwriter_def_init,
+  cw_download_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
+};
+
+/* RAW client writer in phase CURL_CW_RAW that
+ * enabled tracing of raw data. */
+static CURLcode cw_raw_write(struct Curl_easy *data,
+                             struct Curl_cwriter *writer, int type,
+                             const char *buf, size_t nbytes)
+{
+  if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
+    Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
+  }
+  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_raw = {
+  "raw",
+  NULL,
+  Curl_cwriter_def_init,
+  cw_raw_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
 };
 
 /* Create an unencoding writer stage using the given handler. */
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
                                    struct Curl_easy *data,
-                                   const struct content_encoding *ce_handler,
-                                   int order)
+                                   const struct Curl_cwtype *cwt,
+                                   Curl_cwriter_phase phase)
 {
-  struct contenc_writer *writer;
+  struct Curl_cwriter *writer;
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
-  DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer));
-  writer = (struct contenc_writer *) calloc(1, ce_handler->writersize);
+  DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
+  writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
   if(!writer)
     goto out;
 
-  writer->handler = ce_handler;
-  writer->order = order;
-  result = ce_handler->init_writer(data, writer);
+  writer->cwt = cwt;
+  writer->phase = phase;
+  result = cwt->do_init(data, writer);
 
 out:
   *pwriter = result? NULL : writer;
@@ -526,58 +695,92 @@
   return result;
 }
 
-void Curl_client_free_writer(struct Curl_easy *data,
-                             struct contenc_writer *writer)
+void Curl_cwriter_free(struct Curl_easy *data,
+                             struct Curl_cwriter *writer)
 {
   if(writer) {
-    writer->handler->close_writer(data, writer);
+    writer->cwt->do_close(data, writer);
     free(writer);
   }
 }
 
-/* allow no more than 5 "chained" compression steps */
-#define MAX_ENCODE_STACK 5
-
-
-static CURLcode init_writer_stack(struct Curl_easy *data)
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
 {
-  DEBUGASSERT(!data->req.writer_stack);
-  return Curl_client_create_writer(&data->req.writer_stack,
-                                   data, &client_cew, 0);
+  struct Curl_cwriter *w;
+  size_t n = 0;
+
+  for(w = data->req.writer_stack; w; w = w->next) {
+    if(w->phase == phase)
+      ++n;
+  }
+  return n;
 }
 
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+static CURLcode do_init_stack(struct Curl_easy *data)
 {
+  struct Curl_cwriter *writer;
   CURLcode result;
 
-  if(!data->req.writer_stack) {
-    result = init_writer_stack(data);
+  DEBUGASSERT(!data->req.writer_stack);
+  result = Curl_cwriter_create(&data->req.writer_stack,
+                               data, &cw_client, CURL_CW_CLIENT);
+  if(result)
+    return result;
+
+  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+  if(result)
+    return result;
+  result = Curl_cwriter_add(data, writer);
+  if(result) {
+    Curl_cwriter_free(data, writer);
+  }
+
+  result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
+  if(result)
+    return result;
+  result = Curl_cwriter_add(data, writer);
+  if(result) {
+    Curl_cwriter_free(data, writer);
+  }
+  return result;
+}
+
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+                          struct Curl_cwriter *writer)
+{
+  CURLcode result;
+  struct Curl_cwriter **anchor = &data->req.writer_stack;
+
+  if(!*anchor) {
+    result = do_init_stack(data);
     if(result)
       return result;
   }
 
-  if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) {
-    failf(data, "Reject response due to more than %u content encodings",
-          MAX_ENCODE_STACK);
-    return CURLE_BAD_CONTENT_ENCODING;
-  }
-
-  /* Stack the unencoding stage. */
-  if(writer->order >= data->req.writer_stack->order) {
-    writer->downstream = data->req.writer_stack;
-    data->req.writer_stack = writer;
-  }
-  else {
-    struct contenc_writer *w = data->req.writer_stack;
-    while(w->downstream && writer->order < w->downstream->order)
-      w = w->downstream;
-    writer->downstream = w->downstream;
-    w->downstream = writer;
-  }
+  /* Insert the writer as first in its phase.
+   * Skip existing writers of lower phases. */
+  while(*anchor && (*anchor)->phase < writer->phase)
+    anchor = &((*anchor)->next);
+  writer->next = *anchor;
+  *anchor = writer;
   return CURLE_OK;
 }
 
+void Curl_cwriter_remove_by_name(struct Curl_easy *data,
+                                 const char *name)
+{
+  struct Curl_cwriter **anchor = &data->req.writer_stack;
+
+  while(*anchor) {
+    if(!strcmp(name, (*anchor)->cwt->name)) {
+      struct Curl_cwriter *w = (*anchor);
+      *anchor = w->next;
+      Curl_cwriter_free(data, w);
+      continue;
+    }
+    anchor = &((*anchor)->next);
+  }
+}
 
 /*
  * Internal read-from-socket function. This is meant to deal with plain
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h
index 9ee00bb..7deae2a 100644
--- a/Utilities/cmcurl/lib/sendf.h
+++ b/Utilities/cmcurl/lib/sendf.h
@@ -49,44 +49,127 @@
 #define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */
 #define CLIENTWRITE_1XX     (1<<5) /* a 1xx response related HEADER */
 #define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
+#define CLIENTWRITE_EOS     (1<<7) /* End Of transfer download Stream */
 
+/**
+ * Write `len` bytes at `prt` to the client. `type` indicates what
+ * kind of data is being written.
+ */
 CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
                            size_t len) WARN_UNUSED_RESULT;
 
+/**
+ * For a paused transfer, there might be buffered data held back.
+ * Attempt to flush this data to the client. This *may* trigger
+ * another pause of the transfer.
+ */
 CURLcode Curl_client_unpause(struct Curl_easy *data);
+
+/**
+ * Free all resources related to client writing.
+ */
 void Curl_client_cleanup(struct Curl_easy *data);
 
-struct contenc_writer {
-  const struct content_encoding *handler;  /* Encoding handler. */
-  struct contenc_writer *downstream;  /* Downstream writer. */
-  unsigned int order; /* Ordering within writer stack. */
+/**
+ * Client Writers - a chain passing transfer BODY data to the client.
+ * Main application: HTTP and related protocols
+ * Other uses: monitoring of download progress
+ *
+ * Writers in the chain are order by their `phase`. First come all
+ * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE,
+ * followed by any in CURL_CW_PROTOCOL, etc.
+ *
+ * When adding a writer, it is inserted as first in its phase. This means
+ * the order of adding writers of the same phase matters, but writers for
+ * different phases may be added in any order.
+ *
+ * Writers which do modify the BODY data written are expected to be of
+ * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended
+ * for monitoring writers. Which do *not* modify the data but gather
+ * statistics or update progress reporting.
+ */
+
+/* Phase a writer operates at. */
+typedef enum {
+  CURL_CW_RAW,  /* raw data written, before any decoding */
+  CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
+  CURL_CW_PROTOCOL, /* after transfer, but before content decoding */
+  CURL_CW_CONTENT_DECODE, /* remove content-encodings */
+  CURL_CW_CLIENT  /* data written to client */
+} Curl_cwriter_phase;
+
+/* Client Writer Type, provides the implementation */
+struct Curl_cwtype {
+  const char *name;        /* writer name. */
+  const char *alias;       /* writer name alias, maybe NULL. */
+  CURLcode (*do_init)(struct Curl_easy *data,
+                      struct Curl_cwriter *writer);
+  CURLcode (*do_write)(struct Curl_easy *data,
+                       struct Curl_cwriter *writer, int type,
+                       const char *buf, size_t nbytes);
+  void (*do_close)(struct Curl_easy *data,
+                   struct Curl_cwriter *writer);
+  size_t cwriter_size;  /* sizeof() allocated struct Curl_cwriter */
 };
 
-/* Content encoding writer. */
-struct content_encoding {
-  const char *name;        /* Encoding name. */
-  const char *alias;       /* Encoding name alias. */
-  CURLcode (*init_writer)(struct Curl_easy *data,
-                          struct contenc_writer *writer);
-  CURLcode (*unencode_write)(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes);
-  void (*close_writer)(struct Curl_easy *data,
-                       struct contenc_writer *writer);
-  size_t writersize;
+/* Client writer instance */
+struct Curl_cwriter {
+  const struct Curl_cwtype *cwt;  /* type implementation */
+  struct Curl_cwriter *next;  /* Downstream writer. */
+  Curl_cwriter_phase phase; /* phase at which it operates */
 };
 
+/**
+ * Create a new cwriter instance with given type and phase. Is not
+ * inserted into the writer chain by this call.
+ * Invokes `writer->do_init()`.
+ */
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
+                             struct Curl_easy *data,
+                             const struct Curl_cwtype *ce_handler,
+                             Curl_cwriter_phase phase);
 
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
-                                   struct Curl_easy *data,
-                                   const struct content_encoding *ce_handler,
-                                   int order);
+/**
+ * Free a cwriter instance.
+ * Invokes `writer->do_close()`.
+ */
+void Curl_cwriter_free(struct Curl_easy *data,
+                       struct Curl_cwriter *writer);
 
-void Curl_client_free_writer(struct Curl_easy *data,
-                             struct contenc_writer *writer);
+/**
+ * Count the number of writers installed of the given phase.
+ */
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
 
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer);
+/**
+ * Adds a writer to the transfer's writer chain.
+ * The writers `phase` determines where in the chain it is inserted.
+ */
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+                          struct Curl_cwriter *writer);
+
+void Curl_cwriter_remove_by_name(struct Curl_easy *data,
+                                 const char *name);
+
+/**
+ * Convenience method for calling `writer->do_write()` that
+ * checks for NULL writer.
+ */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+                            struct Curl_cwriter *writer, int type,
+                            const char *buf, size_t nbytes);
+
+/**
+ * Default implementations for do_init, do_write, do_close that
+ * do nothing and pass the data through.
+ */
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+                               struct Curl_cwriter *writer);
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes);
+void Curl_cwriter_def_close(struct Curl_easy *data,
+                            struct Curl_cwriter *writer);
 
 
 /* internal read-function, does plain socket, SSL and krb4 */
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index 0d399ad..a527077 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -50,7 +50,8 @@
 #include "multiif.h"
 #include "altsvc.h"
 #include "hsts.h"
-
+#include "tftp.h"
+#include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -171,7 +172,7 @@
     str = strchr(str, ',');
     tlen = str? (size_t) (str - token): strlen(token);
     if(tlen) {
-      const struct Curl_handler *h = Curl_builtin_scheme(token, tlen);
+      const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen);
 
       if(!h)
         return CURLE_UNSUPPORTED_PROTOCOL;
@@ -261,43 +262,43 @@
      * Set the absolute number of maximum simultaneous alive connection that
      * libcurl is allowed to have.
      */
-    arg = va_arg(param, long);
-    if(arg < 0)
+    uarg = va_arg(param, unsigned long);
+    if(uarg > UINT_MAX)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.maxconnects = arg;
+    data->set.maxconnects = (unsigned int)uarg;
     break;
   case CURLOPT_FORBID_REUSE:
     /*
      * When this transfer is done, it must not be left to be reused by a
      * subsequent transfer but shall be closed immediately.
      */
-    data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.reuse_forbid = (0 != va_arg(param, long));
     break;
   case CURLOPT_FRESH_CONNECT:
     /*
      * This transfer shall not use a previously cached connection but
      * should be made with a fresh new connect!
      */
-    data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.reuse_fresh = (0 != va_arg(param, long));
     break;
   case CURLOPT_VERBOSE:
     /*
      * Verbose means infof() calls that give a lot of information about
      * the connection and transfer procedures as well as internal choices.
      */
-    data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.verbose = (0 != va_arg(param, long));
     break;
   case CURLOPT_HEADER:
     /*
      * Set to include the header in the general data output stream.
      */
-    data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.include_header = (0 != va_arg(param, long));
     break;
   case CURLOPT_NOPROGRESS:
     /*
      * Shut off the internal supported progress meter
      */
-    data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.hide_progress = (0 != va_arg(param, long));
     if(data->set.hide_progress)
       data->progress.flags |= PGRS_HIDE;
     else
@@ -307,7 +308,7 @@
     /*
      * Do not include the body part in the output data stream.
      */
-    data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.opt_no_body = (0 != va_arg(param, long));
 #ifndef CURL_DISABLE_HTTP
     if(data->set.opt_no_body)
       /* in HTTP lingo, no body means using the HEAD request... */
@@ -321,11 +322,10 @@
      * Don't output the >=400 error code HTML-page, but instead only
      * return error.
      */
-    data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_fail_on_error = (0 != va_arg(param, long));
     break;
   case CURLOPT_KEEP_SENDING_ON_ERROR:
-    data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.http_keep_sending_on_error = (0 != va_arg(param, long));
     break;
   case CURLOPT_UPLOAD:
   case CURLOPT_PUT:
@@ -353,7 +353,7 @@
      * Try to get the file time of the remote document. The time will
      * later (possibly) become available using curl_easy_getinfo().
      */
-    data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.get_filetime = (0 != va_arg(param, long));
     break;
   case CURLOPT_SERVER_RESPONSE_TIMEOUT:
     /*
@@ -366,6 +366,17 @@
     else
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
+  case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS:
+    /*
+     * Option that specifies how quickly a server response must be obtained
+     * before it is considered failure. For pingpong protocols.
+     */
+    arg = va_arg(param, long);
+    if((arg >= 0) && (arg <= INT_MAX))
+      data->set.server_response_timeout = (unsigned int)arg;
+    else
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
 #ifndef CURL_DISABLE_TFTP
   case CURLOPT_TFTP_NO_OPTIONS:
     /*
@@ -379,7 +390,7 @@
      * TFTP option that specifies the block size to use for data transmission.
      */
     arg = va_arg(param, long);
-    if(arg < 0)
+    if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     data->set.tftp_blksize = arg;
     break;
@@ -409,7 +420,7 @@
      *
      * Transfer using ASCII (instead of BINARY).
      */
-    data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.prefer_ascii = (0 != va_arg(param, long));
     break;
   case CURLOPT_TIMECONDITION:
     /*
@@ -497,26 +508,17 @@
           (data->set.postfieldsize > (curl_off_t)((size_t)-1))))
         result = CURLE_OUT_OF_MEMORY;
       else {
-        char *p;
-
-        (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
-
         /* Allocate even when size == 0. This satisfies the need of possible
-           later address compare to detect the COPYPOSTFIELDS mode, and
-           to mark that postfields is used rather than read function or
-           form data.
+           later address compare to detect the COPYPOSTFIELDS mode, and to
+           mark that postfields is used rather than read function or form
+           data.
         */
-        p = malloc((size_t)(data->set.postfieldsize?
-                            data->set.postfieldsize:1));
-
+        char *p = Curl_memdup0(argptr, (size_t)data->set.postfieldsize);
+        (void) Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL);
         if(!p)
           result = CURLE_OUT_OF_MEMORY;
-        else {
-          if(data->set.postfieldsize)
-            memcpy(p, argptr, (size_t)data->set.postfieldsize);
-
+        else
           data->set.str[STRING_COPYPOSTFIELDS] = p;
-        }
       }
     }
 
@@ -577,7 +579,7 @@
     /*
      * Switch on automatic referer that gets set if curl follows locations.
      */
-    data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_auto_referer = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_ACCEPT_ENCODING:
@@ -592,28 +594,23 @@
      */
     argptr = va_arg(param, char *);
     if(argptr && !*argptr) {
-      argptr = Curl_all_content_encodings();
-      if(!argptr)
-        result = CURLE_OUT_OF_MEMORY;
-      else {
-        result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
-        free(argptr);
-      }
+      char all[256];
+      Curl_all_content_encodings(all, sizeof(all));
+      result = Curl_setstropt(&data->set.str[STRING_ENCODING], all);
     }
     else
       result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
     break;
 
   case CURLOPT_TRANSFER_ENCODING:
-    data->set.http_transfer_encoding = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.http_transfer_encoding = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FOLLOWLOCATION:
     /*
      * Follow Location: header hints on an HTTP-server.
      */
-    data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_follow_location = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_UNRESTRICTED_AUTH:
@@ -621,8 +618,7 @@
      * Send authentication (user+password) when following locations, even when
      * hostname changed.
      */
-    data->set.allow_auth_to_other_hosts =
-      (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_MAXREDIRS:
@@ -676,6 +672,7 @@
     data->set.opt_no_body = FALSE; /* this is implied */
     Curl_mime_cleanpart(data->state.formp);
     Curl_safefree(data->state.formp);
+    data->state.mimepost = NULL;
     break;
 #endif
 
@@ -736,7 +733,7 @@
      * Set header option.
      */
     arg = va_arg(param, long);
-    data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
+    data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE);
     break;
 
 #if !defined(CURL_DISABLE_COOKIES)
@@ -760,18 +757,18 @@
         return CURLE_BAD_FUNCTION_ARGUMENT;
       /* append the cookie file name to the list of file names, and deal with
          them later */
-      cl = curl_slist_append(data->set.cookielist, argptr);
+      cl = curl_slist_append(data->state.cookielist, argptr);
       if(!cl) {
-        curl_slist_free_all(data->set.cookielist);
-        data->set.cookielist = NULL;
+        curl_slist_free_all(data->state.cookielist);
+        data->state.cookielist = NULL;
         return CURLE_OUT_OF_MEMORY;
       }
-      data->set.cookielist = cl; /* store the list for later use */
+      data->state.cookielist = cl; /* store the list for later use */
     }
     else {
       /* clear the list of cookie files */
-      curl_slist_free_all(data->set.cookielist);
-      data->set.cookielist = NULL;
+      curl_slist_free_all(data->state.cookielist);
+      data->state.cookielist = NULL;
 
       if(!data->share || !data->share->cookies) {
         /* throw away all existing cookies if this isn't a shared cookie
@@ -811,17 +808,8 @@
      * prevent the forthcoming read-cookies-from-file actions to accept
      * cookies that are marked as being session cookies, as they belong to a
      * previous session.
-     *
-     * In the original Netscape cookie spec, "session cookies" are cookies
-     * with no expire date set. RFC2109 describes the same action if no
-     * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
-     * a 'Discard' action that can enforce the discard even for cookies that
-     * have a Max-Age.
-     *
-     * We run mostly with the original cookie spec, as hardly anyone implements
-     * anything else.
      */
-    data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.cookiesession = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_COOKIELIST:
@@ -956,7 +944,7 @@
     if(arg)
       return CURLE_BAD_FUNCTION_ARGUMENT;
 #else
-    data->set.http09_allowed = arg ? TRUE : FALSE;
+    data->set.http09_allowed = !!arg;
 #endif
     break;
 
@@ -992,13 +980,15 @@
 #ifndef CURL_DISABLE_FORM_API
       Curl_mime_cleanpart(data->state.formp);
       Curl_safefree(data->state.formp);
+      data->state.mimepost = NULL;
 #endif
     }
     break;
 
   case CURLOPT_MIME_OPTIONS:
-    data->set.mime_options = (unsigned int)va_arg(param, long);
-    break;
+    arg = va_arg(param, long);
+    data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE);
+  break;
 # endif
 #endif
 
@@ -1018,8 +1008,7 @@
 
     /* the DIGEST_IE bit is only used to set a special marker, for all the
        rest we need to handle it as normal DIGEST */
-    data->state.authhost.iestyle =
-      (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+    data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE);
 
     if(auth & CURLAUTH_DIGEST_IE) {
       auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -1072,8 +1061,7 @@
     /*
      * Tunnel operations through the proxy instead of normal proxy use
      */
-    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_PROXYPORT:
@@ -1102,8 +1090,7 @@
 
     /* the DIGEST_IE bit is only used to set a special marker, for all the
        rest we need to handle it as normal DIGEST */
-    data->state.authproxy.iestyle =
-      (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+    data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE);
 
     if(auth & CURLAUTH_DIGEST_IE) {
       auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -1203,7 +1190,7 @@
     /*
      * Set flag for NEC SOCK5 support
      */
-    data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.socks5_gssapi_nec = (0 != va_arg(param, long));
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1251,7 +1238,7 @@
      * An option that changes the command to one that asks for a list only, no
      * file info details. Used for FTP, POP3 and SFTP.
      */
-    data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.list_only = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_APPEND:
@@ -1259,7 +1246,7 @@
      * We want to upload and append to an existing file. Used for FTP and
      * SFTP.
      */
-    data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.remote_append = (0 != va_arg(param, long));
     break;
 
 #ifndef CURL_DISABLE_FTP
@@ -1270,7 +1257,7 @@
     arg = va_arg(param, long);
     if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg;
+    data->set.ftp_filemethod = (unsigned char)arg;
     break;
   case CURLOPT_FTPPORT:
     /*
@@ -1278,26 +1265,26 @@
      */
     result = Curl_setstropt(&data->set.str[STRING_FTPPORT],
                             va_arg(param, char *));
-    data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE;
+    data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]);
     break;
 
   case CURLOPT_FTP_USE_EPRT:
-    data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_eprt = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_USE_EPSV:
-    data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_epsv = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_USE_PRET:
-    data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_pret = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_SSL_CCC:
     arg = va_arg(param, long);
     if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg;
+    data->set.ftp_ccc = (unsigned char)arg;
     break;
 
   case CURLOPT_FTP_SKIP_PASV_IP:
@@ -1305,7 +1292,7 @@
      * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
      * bypass of the IP address in PASV responses.
      */
-    data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_skip_ip = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_ACCOUNT:
@@ -1333,7 +1320,7 @@
      */
     result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL],
                             va_arg(param, char *));
-    data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
+    data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]);
     break;
 #endif
 #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@@ -1867,14 +1854,14 @@
     /*
      * Kludgy option to enable CRLF conversions. Subject for removal.
      */
-    data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.crlf = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_HAPROXYPROTOCOL:
     /*
      * Set to send the HAProxy Proxy Protocol header
      */
-    data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.haproxyprotocol = (0 != va_arg(param, long));
     break;
   case CURLOPT_HAPROXY_CLIENT_IP:
     /*
@@ -1926,22 +1913,17 @@
     /*
      * Enable peer SSL verifying.
      */
-    data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.verifypeer = (0 != va_arg(param, long));
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifypeer =
-        data->set.ssl.primary.verifypeer;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYPEER:
     /*
      * Enable peer SSL verifying for DoH.
      */
-    data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.doh_verifypeer = (0 != va_arg(param, long));
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1953,10 +1935,7 @@
       (0 != va_arg(param, long))?TRUE:FALSE;
 
     /* Update the current connection proxy_ssl_config. */
-    if(data->conn) {
-      data->conn->proxy_ssl_config.verifypeer =
-        data->set.proxy_ssl.primary.verifypeer;
-    }
+    Curl_ssl_conn_config_update(data, TRUE);
     break;
 #endif
   case CURLOPT_SSL_VERIFYHOST:
@@ -1968,13 +1947,10 @@
     /* Obviously people are not reading documentation and too many thought
        this argument took a boolean when it wasn't and misused it.
        Treat 1 and 2 the same */
-    data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+    data->set.ssl.primary.verifyhost = !!(arg & 3);
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifyhost =
-        data->set.ssl.primary.verifyhost;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYHOST:
@@ -1984,7 +1960,7 @@
     arg = va_arg(param, long);
 
     /* Treat both 1 and 2 as TRUE */
-    data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+    data->set.doh_verifyhost = !!(arg & 3);
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1996,12 +1972,8 @@
 
     /* Treat both 1 and 2 as TRUE */
     data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE);
-
     /* Update the current connection proxy_ssl_config. */
-    if(data->conn) {
-      data->conn->proxy_ssl_config.verifyhost =
-        data->set.proxy_ssl.primary.verifyhost;
-    }
+    Curl_ssl_conn_config_update(data, TRUE);
     break;
 #endif
   case CURLOPT_SSL_VERIFYSTATUS:
@@ -2013,14 +1985,10 @@
       break;
     }
 
-    data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.verifystatus = (0 != va_arg(param, long));
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifystatus =
-        data->set.ssl.primary.verifystatus;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYSTATUS:
@@ -2032,8 +2000,7 @@
       break;
     }
 
-    data->set.doh_verifystatus = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.doh_verifystatus = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_SSL_CTX_FUNCTION:
@@ -2067,12 +2034,12 @@
       break;
     }
 
-    data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ssl.falsestart = (0 != va_arg(param, long));
     break;
   case CURLOPT_CERTINFO:
 #ifdef USE_SSL
     if(Curl_ssl_supports(data, SSLSUPP_CERTINFO))
-      data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE;
+      data->set.ssl.certinfo = (0 != va_arg(param, long));
     else
 #endif
       result = CURLE_NOT_BUILT_IN;
@@ -2118,14 +2085,14 @@
      * Specify entire PEM of the CA certificate
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
       result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO],
                                va_arg(param, struct curl_blob *));
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-
-    break;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_CAINFO:
     /*
@@ -2141,13 +2108,14 @@
      * Specify entire PEM of the CA certificate
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
       result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY],
                                va_arg(param, struct curl_blob *));
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-    break;
 #endif
   case CURLOPT_CAPATH:
     /*
@@ -2278,7 +2246,7 @@
      * The application asks not to set any signal() or alarm() handlers,
      * even when using a timeout.
      */
-    data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.no_signal = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_SHARE:
@@ -2453,11 +2421,11 @@
      * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
      * algorithm
      */
-    data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.tcp_nodelay = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_IGNORE_CONTENT_LENGTH:
-    data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ignorecl = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_CONNECT_ONLY:
@@ -2532,8 +2500,7 @@
     break;
 
   case CURLOPT_SSL_SESSIONID_CACHE:
-    data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.sessionid = (0 != va_arg(param, long));
 #ifndef CURL_DISABLE_PROXY
     data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid;
 #endif
@@ -2622,7 +2589,7 @@
      * disable libcurl transfer encoding is used
      */
 #ifndef USE_HYPER
-    data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_te_skip = (0 == va_arg(param, long));
     break;
 #else
     return CURLE_NOT_BUILT_IN; /* hyper doesn't support */
@@ -2632,7 +2599,7 @@
     /*
      * raw data passed to the application when content encoding is used
      */
-    data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_ce_skip = (0 == va_arg(param, long));
     break;
 
 #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@@ -2733,7 +2700,7 @@
     break;
   case CURLOPT_MAIL_RCPT_ALLOWFAILS:
     /* allow RCPT TO command to fail for some recipients */
-    data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.mail_rcpt_allowfails = (0 != va_arg(param, long));
     break;
 #endif
 
@@ -2745,7 +2712,7 @@
 
   case CURLOPT_SASL_IR:
     /* Enable/disable SASL initial response */
-    data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.sasl_ir = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_RTSP
   case CURLOPT_RTSP_REQUEST:
@@ -2859,7 +2826,7 @@
 #endif
 #ifndef CURL_DISABLE_FTP
   case CURLOPT_WILDCARDMATCH:
-    data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.wildcard_enabled = (0 != va_arg(param, long));
     break;
   case CURLOPT_CHUNK_BGN_FUNCTION:
     data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
@@ -2942,7 +2909,7 @@
     break;
 #endif
   case CURLOPT_TCP_KEEPALIVE:
-    data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.tcp_keepalive = (0 != va_arg(param, long));
     break;
   case CURLOPT_TCP_KEEPIDLE:
     arg = va_arg(param, long);
@@ -2971,7 +2938,7 @@
   case CURLOPT_SSL_ENABLE_NPN:
     break;
   case CURLOPT_SSL_ENABLE_ALPN:
-    data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ssl_enable_alpn = (0 != va_arg(param, long));
     break;
 #ifdef USE_UNIX_SOCKETS
   case CURLOPT_UNIX_SOCKET_PATH:
@@ -2987,10 +2954,10 @@
 #endif
 
   case CURLOPT_PATH_AS_IS:
-    data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.path_as_is = (0 != va_arg(param, long));
     break;
   case CURLOPT_PIPEWAIT:
-    data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.pipewait = (0 != va_arg(param, long));
     break;
   case CURLOPT_STREAM_WEIGHT:
 #if defined(USE_HTTP2) || defined(USE_HTTP3)
@@ -3025,12 +2992,11 @@
     break;
 #ifndef CURL_DISABLE_SHUFFLE_DNS
   case CURLOPT_DNS_SHUFFLE_ADDRESSES:
-    data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE;
+    data->set.dns_shuffle_addresses = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_DISALLOW_USERNAME_IN_URL:
-    data->set.disallow_username_in_url =
-      (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.disallow_username_in_url = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_URL:
@@ -3095,18 +3061,18 @@
       /* this needs to build a list of file names to read from, so that it can
          read them later, as we might get a shared HSTS handle to load them
          into */
-      h = curl_slist_append(data->set.hstslist, argptr);
+      h = curl_slist_append(data->state.hstslist, argptr);
       if(!h) {
-        curl_slist_free_all(data->set.hstslist);
-        data->set.hstslist = NULL;
+        curl_slist_free_all(data->state.hstslist);
+        data->state.hstslist = NULL;
         return CURLE_OUT_OF_MEMORY;
       }
-      data->set.hstslist = h; /* store the list for later use */
+      data->state.hstslist = h; /* store the list for later use */
     }
     else {
       /* clear the list of HSTS files */
-      curl_slist_free_all(data->set.hstslist);
-      data->set.hstslist = NULL;
+      curl_slist_free_all(data->state.hstslist);
+      data->state.hstslist = NULL;
       if(!data->share || !data->share->hsts)
         /* throw away the HSTS cache unless shared */
         Curl_hsts_cleanup(&data->hsts);
@@ -3147,6 +3113,10 @@
         return CURLE_OUT_OF_MEMORY;
     }
     arg = va_arg(param, long);
+    if(!arg) {
+      DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input"));
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
     result = Curl_altsvc_ctrl(data->asi, arg);
     if(result)
       return result;
@@ -3201,5 +3171,9 @@
   result = Curl_vsetopt(data, tag, arg);
 
   va_end(arg);
+#ifdef DEBUGBUILD
+  if(result == CURLE_BAD_FUNCTION_ARGUMENT)
+    infof(data, "setopt arg 0x%x returned CURLE_BAD_FUNCTION_ARGUMENT", tag);
+#endif
   return result;
 }
diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h
index 1394838..d7e2e6b 100644
--- a/Utilities/cmcurl/lib/setup-win32.h
+++ b/Utilities/cmcurl/lib/setup-win32.h
@@ -24,18 +24,53 @@
  *
  ***************************************************************************/
 
+#undef USE_WINSOCK
+/* ---------------------------------------------------------------- */
+/*                     Watt-32 TCP/IP SPECIFIC                      */
+/* ---------------------------------------------------------------- */
+#ifdef USE_WATT32
+#  include <tcp.h>
+#  undef byte
+#  undef word
+#  define HAVE_SYS_IOCTL_H
+#  define HAVE_SYS_SOCKET_H
+#  define HAVE_NETINET_IN_H
+#  define HAVE_NETDB_H
+#  define HAVE_ARPA_INET_H
+#  define SOCKET int
+/* ---------------------------------------------------------------- */
+/*               BSD-style lwIP TCP/IP stack SPECIFIC               */
+/* ---------------------------------------------------------------- */
+#elif defined(USE_LWIPSOCK)
+  /* Define to use BSD-style lwIP TCP/IP stack. */
+  /* #define USE_LWIPSOCK 1 */
+#  undef HAVE_GETHOSTNAME
+#  undef LWIP_POSIX_SOCKETS_IO_NAMES
+#  undef RECV_TYPE_ARG1
+#  undef RECV_TYPE_ARG3
+#  undef SEND_TYPE_ARG1
+#  undef SEND_TYPE_ARG3
+#  define HAVE_GETHOSTBYNAME_R
+#  define HAVE_GETHOSTBYNAME_R_6
+#  define LWIP_POSIX_SOCKETS_IO_NAMES 0
+#  define RECV_TYPE_ARG1 int
+#  define RECV_TYPE_ARG3 size_t
+#  define SEND_TYPE_ARG1 int
+#  define SEND_TYPE_ARG3 size_t
+#elif defined(_WIN32)
+#  define USE_WINSOCK 2
+#endif
+
 /*
  * Include header files for windows builds before redefining anything.
  * Use this preprocessor block only to include or exclude windows.h,
  * winsock2.h or ws2tcpip.h. Any other windows thing belongs
  * to any other further and independent block.  Under Cygwin things work
  * just as under linux (e.g. <sys/socket.h>) and the winsock headers should
- * never be included when __CYGWIN__ is defined.  configure script takes
- * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H,
- * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined.
+ * never be included when __CYGWIN__ is defined.
  */
 
-#ifdef HAVE_WINDOWS_H
+#ifdef _WIN32
 #  if defined(UNICODE) && !defined(_UNICODE)
 #    error "UNICODE is defined but _UNICODE is not defined"
 #  endif
@@ -53,14 +88,10 @@
 #  ifndef NOGDI
 #    define NOGDI
 #  endif
-#  include <winerror.h>
+#  include <winsock2.h>
+#  include <ws2tcpip.h>
 #  include <windows.h>
-#  ifdef HAVE_WINSOCK2_H
-#    include <winsock2.h>
-#    ifdef HAVE_WS2TCPIP_H
-#      include <ws2tcpip.h>
-#    endif
-#  endif
+#  include <winerror.h>
 #  include <tchar.h>
 #  ifdef UNICODE
      typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str);
@@ -68,17 +99,6 @@
 #endif
 
 /*
- * Define USE_WINSOCK to 2 if we have and use WINSOCK2 API, else
- * undefine USE_WINSOCK.
- */
-
-#undef USE_WINSOCK
-
-#ifdef HAVE_WINSOCK2_H
-#  define USE_WINSOCK 2
-#endif
-
-/*
  * Define _WIN32_WINNT_[OS] symbols because not all Windows build systems have
  * those symbols to compare against, and even those that do may be missing
  * newer symbols.
@@ -96,18 +116,12 @@
 #ifndef _WIN32_WINNT_WS03
 #define _WIN32_WINNT_WS03           0x0502   /* Windows Server 2003 */
 #endif
-#ifndef _WIN32_WINNT_WIN6
-#define _WIN32_WINNT_WIN6           0x0600   /* Windows Vista */
-#endif
 #ifndef _WIN32_WINNT_VISTA
 #define _WIN32_WINNT_VISTA          0x0600   /* Windows Vista */
 #endif
 #ifndef _WIN32_WINNT_WS08
 #define _WIN32_WINNT_WS08           0x0600   /* Windows Server 2008 */
 #endif
-#ifndef _WIN32_WINNT_LONGHORN
-#define _WIN32_WINNT_LONGHORN       0x0600   /* Windows Vista */
-#endif
 #ifndef _WIN32_WINNT_WIN7
 #define _WIN32_WINNT_WIN7           0x0601   /* Windows 7 */
 #endif
@@ -117,9 +131,6 @@
 #ifndef _WIN32_WINNT_WINBLUE
 #define _WIN32_WINNT_WINBLUE        0x0603   /* Windows 8.1 */
 #endif
-#ifndef _WIN32_WINNT_WINTHRESHOLD
-#define _WIN32_WINNT_WINTHRESHOLD   0x0A00   /* Windows 10 */
-#endif
 #ifndef _WIN32_WINNT_WIN10
 #define _WIN32_WINNT_WIN10          0x0A00   /* Windows 10 */
 #endif
diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c
index c0a8d80..8fa5cda 100644
--- a/Utilities/cmcurl/lib/share.c
+++ b/Utilities/cmcurl/lib/share.c
@@ -133,13 +133,13 @@
       res = CURLSHE_BAD_OPTION;
     }
     if(!res)
-      share->specifier |= (1<<type);
+      share->specifier |= (unsigned int)(1<<type);
     break;
 
   case CURLSHOPT_UNSHARE:
     /* this is a type this share will no longer share */
     type = va_arg(param, int);
-    share->specifier &= ~(1<<type);
+    share->specifier &= ~(unsigned int)(1<<type);
     switch(type) {
     case CURL_LOCK_DATA_DNS:
       break;
@@ -264,7 +264,7 @@
   if(!share)
     return CURLSHE_INVALID;
 
-  if(share->specifier & (1<<type)) {
+  if(share->specifier & (unsigned int)(1<<type)) {
     if(share->lockfunc) /* only call this if set! */
       share->lockfunc(data, type, accesstype, share->clientdata);
   }
@@ -281,7 +281,7 @@
   if(!share)
     return CURLSHE_INVALID;
 
-  if(share->specifier & (1<<type)) {
+  if(share->specifier & (unsigned int)(1<<type)) {
     if(share->unlockfunc) /* only call this if set! */
       share->unlockfunc (data, type, share->clientdata);
   }
diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h
index 7f55aac..632d919 100644
--- a/Utilities/cmcurl/lib/share.h
+++ b/Utilities/cmcurl/lib/share.h
@@ -31,14 +31,6 @@
 #include "urldata.h"
 #include "conncache.h"
 
-/* SalfordC says "A structure member may not be volatile". Hence:
- */
-#ifdef __SALFORDC__
-#define CURL_VOLATILE
-#else
-#define CURL_VOLATILE volatile
-#endif
-
 #define CURL_GOOD_SHARE 0x7e117a1e
 #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
 
@@ -46,7 +38,7 @@
 struct Curl_share {
   unsigned int magic; /* CURL_GOOD_SHARE */
   unsigned int specifier;
-  CURL_VOLATILE unsigned int dirty;
+  volatile unsigned int dirty;
 
   curl_lock_function lockfunc;
   curl_unlock_function unlockfunc;
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 32c5137..1d1867c 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -27,7 +27,7 @@
 
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
 
-#ifdef WIN32
+#ifdef _WIN32
 #define getpid GetCurrentProcessId
 #endif
 
@@ -272,7 +272,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   smb_disconnect,                       /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SMB,                             /* defport */
@@ -299,7 +299,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   smb_disconnect,                       /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SMBS,                            /* defport */
@@ -1047,14 +1047,7 @@
         break;
       }
     }
-    data->req.bytecount += len;
     data->req.offset += len;
-    result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
-    if(result) {
-      req->result = result;
-      next_state = SMB_CLOSE;
-      break;
-    }
     next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
     break;
 
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 81a17e3..bfe7b8f 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -130,7 +130,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   smtp_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_SMTP,                        /* defport */
@@ -159,7 +159,7 @@
   ZERO_NULL,                        /* domore_getsock */
   ZERO_NULL,                        /* perform_getsock */
   smtp_disconnect,                  /* disconnect */
-  ZERO_NULL,                        /* readwrite */
+  ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   PORT_SMTPS,                       /* defport */
@@ -250,8 +250,8 @@
  */
 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = data->state.buffer;
-  size_t len = strlen(message);
+  char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
+  size_t len = data->conn->proto.smtpc.pp.nfinal;
 
   if(len > 4) {
     /* Find the start of the message */
@@ -859,7 +859,7 @@
   (void)instate; /* no use for this yet */
 
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.smtpc.pp.cache_size)
+  if(data->conn->proto.smtpc.pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(smtpcode != 220) {
@@ -883,8 +883,8 @@
 {
   CURLcode result = CURLE_OK;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *line = data->state.buffer;
-  size_t len = strlen(line);
+  const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
+  size_t len = smtpc->pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
@@ -1033,8 +1033,8 @@
 {
   CURLcode result = CURLE_OK;
   struct SMTP *smtp = data->req.p.smtp;
-  char *line = data->state.buffer;
-  size_t len = strlen(line);
+  char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
+  size_t len = data->conn->proto.smtpc.pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
@@ -1044,12 +1044,8 @@
     result = CURLE_WEIRD_SERVER_REPLY;
   }
   else {
-    /* Temporarily add the LF character back and send as body to the client */
-    if(!data->req.no_body) {
-      line[len] = '\n';
-      result = Curl_client_write(data, CLIENTWRITE_BODY, line, len + 1);
-      line[len] = '\0';
-    }
+    if(!data->req.no_body)
+      result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
 
     if(smtpcode != 1) {
       if(smtp->rcpt) {
@@ -1268,7 +1264,6 @@
       break;
 
     case SMTP_QUIT:
-      /* fallthrough, just stop! */
     default:
       /* internal error */
       smtp_state(data, SMTP_STOP);
@@ -1320,7 +1315,7 @@
   CURLcode result = CURLE_OK;
   struct SMTP *smtp;
 
-  smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1);
+  smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
   if(!smtp)
     result = CURLE_OUT_OF_MEMORY;
 
@@ -1362,8 +1357,7 @@
   Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
 
   /* Initialise the pingpong layer */
-  Curl_pp_setup(pp);
-  Curl_pp_init(data, pp);
+  Curl_pp_init(pp);
 
   /* Parse the URL options */
   result = smtp_parse_url_options(conn);
@@ -1541,6 +1535,8 @@
 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
+  DEBUGASSERT(data);
+  DEBUGASSERT(data->conn);
   *done = FALSE; /* default to false */
 
   /* Parse the custom request */
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c
index 963e140..d01b255 100644
--- a/Utilities/cmcurl/lib/socketpair.c
+++ b/Utilities/cmcurl/lib/socketpair.c
@@ -28,14 +28,11 @@
 #include "rand.h"
 
 #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR)
-#ifdef WIN32
+#ifdef _WIN32
 /*
  * This is a socketpair() implementation for Windows.
  */
 #include <string.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <windows.h>
 #include <io.h>
 #else
 #ifdef HAVE_NETDB_H
@@ -50,7 +47,7 @@
 #ifndef INADDR_LOOPBACK
 #define INADDR_LOOPBACK 0x7f000001
 #endif /* !INADDR_LOOPBACK */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 #include "nonblock.h" /* for curlx_nonblock */
 #include "timeval.h"  /* needed before select.h */
@@ -87,7 +84,7 @@
 
   socks[0] = socks[1] = CURL_SOCKET_BAD;
 
-#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(_WIN32) || defined(__CYGWIN__)
   /* don't set SO_REUSEADDR on Windows */
   (void)reuse;
 #ifdef SO_EXCLUSIVEADDRUSE
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h
index 306ab5d..bd499ab 100644
--- a/Utilities/cmcurl/lib/socketpair.h
+++ b/Utilities/cmcurl/lib/socketpair.h
@@ -25,6 +25,23 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+
+#ifdef HAVE_PIPE
+
+#define wakeup_write  write
+#define wakeup_read   read
+#define wakeup_close  close
+#define wakeup_create pipe
+
+#else /* HAVE_PIPE */
+
+#define wakeup_write     swrite
+#define wakeup_read      sread
+#define wakeup_close     sclose
+#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
+
+#endif /* HAVE_PIPE */
+
 #ifndef HAVE_SOCKETPAIR
 #include <curl/curl.h>
 
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index a7b5ab0..ecd2f7e 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -71,9 +71,18 @@
   CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
 };
 
+#define CURL_SOCKS_BUF_SIZE 600
+
+/* make sure we configure it not too low */
+#if CURL_SOCKS_BUF_SIZE < 600
+#error CURL_SOCKS_BUF_SIZE must be at least 600
+#endif
+
+
 struct socks_state {
   enum connect_t state;
   ssize_t outstanding;  /* send this many bytes more */
+  unsigned char buffer[CURL_SOCKS_BUF_SIZE];
   unsigned char *outp; /* send from this pointer */
 
   const char *hostname;
@@ -249,7 +258,7 @@
       failf(data, "connection to proxy closed");
       return CURLPX_CLOSED;
     }
-    failf(data, "SOCKS4: Failed receiving %s: %s", description,
+    failf(data, "SOCKS: Failed receiving %s: %s", description,
           curl_easy_strerror(result));
     return failcode;
   }
@@ -278,14 +287,11 @@
   struct connectdata *conn = cf->conn;
   const bool protocol4a =
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
-  unsigned char *socksreq = (unsigned char *)data->state.buffer;
+  unsigned char *socksreq = sx->buffer;
   CURLcode result;
   CURLproxycode presult;
   struct Curl_dns_entry *dns = NULL;
 
-  /* make sure that the buffer is at least 600 bytes */
-  DEBUGASSERT(READBUFFER_MIN >= 600);
-
   switch(sx->state) {
   case CONNECT_SOCKS_INIT:
     /* SOCKS4 can only do IPv4, insist! */
@@ -353,9 +359,10 @@
         return CURLPX_OK;
       }
     }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
+  case CONNECT_RESOLVED:
 CONNECT_RESOLVED:
-  case CONNECT_RESOLVED: {
+  {
     struct Curl_addrinfo *hp = NULL;
     /*
      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
@@ -393,17 +400,20 @@
     if(!hp)
       return CURLPX_RESOLVE_HOST;
   }
-    /* FALLTHROUGH */
-CONNECT_REQ_INIT:
+    FALLTHROUGH();
   case CONNECT_REQ_INIT:
+CONNECT_REQ_INIT:
     /*
      * This is currently not supporting "Identification Protocol (RFC1413)".
      */
     socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
     if(sx->proxy_user) {
       size_t plen = strlen(sx->proxy_user);
-      if(plen >= (size_t)data->set.buffer_size - 8) {
-        failf(data, "Too long SOCKS proxy user name, can't use");
+      if(plen > 255) {
+        /* there is no real size limit to this field in the protocol, but
+           SOCKS5 limits the proxy user field to 255 bytes and it seems likely
+           that a longer field is either a mistake or malicious input */
+        failf(data, "Too long SOCKS proxy user name");
         return CURLPX_LONG_USER;
       }
       /* copy the proxy name WITH trailing zero */
@@ -426,7 +436,8 @@
         socksreq[7] = 1;
         /* append hostname */
         hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
-        if(hostnamelen <= 255)
+        if((hostnamelen <= 255) &&
+           (packetsize + hostnamelen < sizeof(sx->buffer)))
           strcpy((char *)socksreq + packetsize, sx->hostname);
         else {
           failf(data, "SOCKS4: too long host name");
@@ -435,10 +446,11 @@
         packetsize += hostnamelen;
       }
       sx->outp = socksreq;
+      DEBUGASSERT(packetsize <= sizeof(sx->buffer));
       sx->outstanding = packetsize;
       sxstate(sx, data, CONNECT_REQ_SENDING);
     }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_REQ_SENDING:
     /* Send request */
     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
@@ -454,7 +466,7 @@
     sx->outp = socksreq;
     sxstate(sx, data, CONNECT_SOCKS_READ);
 
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_SOCKS_READ:
     /* Receive response */
     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
@@ -566,14 +578,14 @@
     o  X'00' succeeded
   */
   struct connectdata *conn = cf->conn;
-  unsigned char *socksreq = (unsigned char *)data->state.buffer;
-  int idx;
+  unsigned char *socksreq = sx->buffer;
+  size_t idx;
   CURLcode result;
   CURLproxycode presult;
   bool socks5_resolve_local =
     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
   const size_t hostname_len = strlen(sx->hostname);
-  ssize_t len = 0;
+  size_t len = 0;
   const unsigned char auth = data->set.socks5auth;
   bool allow_gssapi = FALSE;
   struct Curl_dns_entry *dns = NULL;
@@ -616,6 +628,7 @@
     socksreq[1] = (unsigned char) (idx - 2);
 
     sx->outp = socksreq;
+    DEBUGASSERT(idx <= sizeof(sx->buffer));
     sx->outstanding = idx;
     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
                                "initial SOCKS5 request");
@@ -636,12 +649,12 @@
       /* remain in sending state */
       return CURLPX_OK;
     }
-    /* FALLTHROUGH */
-CONNECT_SOCKS_READ_INIT:
+    FALLTHROUGH();
   case CONNECT_SOCKS_READ_INIT:
+CONNECT_SOCKS_READ_INIT:
     sx->outstanding = 2; /* expect two bytes */
     sx->outp = socksreq; /* store it here */
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_SOCKS_READ:
     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
                                "initial SOCKS5 response");
@@ -742,10 +755,11 @@
     }
     len += proxy_password_len;
     sxstate(sx, data, CONNECT_AUTH_SEND);
+    DEBUGASSERT(len <= sizeof(sx->buffer));
     sx->outstanding = len;
     sx->outp = socksreq;
   }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_AUTH_SEND:
     presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
                                "SOCKS5 sub-negotiation request");
@@ -758,7 +772,7 @@
     sx->outp = socksreq;
     sx->outstanding = 2;
     sxstate(sx, data, CONNECT_AUTH_READ);
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_AUTH_READ:
     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
                                "SOCKS5 sub-negotiation response");
@@ -777,9 +791,9 @@
 
     /* Everything is good so far, user was authenticated! */
     sxstate(sx, data, CONNECT_REQ_INIT);
-    /* FALLTHROUGH */
-CONNECT_REQ_INIT:
+    FALLTHROUGH();
   case CONNECT_REQ_INIT:
+CONNECT_REQ_INIT:
     if(socks5_resolve_local) {
       enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
                                       TRUE, &dns);
@@ -816,13 +830,23 @@
         return CURLPX_OK;
       }
     }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
+  case CONNECT_RESOLVED:
 CONNECT_RESOLVED:
-  case CONNECT_RESOLVED: {
-    char dest[MAX_IPADR_LEN] = "unknown";  /* printable address */
+  {
+    char dest[MAX_IPADR_LEN];  /* printable address */
     struct Curl_addrinfo *hp = NULL;
     if(dns)
       hp = dns->addr;
+#ifdef ENABLE_IPV6
+    if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) {
+      int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ?
+        AF_INET : AF_INET6;
+      /* scan for the first proper address */
+      while(hp && (hp->ai_family != wanted_family))
+        hp = hp->ai_next;
+    }
+#endif
     if(!hp) {
       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
             sx->hostname);
@@ -910,10 +934,10 @@
       infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
             sx->hostname, sx->remote_port);
     }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
 
-CONNECT_REQ_SEND:
   case CONNECT_REQ_SEND:
+CONNECT_REQ_SEND:
     /* PORT MSB */
     socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
     /* PORT LSB */
@@ -926,9 +950,10 @@
     }
 #endif
     sx->outp = socksreq;
+    DEBUGASSERT(len <= sizeof(sx->buffer));
     sx->outstanding = len;
     sxstate(sx, data, CONNECT_REQ_SENDING);
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_REQ_SENDING:
     presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
                                "SOCKS5 connect request");
@@ -947,7 +972,7 @@
     sx->outstanding = 10; /* minimum packet size is 10 */
     sx->outp = socksreq;
     sxstate(sx, data, CONNECT_REQ_READ);
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_REQ_READ:
     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
                                "SOCKS5 connect request ack");
@@ -1025,6 +1050,7 @@
       /* decrypt_gssapi_blockread already read the whole packet */
 #endif
       if(len > 10) {
+        DEBUGASSERT(len <= sizeof(sx->buffer));
         sx->outstanding = len - 10; /* get the rest */
         sx->outp = &socksreq[10];
         sxstate(sx, data, CONNECT_REQ_READ_MORE);
@@ -1036,7 +1062,7 @@
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
     }
 #endif
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CONNECT_REQ_READ_MORE:
     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
                                "SOCKS5 connect request address");
@@ -1119,7 +1145,7 @@
     return result;
 
   if(!sx) {
-    sx = calloc(sizeof(*sx), 1);
+    sx = calloc(1, sizeof(*sx));
     if(!sx)
       return CURLE_OUT_OF_MEMORY;
     cf->ctx = sx;
@@ -1157,32 +1183,29 @@
   return result;
 }
 
-static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
+static void socks_cf_adjust_pollset(struct Curl_cfilter *cf,
                                      struct Curl_easy *data,
-                                     curl_socket_t *socks)
+                                     struct easy_pollset *ps)
 {
   struct socks_state *sx = cf->ctx;
-  int fds;
 
-  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  if(!fds && cf->next->connected && !cf->connected && sx) {
+  if(!cf->connected && sx) {
     /* If we are not connected, the filter below is and has nothing
      * to wait on, we determine what to wait for. */
-    socks[0] = Curl_conn_cf_get_socket(cf, data);
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
     switch(sx->state) {
     case CONNECT_RESOLVING:
     case CONNECT_SOCKS_READ:
     case CONNECT_AUTH_READ:
     case CONNECT_REQ_READ:
     case CONNECT_REQ_READ_MORE:
-      fds = GETSOCK_READSOCK(0);
+      Curl_pollset_set_in_only(data, ps, sock);
       break;
     default:
-      fds = GETSOCK_WRITESOCK(0);
+      Curl_pollset_set_out_only(data, ps, sock);
       break;
     }
   }
-  return fds;
 }
 
 static void socks_proxy_cf_close(struct Curl_cfilter *cf,
@@ -1227,7 +1250,7 @@
   socks_proxy_cf_connect,
   socks_proxy_cf_close,
   socks_cf_get_host,
-  socks_cf_get_select_socks,
+  socks_cf_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index 2ede8c7..2437150 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -35,6 +35,7 @@
 #include "timeval.h"
 #include "socks.h"
 #include "warnless.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -139,10 +140,9 @@
   /* prepare service name */
   if(strchr(serviceptr, '/')) {
     service.length = serviceptr_length;
-    service.value = malloc(service.length);
+    service.value = Curl_memdup(serviceptr, service.length);
     if(!service.value)
       return CURLE_OUT_OF_MEMORY;
-    memcpy(service.value, serviceptr, service.length);
 
     gss_major_status = gss_import_name(&gss_minor_status, &service,
                                        (gss_OID) GSS_C_NULL_OID, &server);
@@ -387,12 +387,11 @@
   }
   else {
     gss_send_token.length = 1;
-    gss_send_token.value = malloc(1);
+    gss_send_token.value = Curl_memdup(&gss_enc, 1);
     if(!gss_send_token.value) {
       gss_delete_sec_context(&gss_status, &gss_context, NULL);
       return CURLE_OUT_OF_MEMORY;
     }
-    memcpy(gss_send_token.value, &gss_enc, 1);
 
     gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
                                 GSS_C_QOP_DEFAULT, &gss_send_token,
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index d1200ea..2baae2c 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -331,9 +331,15 @@
     failf(data, "Failed to determine user name.");
     return CURLE_COULDNT_CONNECT;
   }
-  infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
-        names.sUserName);
-  s_pSecFn->FreeContextBuffer(names.sUserName);
+  else {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+    char *user_utf8 = curlx_convert_tchar_to_UTF8(names.sUserName);
+    infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
+          (user_utf8 ? user_utf8 : "(unknown)"));
+    curlx_unicodefree(user_utf8);
+#endif
+    s_pSecFn->FreeContextBuffer(names.sUserName);
+  }
 
   /* Do encryption */
   socksreq[0] = 1;    /* GSS-API subnegotiation version */
diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c
index 07a6139..299c9cc 100644
--- a/Utilities/cmcurl/lib/strdup.c
+++ b/Utilities/cmcurl/lib/strdup.c
@@ -26,7 +26,7 @@
 
 #include <curl/curl.h>
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <wchar.h>
 #endif
 
@@ -56,7 +56,7 @@
 }
 #endif
 
-#ifdef WIN32
+#ifdef _WIN32
 /***************************************************************************
  *
  * Curl_wcsdup(source)
@@ -101,6 +101,26 @@
 
 /***************************************************************************
  *
+ * Curl_memdup0(source, length)
+ *
+ * Copies the 'source' string to a newly allocated buffer (that is returned).
+ * Copies 'length' bytes then adds a null terminator.
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+void *Curl_memdup0(const char *src, size_t length)
+{
+  char *buf = malloc(length + 1);
+  if(!buf)
+    return NULL;
+  memcpy(buf, src, length);
+  buf[length] = 0;
+  return buf;
+}
+
+/***************************************************************************
+ *
  * Curl_saferealloc(ptr, size)
  *
  * Does a normal realloc(), but will free the data pointer if the realloc
diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h
index c3430b5..238a261 100644
--- a/Utilities/cmcurl/lib/strdup.h
+++ b/Utilities/cmcurl/lib/strdup.h
@@ -28,10 +28,11 @@
 #ifndef HAVE_STRDUP
 char *Curl_strdup(const char *str);
 #endif
-#ifdef WIN32
+#ifdef _WIN32
 wchar_t* Curl_wcsdup(const wchar_t* src);
 #endif
 void *Curl_memdup(const void *src, size_t buffer_length);
 void *Curl_saferealloc(void *ptr, size_t size);
+void *Curl_memdup0(const char *src, size_t length);
 
 #endif /* HEADER_CURL_STRDUP_H */
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index be41914..a900e78 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -48,7 +48,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(WIN32) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(_WIN32_WCE)
 #define PRESERVE_WINDOWS_ERROR_CODE
 #endif
 
@@ -319,6 +319,9 @@
   case CURLE_UNRECOVERABLE_POLL:
     return "Unrecoverable error in select/poll";
 
+  case CURLE_TOO_LARGE:
+    return "A value or data field grew larger than allowed";
+
     /* error codes not used by current libcurl */
   case CURLE_OBSOLETE20:
   case CURLE_OBSOLETE24:
@@ -553,6 +556,9 @@
   case CURLUE_LACKS_IDN:
     return "libcurl lacks IDN support";
 
+  case CURLUE_TOO_LARGE:
+    return "A value or data field is larger than allowed";
+
   case CURLUE_LAST:
     break;
   }
@@ -572,10 +578,11 @@
  * Returns NULL if no error message was found for error code.
  */
 static const char *
-get_winsock_error (int err, char *buf, size_t len)
+get_winsock_error(int err, char *buf, size_t len)
 {
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   const char *p;
+  size_t alen;
 #endif
 
   if(!len)
@@ -755,14 +762,15 @@
   default:
     return NULL;
   }
-  strncpy(buf, p, len);
-  buf [len-1] = '\0';
+  alen = strlen(p);
+  if(alen < len)
+    strcpy(buf, p);
   return buf;
 #endif
 }
 #endif   /* USE_WINSOCK */
 
-#if defined(WIN32) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(_WIN32_WCE)
 /* This is a helper function for Curl_strerror that converts Windows API error
  * codes (GetLastError) to error messages.
  * Returns NULL if no error message was found for error code.
@@ -804,7 +812,7 @@
 
   return (*buf ? buf : NULL);
 }
-#endif /* WIN32 || _WIN32_WCE */
+#endif /* _WIN32 || _WIN32_WCE */
 
 /*
  * Our thread-safe and smart strerror() replacement.
@@ -832,32 +840,30 @@
 #endif
   int old_errno = errno;
   char *p;
-  size_t max;
 
   if(!buflen)
     return NULL;
 
-#ifndef WIN32
+#ifndef _WIN32
   DEBUGASSERT(err >= 0);
 #endif
 
-  max = buflen - 1;
   *buf = '\0';
 
-#if defined(WIN32) || defined(_WIN32_WCE)
-#if defined(WIN32)
+#if defined(_WIN32) || defined(_WIN32_WCE)
+#if defined(_WIN32)
   /* 'sys_nerr' is the maximum errno number, it is not widely portable */
   if(err >= 0 && err < sys_nerr)
-    strncpy(buf, sys_errlist[err], max);
+    msnprintf(buf, buflen, "%s", sys_errlist[err]);
   else
 #endif
   {
     if(
 #ifdef USE_WINSOCK
-       !get_winsock_error(err, buf, max) &&
+       !get_winsock_error(err, buf, buflen) &&
 #endif
-       !get_winapi_error((DWORD)err, buf, max))
-      msnprintf(buf, max, "Unknown error %d (%#x)", err, err);
+       !get_winapi_error((DWORD)err, buf, buflen))
+      msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
   }
 #else /* not Windows coming up */
 
@@ -867,9 +873,9 @@
   * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
   * message string, or EINVAL if 'errnum' is not a valid error number.
   */
-  if(0 != strerror_r(err, buf, max)) {
+  if(0 != strerror_r(err, buf, buflen)) {
     if('\0' == buf[0])
-      msnprintf(buf, max, "Unknown error %d", err);
+      msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
  /*
@@ -881,25 +887,23 @@
     char buffer[256];
     char *msg = strerror_r(err, buffer, sizeof(buffer));
     if(msg)
-      strncpy(buf, msg, max);
+      msnprintf(buf, buflen, "%s", msg);
     else
-      msnprintf(buf, max, "Unknown error %d", err);
+      msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #else
   {
     /* !checksrc! disable STRERROR 1 */
     const char *msg = strerror(err);
     if(msg)
-      strncpy(buf, msg, max);
+      msnprintf(buf, buflen, "%s", msg);
     else
-      msnprintf(buf, max, "Unknown error %d", err);
+      msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #endif
 
 #endif /* end of not Windows */
 
-  buf[max] = '\0'; /* make sure the string is null-terminated */
-
   /* strip trailing '\r\n' or '\n'. */
   p = strrchr(buf, '\n');
   if(p && (p - buf) >= 2)
@@ -923,7 +927,7 @@
  * Curl_winapi_strerror:
  * Variant of Curl_strerror if the error code is definitely Windows API.
  */
-#if defined(WIN32) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(_WIN32_WCE)
 const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
 {
 #ifdef PRESERVE_WINDOWS_ERROR_CODE
@@ -943,8 +947,8 @@
 #else
   {
     const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
-    strncpy(buf, txt, buflen);
-    buf[buflen - 1] = '\0';
+    if(strlen(txt) < buflen)
+      strcpy(buf, txt);
   }
 #endif
 
@@ -958,7 +962,7 @@
 
   return buf;
 }
-#endif /* WIN32 || _WIN32_WCE */
+#endif /* _WIN32 || _WIN32_WCE */
 
 #ifdef USE_WINDOWS_SSPI
 /*
@@ -986,6 +990,10 @@
       break;
 #define SEC2TXT(sec) case sec: txt = #sec; break
     SEC2TXT(CRYPT_E_REVOKED);
+    SEC2TXT(CRYPT_E_NO_REVOCATION_DLL);
+    SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK);
+    SEC2TXT(CRYPT_E_REVOCATION_OFFLINE);
+    SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE);
     SEC2TXT(SEC_E_ALGORITHM_MISMATCH);
     SEC2TXT(SEC_E_BAD_BINDINGS);
     SEC2TXT(SEC_E_BAD_PKGID);
@@ -1077,17 +1085,11 @@
               err);
   }
   else {
-    char txtbuf[80];
     char msgbuf[256];
-
-    msnprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
-
     if(get_winapi_error(err, msgbuf, sizeof(msgbuf)))
-      msnprintf(buf, buflen, "%s - %s", txtbuf, msgbuf);
-    else {
-      strncpy(buf, txtbuf, buflen);
-      buf[buflen - 1] = '\0';
-    }
+      msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
+    else
+      msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
   }
 
 #else
@@ -1095,8 +1097,8 @@
     txt = "No error";
   else
     txt = "Error";
-  strncpy(buf, txt, buflen);
-  buf[buflen - 1] = '\0';
+  if(buflen > strlen(txt))
+    strcpy(buf, txt);
 #endif
 
   if(errno != old_errno)
diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h
index 399712f..6806867 100644
--- a/Utilities/cmcurl/lib/strerror.h
+++ b/Utilities/cmcurl/lib/strerror.h
@@ -29,7 +29,7 @@
 #define STRERROR_LEN 256 /* a suitable length */
 
 const char *Curl_strerror(int err, char *buf, size_t buflen);
-#if defined(WIN32) || defined(_WIN32_WCE)
+#if defined(_WIN32) || defined(_WIN32_WCE)
 const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen);
 #endif
 #ifdef USE_WINDOWS_SSPI
diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c
index 0cdaf3b..d2862de 100644
--- a/Utilities/cmcurl/lib/system_win32.c
+++ b/Utilities/cmcurl/lib/system_win32.c
@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 #include <curl/curl.h>
 #include "system_win32.h"
@@ -38,16 +38,23 @@
 
 LARGE_INTEGER Curl_freq;
 bool Curl_isVistaOrGreater;
+bool Curl_isWindows8OrGreater;
 
 /* Handle of iphlpapp.dll */
 static HMODULE s_hIpHlpApiDll = NULL;
 
-/* Pointer to the if_nametoindex function */
+/* Function pointers */
 IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL;
+FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW = NULL;
+GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel = NULL;
+GETADDRINFOEXW_FN Curl_GetAddrInfoExW = NULL;
 
 /* Curl_win32_init() performs win32 global initialization */
 CURLcode Curl_win32_init(long flags)
 {
+#ifdef USE_WINSOCK
+  HMODULE ws2_32Dll;
+#endif
   /* CURL_GLOBAL_WIN32 controls the *optional* part of the initialization which
      is just for Winsock at the moment. Any required win32 initialization
      should take place after this block. */
@@ -104,6 +111,18 @@
       Curl_if_nametoindex = pIfNameToIndex;
   }
 
+#ifdef USE_WINSOCK
+  ws2_32Dll = GetModuleHandleA("ws2_32");
+  if(ws2_32Dll) {
+    Curl_FreeAddrInfoExW = CURLX_FUNCTION_CAST(FREEADDRINFOEXW_FN,
+      GetProcAddress(ws2_32Dll, "FreeAddrInfoExW"));
+    Curl_GetAddrInfoExCancel = CURLX_FUNCTION_CAST(GETADDRINFOEXCANCEL_FN,
+      GetProcAddress(ws2_32Dll, "GetAddrInfoExCancel"));
+    Curl_GetAddrInfoExW = CURLX_FUNCTION_CAST(GETADDRINFOEXW_FN,
+      GetProcAddress(ws2_32Dll, "GetAddrInfoExW"));
+  }
+#endif
+
   /* curlx_verify_windows_version must be called during init at least once
      because it has its own initialization routine. */
   if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
@@ -113,6 +132,13 @@
   else
     Curl_isVistaOrGreater = FALSE;
 
+  if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+                                  VERSION_GREATER_THAN_EQUAL)) {
+    Curl_isWindows8OrGreater = TRUE;
+  }
+  else
+    Curl_isWindows8OrGreater = FALSE;
+
   QueryPerformanceFrequency(&Curl_freq);
   return CURLE_OK;
 }
@@ -120,6 +146,9 @@
 /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */
 void Curl_win32_cleanup(long init_flags)
 {
+  Curl_FreeAddrInfoExW = NULL;
+  Curl_GetAddrInfoExCancel = NULL;
+  Curl_GetAddrInfoExW = NULL;
   if(s_hIpHlpApiDll) {
     FreeLibrary(s_hIpHlpApiDll);
     s_hIpHlpApiDll = NULL;
@@ -238,4 +267,4 @@
 #endif
 }
 
-#endif /* WIN32 */
+#endif /* _WIN32 */
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h
index 6482643..bd490ca 100644
--- a/Utilities/cmcurl/lib/system_win32.h
+++ b/Utilities/cmcurl/lib/system_win32.h
@@ -26,10 +26,11 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#ifdef _WIN32
 
 extern LARGE_INTEGER Curl_freq;
 extern bool Curl_isVistaOrGreater;
+extern bool Curl_isWindows8OrGreater;
 
 CURLcode Curl_win32_init(long flags);
 void Curl_win32_cleanup(long init_flags);
@@ -40,10 +41,37 @@
 /* This is used instead of if_nametoindex if available on Windows */
 extern IF_NAMETOINDEX_FN Curl_if_nametoindex;
 
+/* Identical copy of addrinfoexW/ADDRINFOEXW */
+typedef struct addrinfoexW_
+{
+  int                  ai_flags;
+  int                  ai_family;
+  int                  ai_socktype;
+  int                  ai_protocol;
+  size_t               ai_addrlen;
+  PWSTR                ai_canonname;
+  struct sockaddr     *ai_addr;
+  void                *ai_blob;
+  size_t               ai_bloblen;
+  LPGUID               ai_provider;
+  struct addrinfoexW_ *ai_next;
+} ADDRINFOEXW_;
+
+typedef void (CALLBACK *LOOKUP_COMPLETION_FN)(DWORD, DWORD, LPWSAOVERLAPPED);
+typedef void (WSAAPI *FREEADDRINFOEXW_FN)(ADDRINFOEXW_*);
+typedef int (WSAAPI *GETADDRINFOEXCANCEL_FN)(LPHANDLE);
+typedef int (WSAAPI *GETADDRINFOEXW_FN)(PCWSTR, PCWSTR, DWORD, LPGUID,
+  const ADDRINFOEXW_*, ADDRINFOEXW_**, struct timeval*, LPOVERLAPPED,
+  LOOKUP_COMPLETION_FN, LPHANDLE);
+
+extern FREEADDRINFOEXW_FN Curl_FreeAddrInfoExW;
+extern GETADDRINFOEXCANCEL_FN Curl_GetAddrInfoExCancel;
+extern GETADDRINFOEXW_FN Curl_GetAddrInfoExW;
+
 /* This is used to dynamically load DLLs */
 HMODULE Curl_load_library(LPCTSTR filename);
-#else  /* WIN32 */
+#else  /* _WIN32 */
 #define Curl_win32_init(x) CURLE_OK
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 #endif /* HEADER_CURL_SYSTEM_WIN32_H */
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 836e255..34dc5e8 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -160,6 +160,7 @@
   unsigned short subopt_wsy;         /* Set with suboption NAWS */
   TelnetReceive telrcv_state;
   struct curl_slist *telnet_vars;    /* Environment variables */
+  struct dynbuf out;                 /* output buffer */
 
   /* suboptions */
   unsigned char subbuffer[SUBBUFSIZE];
@@ -185,7 +186,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_TELNET,                          /* defport */
@@ -204,6 +205,7 @@
   if(!tn)
     return CURLE_OUT_OF_MEMORY;
 
+  Curl_dyn_init(&tn->out, 0xffff);
   data->req.p.telnet = tn; /* make us known */
 
   tn->telrcv_state = CURL_TS_DATA;
@@ -799,8 +801,10 @@
      was given on the command line */
   if(data->state.aptr.user) {
     char buffer[256];
-    if(str_is_nonascii(data->conn->user))
+    if(str_is_nonascii(data->conn->user)) {
+      DEBUGF(infof(data, "set a non ASCII user name in telnet"));
       return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
     msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
     beg = curl_slist_append(tn->telnet_vars, buffer);
     if(!beg) {
@@ -826,23 +830,27 @@
       case 5:
         /* Terminal type */
         if(strncasecompare(option, "TTYPE", 5)) {
-          strncpy(tn->subopt_ttype, arg, 31);
-          tn->subopt_ttype[31] = 0; /* String termination */
-          tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+          size_t l = strlen(arg);
+          if(l < sizeof(tn->subopt_ttype)) {
+            strcpy(tn->subopt_ttype, arg);
+            tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+            break;
+          }
         }
-        else
-          result = CURLE_UNKNOWN_OPTION;
+        result = CURLE_UNKNOWN_OPTION;
         break;
 
       case 8:
         /* Display variable */
         if(strncasecompare(option, "XDISPLOC", 8)) {
-          strncpy(tn->subopt_xdisploc, arg, 127);
-          tn->subopt_xdisploc[127] = 0; /* String termination */
-          tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+          size_t l = strlen(arg);
+          if(l < sizeof(tn->subopt_xdisploc)) {
+            strcpy(tn->subopt_xdisploc, arg);
+            tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+            break;
+          }
         }
-        else
-          result = CURLE_UNKNOWN_OPTION;
+        result = CURLE_UNKNOWN_OPTION;
         break;
 
       case 7:
@@ -1223,37 +1231,33 @@
 static CURLcode send_telnet_data(struct Curl_easy *data,
                                  char *buffer, ssize_t nread)
 {
-  ssize_t escapes, i, outlen;
-  unsigned char *outbuf = NULL;
+  ssize_t i, outlen;
+  unsigned char *outbuf;
   CURLcode result = CURLE_OK;
-  ssize_t bytes_written, total_written;
+  ssize_t bytes_written, total_written = 0;
   struct connectdata *conn = data->conn;
+  struct TELNET *tn = data->req.p.telnet;
 
-  /* Determine size of new buffer after escaping */
-  escapes = 0;
-  for(i = 0; i < nread; i++)
-    if((unsigned char)buffer[i] == CURL_IAC)
-      escapes++;
-  outlen = nread + escapes;
+  DEBUGASSERT(tn);
 
-  if(outlen == nread)
-    outbuf = (unsigned char *)buffer;
-  else {
-    ssize_t j;
-    outbuf = malloc(nread + escapes + 1);
-    if(!outbuf)
-      return CURLE_OUT_OF_MEMORY;
+  if(memchr(buffer, CURL_IAC, nread)) {
+    /* only use the escape buffer when necessary */
+    Curl_dyn_reset(&tn->out);
 
-    j = 0;
-    for(i = 0; i < nread; i++) {
-      outbuf[j++] = (unsigned char)buffer[i];
-      if((unsigned char)buffer[i] == CURL_IAC)
-        outbuf[j++] = CURL_IAC;
+    for(i = 0; i < nread && !result; i++) {
+      result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
+      if(!result && ((unsigned char)buffer[i] == CURL_IAC))
+        /* IAC is FF in hex */
+        result = Curl_dyn_addn(&tn->out, "\xff", 1);
     }
-    outbuf[j] = '\0';
-  }
 
-  total_written = 0;
+    outlen = Curl_dyn_len(&tn->out);
+    outbuf = Curl_dyn_uptr(&tn->out);
+  }
+  else {
+    outlen = nread;
+    outbuf = (unsigned char *)buffer;
+  }
   while(!result && total_written < outlen) {
     /* Make sure socket is writable to avoid EWOULDBLOCK condition */
     struct pollfd pfd[1];
@@ -1266,19 +1270,13 @@
         break;
       default:                    /* write! */
         bytes_written = 0;
-        result = Curl_nwrite(data, FIRSTSOCKET,
-                             outbuf + total_written,
-                             outlen - total_written,
-                             &bytes_written);
+        result = Curl_nwrite(data, FIRSTSOCKET, outbuf + total_written,
+                             outlen - total_written, &bytes_written);
         total_written += bytes_written;
         break;
     }
   }
 
-  /* Free malloc copy if escaped */
-  if(outbuf != (unsigned char *)buffer)
-    free(outbuf);
-
   return result;
 }
 
@@ -1294,6 +1292,7 @@
 
   curl_slist_free_all(tn->telnet_vars);
   tn->telnet_vars = NULL;
+  Curl_dyn_free(&tn->out);
   return CURLE_OK;
 }
 
@@ -1321,7 +1320,7 @@
   ssize_t nread;
   struct curltime now;
   bool keepon = TRUE;
-  char *buf = data->state.buffer;
+  char buffer[4*1024];
   struct TELNET *tn;
 
   *done = TRUE; /* unconditionally */
@@ -1378,7 +1377,7 @@
 
   /* Keep on listening and act on events */
   while(keepon) {
-    const DWORD buf_size = (DWORD)data->set.buffer_size;
+    const DWORD buf_size = (DWORD)sizeof(buffer);
     DWORD waitret = WaitForMultipleObjects(obj_count, objs,
                                            FALSE, wait_timeout);
     switch(waitret) {
@@ -1389,7 +1388,7 @@
         if(data->set.is_fread_set) {
           size_t n;
           /* read from user-supplied method */
-          n = data->state.fread_func(buf, 1, buf_size, data->state.in);
+          n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
           if(n == CURL_READFUNC_ABORT) {
             keepon = FALSE;
             result = CURLE_READ_ERROR;
@@ -1417,7 +1416,7 @@
           if(!readfile_read)
             break;
 
-          if(!ReadFile(stdin_handle, buf, buf_size,
+          if(!ReadFile(stdin_handle, buffer, buf_size,
                        &readfile_read, NULL)) {
             keepon = FALSE;
             result = CURLE_READ_ERROR;
@@ -1425,7 +1424,7 @@
           }
         }
 
-        result = send_telnet_data(data, buf, readfile_read);
+        result = send_telnet_data(data, buffer, readfile_read);
         if(result) {
           keepon = FALSE;
           break;
@@ -1436,14 +1435,14 @@
 
     case WAIT_OBJECT_0 + 1:
     {
-      if(!ReadFile(stdin_handle, buf, buf_size,
+      if(!ReadFile(stdin_handle, buffer, buf_size,
                    &readfile_read, NULL)) {
         keepon = FALSE;
         result = CURLE_READ_ERROR;
         break;
       }
 
-      result = send_telnet_data(data, buf, readfile_read);
+      result = send_telnet_data(data, buffer, readfile_read);
       if(result) {
         keepon = FALSE;
         break;
@@ -1465,7 +1464,7 @@
       }
       if(events.lNetworkEvents & FD_READ) {
         /* read data from network */
-        result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+        result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
         /* read would've blocked. Loop again */
         if(result == CURLE_AGAIN)
           break;
@@ -1481,7 +1480,7 @@
           break;
         }
 
-        result = telrcv(data, (unsigned char *) buf, nread);
+        result = telrcv(data, (unsigned char *) buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
@@ -1542,11 +1541,11 @@
     case 0:                     /* timeout */
       pfd[0].revents = 0;
       pfd[1].revents = 0;
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     default:                    /* read! */
       if(pfd[0].revents & POLLIN) {
         /* read data from network */
-        result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
+        result = Curl_read(data, sockfd, buffer, sizeof(buffer), &nread);
         /* read would've blocked. Loop again */
         if(result == CURLE_AGAIN)
           break;
@@ -1572,7 +1571,7 @@
         total_dl += nread;
         result = Curl_pgrsSetDownloadCounter(data, total_dl);
         if(!result)
-          result = telrcv(data, (unsigned char *)buf, nread);
+          result = telrcv(data, (unsigned char *)buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
@@ -1590,12 +1589,12 @@
       nread = 0;
       if(poll_cnt == 2) {
         if(pfd[1].revents & POLLIN) { /* read from in file */
-          nread = read(pfd[1].fd, buf, data->set.buffer_size);
+          nread = read(pfd[1].fd, buffer, sizeof(buffer));
         }
       }
       else {
         /* read from user-supplied method */
-        nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
+        nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
                                             data->state.in);
         if(nread == CURL_READFUNC_ABORT) {
           keepon = FALSE;
@@ -1606,7 +1605,7 @@
       }
 
       if(nread > 0) {
-        result = send_telnet_data(data, buf, nread);
+        result = send_telnet_data(data, buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index e78140d..4288110 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -70,8 +70,6 @@
 
 /* RFC2348 allows the block size to be negotiated */
 #define TFTP_BLKSIZE_DEFAULT 512
-#define TFTP_BLKSIZE_MIN 8
-#define TFTP_BLKSIZE_MAX 65464
 #define TFTP_OPTION_BLKSIZE "blksize"
 
 /* from RFC2349: */
@@ -183,7 +181,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
   tftp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_TFTP,                            /* defport */
@@ -978,11 +976,9 @@
     return CURLE_OUT_OF_MEMORY;
 
   /* alloc pkt buffers based on specified blksize */
-  if(data->set.tftp_blksize) {
+  if(data->set.tftp_blksize)
+    /* range checked when set */
     blksize = (int)data->set.tftp_blksize;
-    if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN)
-      return CURLE_TFTP_ILLEGAL;
-  }
 
   need_blksize = blksize;
   /* default size is the fallback when no OACK is received */
@@ -1107,7 +1103,6 @@
   CURLcode              result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct tftp_state_data *state = conn->proto.tftpc;
-  struct SingleRequest  *k = &data->req;
 
   /* Receive the packet */
   fromlen = sizeof(fromaddr);
@@ -1141,11 +1136,6 @@
         result = Curl_client_write(data, CLIENTWRITE_BODY,
                                    (char *)state->rpacket.data + 4,
                                    state->rbytes-4);
-        if(!result) {
-          k->bytecount += state->rbytes-4;
-          result = Curl_pgrsSetDownloadCounter(data,
-                                               (curl_off_t) k->bytecount);
-        }
         if(result) {
           tftp_state_machine(state, TFTP_EVENT_ERROR);
           return result;
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h
index 5d2d5da..12404bf 100644
--- a/Utilities/cmcurl/lib/tftp.h
+++ b/Utilities/cmcurl/lib/tftp.h
@@ -25,6 +25,9 @@
  ***************************************************************************/
 #ifndef CURL_DISABLE_TFTP
 extern const struct Curl_handler Curl_handler_tftp;
+
+#define TFTP_BLKSIZE_MIN 8
+#define TFTP_BLKSIZE_MAX 65464
 #endif
 
 #endif /* HEADER_CURL_TFTP_H */
diff --git a/Utilities/cmcurl/lib/timediff.c b/Utilities/cmcurl/lib/timediff.c
index 1b762bb..d0824d1 100644
--- a/Utilities/cmcurl/lib/timediff.c
+++ b/Utilities/cmcurl/lib/timediff.c
@@ -53,7 +53,7 @@
 #endif
     tv->tv_sec = (time_t)tv_sec;
     tv->tv_usec = (suseconds_t)tv_usec;
-#elif defined(WIN32) /* maybe also others in the future */
+#elif defined(_WIN32) /* maybe also others in the future */
 #if TIMEDIFF_T_MAX > LONG_MAX
     /* tv_sec overflow check on Windows there we know it is long */
     if(tv_sec > LONG_MAX)
diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c
index 026d9d1..5a6727c 100644
--- a/Utilities/cmcurl/lib/timeval.c
+++ b/Utilities/cmcurl/lib/timeval.c
@@ -24,11 +24,10 @@
 
 #include "timeval.h"
 
-#if defined(WIN32) && !defined(MSDOS)
+#if defined(_WIN32)
 
-/* set in win32_init() */
-extern LARGE_INTEGER Curl_freq;
-extern bool Curl_isVistaOrGreater;
+#include <curl/curl.h>
+#include "system_win32.h"
 
 /* In case of bug fix this function has a counterpart in tool_util.c */
 struct curltime Curl_now(void)
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index 6886764..3ae4b61 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -163,9 +163,9 @@
 {
   size_t buffersize = bytes;
   size_t nread;
-
   curl_read_callback readfunc = NULL;
   void *extra_data = NULL;
+  int eof_index = 0;
 
 #ifndef CURL_DISABLE_HTTP
   if(data->state.trailers_state == TRAILERS_INITIALIZED) {
@@ -223,6 +223,7 @@
        */
     readfunc = trailers_read;
     extra_data = (void *)data;
+    eof_index = 1;
   }
   else
 #endif
@@ -231,10 +232,15 @@
     extra_data = data->state.in;
   }
 
-  Curl_set_in_callback(data, true);
-  nread = readfunc(data->req.upload_fromhere, 1,
-                   buffersize, extra_data);
-  Curl_set_in_callback(data, false);
+  if(!data->req.fread_eof[eof_index]) {
+    Curl_set_in_callback(data, true);
+    nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data);
+    Curl_set_in_callback(data, false);
+    /* make sure the callback is not called again after EOF */
+    data->req.fread_eof[eof_index] = !nread;
+  }
+  else
+    nread = 0;
 
   if(nread == CURL_READFUNC_ABORT) {
     failf(data, "operation aborted by callback");
@@ -407,363 +413,154 @@
   return TRUE;
 }
 
+/**
+ * Receive raw response data for the transfer.
+ * @param data         the transfer
+ * @param buf          buffer to keep response data received
+ * @param blen         length of `buf`
+ * @param eos_reliable if EOS detection in underlying connection is reliable
+ * @param err error    code in case of -1 return
+ * @return number of bytes read or -1 for error
+ */
+static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
+                                   char *buf, size_t blen,
+                                   bool eos_reliable,
+                                   CURLcode *err)
+{
+  ssize_t nread;
+
+  DEBUGASSERT(blen > 0);
+  /* If we are reading BODY data and the connection does NOT handle EOF
+   * and we know the size of the BODY data, limit the read amount */
+  if(!eos_reliable && !data->req.header && data->req.size != -1) {
+    curl_off_t totalleft = data->req.size - data->req.bytecount;
+    if(totalleft <= 0)
+      blen = 0;
+    else if(totalleft < (curl_off_t)blen)
+      blen = (size_t)totalleft;
+  }
+
+  if(!blen) {
+    /* want nothing - continue as if read nothing. */
+    DEBUGF(infof(data, "readwrite_data: we're done"));
+    *err = CURLE_OK;
+    return 0;
+  }
+
+  *err = Curl_read(data, data->conn->sockfd, buf, blen, &nread);
+  if(*err)
+    return -1;
+  DEBUGASSERT(nread >= 0);
+  *err = CURLE_OK;
+  return nread;
+}
+
 /*
  * Go ahead and do a read if we have a readable socket or if
  * the stream was rewound (in which case we have data in a
  * buffer)
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
  */
 static CURLcode readwrite_data(struct Curl_easy *data,
-                               struct connectdata *conn,
                                struct SingleRequest *k,
-                               int *didwhat, bool *done,
-                               bool *comeback)
+                               int *didwhat, bool *done)
 {
+  struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
-  ssize_t nread; /* number of bytes read */
-  size_t excess = 0; /* excess bytes read */
-  bool readmore = FALSE; /* used by RTP to signal for more data */
-  int maxloops = 100;
-  curl_off_t max_recv = data->set.max_recv_speed?
-                        data->set.max_recv_speed : CURL_OFF_T_MAX;
-  char *buf = data->state.buffer;
-  bool data_eof_handled = FALSE;
-  DEBUGASSERT(buf);
+  char *buf;
+  size_t blen;
+  int maxloops = 10;
+  curl_off_t total_received = 0;
+  bool is_multiplex = FALSE;
 
+  DEBUGASSERT(data->state.buffer);
   *done = FALSE;
-  *comeback = FALSE;
 
   /* This is where we loop until we have read everything there is to
      read or we get a CURLE_AGAIN */
   do {
-    bool is_empty_data = FALSE;
-    size_t buffersize = data->set.buffer_size;
-    size_t bytestoread = buffersize;
-    /* For HTTP/2 and HTTP/3, read data without caring about the content
-       length. This is safe because body in HTTP/2 is always segmented
-       thanks to its framing layer. Meanwhile, we have to call Curl_read
-       to ensure that http2_handle_stream_close is called when we read all
-       incoming bytes for a particular stream. */
-    bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
-    data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
+    bool is_eos = FALSE;
+    size_t bytestoread;
+    ssize_t nread;
 
-    if(!data_eof_handled && k->size != -1 && !k->header) {
-      /* make sure we don't read too much */
-      curl_off_t totalleft = k->size - k->bytecount;
-      if(totalleft < (curl_off_t)bytestoread)
-        bytestoread = (size_t)totalleft;
+    if(!is_multiplex) {
+      /* Multiplexed connection have inherent handling of EOF and we do not
+       * have to carefully restrict the amount we try to read.
+       * Multiplexed changes only in one direction. */
+      is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
     }
 
-    if(bytestoread) {
-      /* receive data from the network! */
-      result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
+    buf = data->state.buffer;
+    bytestoread = data->set.buffer_size;
 
-      /* read would've blocked */
+    /* Observe any imposed speed limit */
+    if(bytestoread && data->set.max_recv_speed) {
+      curl_off_t net_limit = data->set.max_recv_speed - total_received;
+      if(net_limit <= 0)
+        break;
+      if((size_t)net_limit < bytestoread)
+        bytestoread = (size_t)net_limit;
+    }
+
+    nread = Curl_xfer_recv_resp(data, buf, bytestoread,
+                                is_multiplex, &result);
+    if(nread < 0) {
       if(CURLE_AGAIN == result) {
         result = CURLE_OK;
         break; /* get out of loop */
       }
-
-      if(result>0)
-        goto out;
-    }
-    else {
-      /* read nothing but since we wanted nothing we consider this an OK
-         situation to proceed from */
-      DEBUGF(infof(data, "readwrite_data: we're done"));
-      nread = 0;
+      goto out; /* real error */
     }
 
-    if(!k->bytecount) {
-      Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-      if(k->exp100 > EXP100_SEND_DATA)
-        /* set time stamp to compare with when waiting for the 100 */
-        k->start100 = Curl_now();
-    }
-
+    /* We only get a 0-length read on EndOfStream */
+    blen = (size_t)nread;
+    is_eos = (blen == 0);
     *didwhat |= KEEP_RECV;
-    /* indicates data of zero size, i.e. empty file */
-    is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
 
-    if(0 < nread || is_empty_data) {
-      buf[nread] = 0;
-    }
-    if(!nread) {
+    if(!blen) {
       /* if we receive 0 or less here, either the data transfer is done or the
          server closed the connection and we bail out from this! */
-      if(data_eof_handled)
+      if(is_multiplex)
         DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
       else
         DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
-      k->keepon = 0; /* stop sending as well */
-      if(!is_empty_data)
-        break;
-    }
-
-    /* Default buffer to use when we write the buffer, it may be changed
-       in the flow below before the actual storing is done. */
-    k->str = buf;
-
-    if(conn->handler->readwrite) {
-      result = conn->handler->readwrite(data, conn, &nread, &readmore);
-      if(result)
-        goto out;
-      if(readmore)
-        break;
-    }
-
-#ifndef CURL_DISABLE_HTTP
-    /* Since this is a two-state thing, we check if we are parsing
-       headers at the moment or not. */
-    if(k->header) {
-      /* we are in parse-the-header-mode */
-      bool stop_reading = FALSE;
-      result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
-      if(result)
-        goto out;
-
-      if(conn->handler->readwrite &&
-         (k->maxdownload <= 0 && nread > 0)) {
-        result = conn->handler->readwrite(data, conn, &nread, &readmore);
-        if(result)
-          goto out;
-        if(readmore)
-          break;
-      }
-
-      if(stop_reading) {
-        /* We've stopped dealing with input, get out of the do-while loop */
-
-        if(nread > 0) {
-          infof(data,
-                "Excess found:"
-                " excess = %zd"
-                " url = %s (zero-length body)",
-                nread, data->state.up.path);
-        }
-
+      if(k->eos_written) { /* already did write this to client, leave */
+        k->keepon = 0; /* stop sending as well */
         break;
       }
     }
-#endif /* CURL_DISABLE_HTTP */
+    total_received += blen;
 
+    result = Curl_xfer_write_resp(data, buf, blen, is_eos, done);
+    if(result || *done)
+      goto out;
 
-    /* This is not an 'else if' since it may be a rest from the header
-       parsing, where the beginning of the buffer is headers and the end
-       is non-headers. */
-    if(!k->header && (nread > 0 || is_empty_data)) {
-
-      if(data->req.no_body) {
-        /* data arrives although we want none, bail out */
-        streamclose(conn, "ignoring body");
-        *done = TRUE;
-        result = CURLE_WEIRD_SERVER_REPLY;
-        goto out;
-      }
-
-#ifndef CURL_DISABLE_HTTP
-      if(0 == k->bodywrites && !is_empty_data) {
-        /* These checks are only made the first time we are about to
-           write a piece of the body */
-        if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
-          /* HTTP-only checks */
-          result = Curl_http_firstwrite(data, conn, done);
-          if(result || *done)
-            goto out;
-        }
-      } /* this is the first time we write a body part */
-#endif /* CURL_DISABLE_HTTP */
-
-      k->bodywrites++;
-
-      /* pass data to the debug function before it gets "dechunked" */
-      if(data->set.verbose) {
-        if(k->badheader) {
-          Curl_debug(data, CURLINFO_DATA_IN,
-                     Curl_dyn_ptr(&data->state.headerb),
-                     Curl_dyn_len(&data->state.headerb));
-          if(k->badheader == HEADER_PARTHEADER)
-            Curl_debug(data, CURLINFO_DATA_IN,
-                       k->str, (size_t)nread);
-        }
-        else
-          Curl_debug(data, CURLINFO_DATA_IN,
-                     k->str, (size_t)nread);
-      }
-
-#ifndef CURL_DISABLE_HTTP
-      if(k->chunk) {
-        /*
-         * Here comes a chunked transfer flying and we need to decode this
-         * properly.  While the name says read, this function both reads
-         * and writes away the data. The returned 'nread' holds the number
-         * of actual data it wrote to the client.
-         */
-        CURLcode extra;
-        CHUNKcode res =
-          Curl_httpchunk_read(data, k->str, nread, &nread, &extra);
-
-        if(CHUNKE_OK < res) {
-          if(CHUNKE_PASSTHRU_ERROR == res) {
-            failf(data, "Failed reading the chunked-encoded stream");
-            result = extra;
-            goto out;
-          }
-          failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
-          result = CURLE_RECV_ERROR;
-          goto out;
-        }
-        if(CHUNKE_STOP == res) {
-          /* we're done reading chunks! */
-          k->keepon &= ~KEEP_RECV; /* read no more */
-
-          /* N number of bytes at the end of the str buffer that weren't
-             written to the client. */
-          if(conn->chunk.datasize) {
-            infof(data, "Leftovers after chunking: % "
-                  CURL_FORMAT_CURL_OFF_T "u bytes",
-                  conn->chunk.datasize);
-          }
-        }
-        /* If it returned OK, we just keep going */
-      }
-#endif   /* CURL_DISABLE_HTTP */
-
-      /* Account for body content stored in the header buffer */
-      if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
-        size_t headlen = Curl_dyn_len(&data->state.headerb);
-        DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
-        k->bytecount += headlen;
-      }
-
-      if((-1 != k->maxdownload) &&
-         (k->bytecount + nread >= k->maxdownload)) {
-
-        excess = (size_t)(k->bytecount + nread - k->maxdownload);
-        if(excess > 0 && !k->ignorebody) {
-          infof(data,
-                "Excess found in a read:"
-                " excess = %zu"
-                ", size = %" CURL_FORMAT_CURL_OFF_T
-                ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
-                ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
-                excess, k->size, k->maxdownload, k->bytecount);
-          connclose(conn, "excess found in a read");
-        }
-
-        nread = (ssize_t) (k->maxdownload - k->bytecount);
-        if(nread < 0) /* this should be unusual */
-          nread = 0;
-
-        /* HTTP/3 over QUIC should keep reading until QUIC connection
-           is closed.  In contrast to HTTP/2 which can stop reading
-           from TCP connection, HTTP/3 over QUIC needs ACK from server
-           to ensure stream closure.  It should keep reading. */
-        if(!is_http3) {
-          k->keepon &= ~KEEP_RECV; /* we're done reading */
-        }
-      }
-
-      k->bytecount += nread;
-      max_recv -= nread;
-
-      result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
-      if(result)
-        goto out;
-
-      if(!k->chunk && (nread || k->badheader || is_empty_data)) {
-        /* If this is chunky transfer, it was already written */
-
-        if(k->badheader && !k->ignorebody) {
-          /* we parsed a piece of data wrongly assuming it was a header
-             and now we output it as body instead */
-          size_t headlen = Curl_dyn_len(&data->state.headerb);
-
-          /* Don't let excess data pollute body writes */
-          if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload)
-            result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                       Curl_dyn_ptr(&data->state.headerb),
-                                       headlen);
-          else
-            result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                       Curl_dyn_ptr(&data->state.headerb),
-                                       (size_t)k->maxdownload);
-
-          if(result)
-            goto out;
-        }
-        if(k->badheader < HEADER_ALLBAD) {
-          /* This switch handles various content encodings. If there's an
-             error here, be sure to check over the almost identical code
-             in http_chunks.c.
-             Make sure that ALL_CONTENT_ENCODINGS contains all the
-             encodings handled here. */
-          if(!k->ignorebody && nread) {
-#ifndef CURL_DISABLE_POP3
-            if(conn->handler->protocol & PROTO_FAMILY_POP3)
-              result = Curl_pop3_write(data, k->str, nread);
-            else
-#endif /* CURL_DISABLE_POP3 */
-              result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
-                                         nread);
-          }
-        }
-        k->badheader = HEADER_NORMAL; /* taken care of now */
-
-        if(result)
-          goto out;
-      }
-
-    } /* if(!header and data to read) */
-
-    if(conn->handler->readwrite && excess) {
-      /* Parse the excess data */
-      k->str += nread;
-
-      if(&k->str[excess] > &buf[data->set.buffer_size]) {
-        /* the excess amount was too excessive(!), make sure
-           it doesn't read out of buffer */
-        excess = &buf[data->set.buffer_size] - k->str;
-      }
-      nread = (ssize_t)excess;
-
-      result = conn->handler->readwrite(data, conn, &nread, &readmore);
-      if(result)
-        goto out;
-
-      if(readmore)
-        k->keepon |= KEEP_RECV; /* we're not done reading */
+    /* if we are done, we stop receiving. On multiplexed connections,
+     * we should read the EOS. Which may arrive as meta data after
+     * the bytes. Not taking it in might lead to RST of streams. */
+    if((!is_multiplex && data->req.download_done) || is_eos) {
+      data->req.keepon &= ~KEEP_RECV;
+    }
+    /* if we are PAUSEd or stopped receiving, leave the loop */
+    if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV))
       break;
-    }
 
-    if(is_empty_data) {
-      /* if we received nothing, the server closed the connection and we
-         are done */
-      k->keepon &= ~KEEP_RECV;
-    }
+  } while(maxloops-- && data_pending(data));
 
-    if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
-      /* this is a paused or stopped transfer */
-      break;
-    }
-
-  } while((max_recv > 0) && data_pending(data) && maxloops--);
-
-  if(maxloops <= 0 || max_recv <= 0) {
-    /* we mark it as read-again-please */
-    data->state.dselect_bits = CURL_CSELECT_IN;
-    *comeback = TRUE;
+  if(maxloops <= 0) {
+    /* did not read until EAGAIN, mark read-again-please */
+    data->state.select_bits = CURL_CSELECT_IN;
+    if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+      data->state.select_bits |= CURL_CSELECT_OUT;
   }
 
   if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
-     (conn->bits.close || data_eof_handled)) {
+     (conn->bits.close || is_multiplex)) {
     /* When we've read the entire thing and the close bit is set, the server
        may now close the connection. If there's now any kind of sending going
        on from our side, we need to stop that immediately. */
     infof(data, "we are done reading and this is set to close, stop send");
     k->keepon &= ~KEEP_SEND; /* no writing anymore either */
+    k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */
   }
 
 out:
@@ -783,7 +580,7 @@
   return CURLE_OK;
 }
 
-#if defined(WIN32) && defined(USE_WINSOCK)
+#if defined(_WIN32) && defined(USE_WINSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
@@ -977,7 +774,7 @@
     if(result)
       return result;
 
-#if defined(WIN32) && defined(USE_WINSOCK)
+#if defined(_WIN32) && defined(USE_WINSOCK)
     {
       struct curltime n = Curl_now();
       if(Curl_timediff(n, k->last_sndbuf_update) > 1000) {
@@ -1053,46 +850,41 @@
    * of our state machine are handling PAUSED transfers correctly. So, we
    * do not want to go there.
    * NOTE: we are only interested in PAUSE, not HOLD. */
-  return (((select_bits & CURL_CSELECT_IN) &&
-           (data->req.keepon & KEEP_RECV_PAUSE)) ||
-          ((select_bits & CURL_CSELECT_OUT) &&
-           (data->req.keepon & KEEP_SEND_PAUSE)));
+
+  /* if there is data in a direction not paused, return false */
+  if(((select_bits & CURL_CSELECT_IN) &&
+      !(data->req.keepon & KEEP_RECV_PAUSE)) ||
+     ((select_bits & CURL_CSELECT_OUT) &&
+      !(data->req.keepon & KEEP_SEND_PAUSE)))
+    return FALSE;
+
+  return (data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE));
 }
 
 /*
  * Curl_readwrite() is the low-level function to be called when data is to
  * be read and written to/from the connection.
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
  */
-CURLcode Curl_readwrite(struct connectdata *conn,
-                        struct Curl_easy *data,
-                        bool *done,
-                        bool *comeback)
+CURLcode Curl_readwrite(struct Curl_easy *data,
+                        bool *done)
 {
+  struct connectdata *conn = data->conn;
   struct SingleRequest *k = &data->req;
   CURLcode result;
   struct curltime now;
   int didwhat = 0;
   int select_bits;
 
-  if(data->state.dselect_bits) {
-    if(select_bits_paused(data, data->state.dselect_bits)) {
+  if(data->state.select_bits) {
+    if(select_bits_paused(data, data->state.select_bits)) {
       /* leave the bits unchanged, so they'll tell us what to do when
        * this transfer gets unpaused. */
-      DEBUGF(infof(data, "readwrite, dselect_bits, early return on PAUSED"));
+      DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED"));
       result = CURLE_OK;
       goto out;
     }
-    select_bits = data->state.dselect_bits;
-    data->state.dselect_bits = 0;
-  }
-  else if(conn->cselect_bits) {
-    /* CAVEAT: adding `select_bits_paused()` check here makes test640 hang
-     * (among others). Which hints at strange state handling in FTP land... */
-    select_bits = conn->cselect_bits;
-    conn->cselect_bits = 0;
+    select_bits = data->state.select_bits;
+    data->state.select_bits = 0;
   }
   else {
     curl_socket_t fd_read;
@@ -1130,7 +922,7 @@
      the stream was rewound (in which case we have data in a
      buffer) */
   if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
-    result = readwrite_data(data, conn, k, &didwhat, done, comeback);
+    result = readwrite_data(data, k, &didwhat, done);
     if(result || *done)
       goto out;
   }
@@ -1226,21 +1018,6 @@
       result = CURLE_PARTIAL_FILE;
       goto out;
     }
-    if(!(data->req.no_body) && k->chunk &&
-       (conn->chunk.state != CHUNK_STOP)) {
-      /*
-       * In chunked mode, return an error if the connection is closed prior to
-       * the empty (terminating) chunk is read.
-       *
-       * The condition above used to check for
-       * conn->proto.http->chunk.datasize != 0 which is true after reading
-       * *any* chunk, not just the empty chunk.
-       *
-       */
-      failf(data, "transfer closed with outstanding read data remaining");
-      result = CURLE_PARTIAL_FILE;
-      goto out;
-    }
     if(Curl_pgrsUpdate(data)) {
       result = CURLE_ABORTED_BY_CALLBACK;
       goto out;
@@ -1255,52 +1032,6 @@
   return result;
 }
 
-/*
- * Curl_single_getsock() gets called by the multi interface code when the app
- * has requested to get the sockets for the current connection. This function
- * will then be called once for every connection that the multi interface
- * keeps track of. This function will only be called for connections that are
- * in the proper state to have this information available.
- */
-int Curl_single_getsock(struct Curl_easy *data,
-                        struct connectdata *conn,
-                        curl_socket_t *sock)
-{
-  int bitmap = GETSOCK_BLANK;
-  unsigned sockindex = 0;
-
-  if(conn->handler->perform_getsock)
-    return conn->handler->perform_getsock(data, conn, sock);
-
-  /* don't include HOLD and PAUSE connections */
-  if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
-
-    DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
-
-    bitmap |= GETSOCK_READSOCK(sockindex);
-    sock[sockindex] = conn->sockfd;
-  }
-
-  /* don't include HOLD and PAUSE connections */
-  if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
-    if((conn->sockfd != conn->writesockfd) ||
-       bitmap == GETSOCK_BLANK) {
-      /* only if they are not the same socket and we have a readable
-         one, we increase index */
-      if(bitmap != GETSOCK_BLANK)
-        sockindex++; /* increase index if we need two entries */
-
-      DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
-
-      sock[sockindex] = conn->writesockfd;
-    }
-
-    bitmap |= GETSOCK_WRITESOCK(sockindex);
-  }
-
-  return bitmap;
-}
-
 /* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
    which means this gets called once for each subsequent redirect etc */
 void Curl_init_CONNECT(struct Curl_easy *data)
@@ -1430,8 +1161,7 @@
           return CURLE_OUT_OF_MEMORY;
       }
       wc = data->wildcard;
-      if((wc->state < CURLWC_INIT) ||
-         (wc->state >= CURLWC_CLEAN)) {
+      if(wc->state < CURLWC_INIT) {
         if(wc->ftpwc)
           wc->dtor(wc->ftpwc);
         Curl_safefree(wc->pattern);
@@ -1635,7 +1365,7 @@
           return Curl_uc_to_curlcode(uc);
         }
 
-        p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
+        p = Curl_get_scheme_handler(scheme);
         if(p && (p->protocol != data->info.conn_protocol)) {
           infof(data, "Clear auth, redirects scheme from %s to %s",
                 data->info.conn_scheme, scheme);
@@ -1948,3 +1678,41 @@
   } /* if(k->getheader || !data->req.no_body) */
 
 }
+
+CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
+                              char *buf, size_t blen,
+                              bool is_eos, bool *done)
+{
+  CURLcode result = CURLE_OK;
+
+  if(data->conn->handler->write_resp) {
+    /* protocol handlers offering this function take full responsibility
+     * for writing all received download data to the client. */
+    result = data->conn->handler->write_resp(data, buf, blen, is_eos, done);
+  }
+  else {
+    /* No special handling by protocol handler, write all received data
+     * as BODY to the client. */
+    if(blen || is_eos) {
+      int cwtype = CLIENTWRITE_BODY;
+      if(is_eos)
+        cwtype |= CLIENTWRITE_EOS;
+
+#ifndef CURL_DISABLE_POP3
+      if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) {
+        result = data->req.ignorebody? CURLE_OK :
+                 Curl_pop3_write(data, buf, blen);
+      }
+      else
+#endif /* CURL_DISABLE_POP3 */
+        result = Curl_client_write(data, cwtype, buf, blen);
+    }
+  }
+
+  if(!result && is_eos) {
+    /* If we wrote the EOS, we are definitely done */
+    data->req.eos_written = TRUE;
+    data->req.download_done = TRUE;
+  }
+  return result;
+}
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h
index 536ac24..0507f1a 100644
--- a/Utilities/cmcurl/lib/transfer.h
+++ b/Utilities/cmcurl/lib/transfer.h
@@ -45,9 +45,7 @@
 
 CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
                      followtype type);
-CURLcode Curl_readwrite(struct connectdata *conn,
-                        struct Curl_easy *data, bool *done,
-                        bool *comeback);
+CURLcode Curl_readwrite(struct Curl_easy *data, bool *done);
 int Curl_single_getsock(struct Curl_easy *data,
                         struct connectdata *conn, curl_socket_t *socks);
 CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
@@ -59,6 +57,23 @@
 CURLcode Curl_done_sending(struct Curl_easy *data,
                            struct SingleRequest *k);
 
+/**
+ * Write the transfer raw response bytes, as received from the connection.
+ * Will handle all passed bytes or return an error. By default, this will
+ * write the bytes as BODY to the client. Protocols may provide a
+ * "write_resp" callback in their handler to add specific treatment. E.g.
+ * HTTP parses response headers and passes them differently to the client.
+ * @param data     the transfer
+ * @param buf      the raw response bytes
+ * @param blen     the amount of bytes in `buf`
+ * @param is_eos   TRUE iff the connection indicates this to be the last
+ *                 bytes of the response
+ * @param done     on returnm, TRUE iff the response is complete
+ */
+CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
+                              char *buf, size_t blen,
+                              bool is_eos, bool *done);
+
 /* This sets up a forthcoming transfer */
 void
 Curl_setup_transfer (struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 61dad44..36395a1 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -168,130 +168,6 @@
   return h->family;
 }
 
-
-/*
- * Protocol table. Schemes (roughly) in 2019 popularity order:
- *
- * HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP,
- * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT
- */
-static const struct Curl_handler * const protocols[] = {
-
-#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
-  &Curl_handler_https,
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-  &Curl_handler_http,
-#endif
-
-#ifdef USE_WEBSOCKETS
-#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
-  &Curl_handler_wss,
-#endif
-
-#ifndef CURL_DISABLE_HTTP
-  &Curl_handler_ws,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_FTP
-  &Curl_handler_ftp,
-#endif
-
-#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
-  &Curl_handler_ftps,
-#endif
-
-#if defined(USE_SSH)
-  &Curl_handler_sftp,
-#endif
-
-#ifndef CURL_DISABLE_FILE
-  &Curl_handler_file,
-#endif
-
-#if defined(USE_SSH) && !defined(USE_WOLFSSH)
-  &Curl_handler_scp,
-#endif
-
-#ifndef CURL_DISABLE_SMTP
-  &Curl_handler_smtp,
-#ifdef USE_SSL
-  &Curl_handler_smtps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_LDAP
-  &Curl_handler_ldap,
-#if !defined(CURL_DISABLE_LDAPS) && \
-    ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
-     (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
-  &Curl_handler_ldaps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_IMAP
-  &Curl_handler_imap,
-#ifdef USE_SSL
-  &Curl_handler_imaps,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_TELNET
-  &Curl_handler_telnet,
-#endif
-
-#ifndef CURL_DISABLE_TFTP
-  &Curl_handler_tftp,
-#endif
-
-#ifndef CURL_DISABLE_POP3
-  &Curl_handler_pop3,
-#ifdef USE_SSL
-  &Curl_handler_pop3s,
-#endif
-#endif
-
-#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
-   (SIZEOF_CURL_OFF_T > 4)
-  &Curl_handler_smb,
-#ifdef USE_SSL
-  &Curl_handler_smbs,
-#endif
-#endif
-
-#ifndef CURL_DISABLE_RTSP
-  &Curl_handler_rtsp,
-#endif
-
-#ifndef CURL_DISABLE_MQTT
-  &Curl_handler_mqtt,
-#endif
-
-#ifndef CURL_DISABLE_GOPHER
-  &Curl_handler_gopher,
-#ifdef USE_SSL
-  &Curl_handler_gophers,
-#endif
-#endif
-
-#ifdef USE_LIBRTMP
-  &Curl_handler_rtmp,
-  &Curl_handler_rtmpt,
-  &Curl_handler_rtmpe,
-  &Curl_handler_rtmpte,
-  &Curl_handler_rtmps,
-  &Curl_handler_rtmpts,
-#endif
-
-#ifndef CURL_DISABLE_DICT
-  &Curl_handler_dict,
-#endif
-
-  (struct Curl_handler *) NULL
-};
-
 void Curl_freeset(struct Curl_easy *data)
 {
   /* Free all dynamic strings stored in the data->set substructure. */
@@ -320,8 +196,8 @@
   Curl_mime_cleanpart(&data->set.mimepost);
 
 #ifndef CURL_DISABLE_COOKIES
-  curl_slist_free_all(data->set.cookielist);
-  data->set.cookielist = NULL;
+  curl_slist_free_all(data->state.cookielist);
+  data->state.cookielist = NULL;
 #endif
 }
 
@@ -363,16 +239,18 @@
   /* Detach connection if any is left. This should not be normal, but can be
      the case for example with CONNECT_ONLY + recv/send (test 556) */
   Curl_detach_connection(data);
-  if(data->multi)
-    /* This handle is still part of a multi handle, take care of this first
-       and detach this handle from there. */
-    curl_multi_remove_handle(data->multi, data);
+  if(!data->state.internal) {
+    if(data->multi)
+      /* This handle is still part of a multi handle, take care of this first
+         and detach this handle from there. */
+      curl_multi_remove_handle(data->multi, data);
 
-  if(data->multi_easy) {
-    /* when curl_easy_perform() is used, it creates its own multi handle to
-       use and this is the one */
-    curl_multi_cleanup(data->multi_easy);
-    data->multi_easy = NULL;
+    if(data->multi_easy) {
+      /* when curl_easy_perform() is used, it creates its own multi handle to
+         use and this is the one */
+      curl_multi_cleanup(data->multi_easy);
+      data->multi_easy = NULL;
+    }
   }
 
   data->magic = 0; /* force a clear AFTER the possibly enforced removal from
@@ -412,7 +290,7 @@
 #ifndef CURL_DISABLE_HSTS
   if(!data->share || !data->share->hsts)
     Curl_hsts_cleanup(&data->hsts);
-  curl_slist_free_all(data->set.hstslist); /* clean up list */
+  curl_slist_free_all(data->state.hstslist); /* clean up list */
 #endif
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
@@ -457,8 +335,8 @@
   }
 #endif
 
-  Curl_mime_cleanpart(data->state.formp);
 #ifndef CURL_DISABLE_HTTP
+  Curl_mime_cleanpart(data->state.formp);
   Curl_safefree(data->state.formp);
 #endif
 
@@ -530,26 +408,16 @@
 
   Curl_mime_initpart(&set->mimepost);
 
-  /*
-   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
-   * switched off unless wanted.
-   */
+  Curl_ssl_easy_config_init(data);
 #ifndef CURL_DISABLE_DOH
   set->doh_verifyhost = TRUE;
   set->doh_verifypeer = TRUE;
 #endif
-  set->ssl.primary.verifypeer = TRUE;
-  set->ssl.primary.verifyhost = TRUE;
 #ifdef USE_SSH
   /* defaults to any auth type */
   set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
   set->new_directory_perms = 0755; /* Default permissions */
 #endif
-  set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by
-                                        default */
-#ifndef CURL_DISABLE_PROXY
-  set->proxy_ssl = set->ssl;
-#endif
 
   set->new_file_perms = 0644;    /* Default permissions */
   set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
@@ -566,11 +434,13 @@
 
   /* Set the default CA cert bundle/path detected/specified at build time.
    *
-   * If Schannel is the selected SSL backend then these locations are
-   * ignored. We allow setting CA location for schannel only when explicitly
-   * specified by the user via CURLOPT_CAINFO / --cacert.
+   * If Schannel or SecureTransport is the selected SSL backend then these
+   * locations are ignored. We allow setting CA location for schannel and
+   * securetransport when explicitly specified by the user via
+   *  CURLOPT_CAINFO / --cacert.
    */
-  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
+  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL &&
+     Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) {
 #if defined(CURL_CA_BUNDLE)
     result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
     if(result)
@@ -718,22 +588,18 @@
   Curl_safefree(conn->socks_proxy.passwd);
   Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
   Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
-  Curl_free_primary_ssl_config(&conn->proxy_ssl_config);
 #endif
   Curl_safefree(conn->user);
   Curl_safefree(conn->passwd);
   Curl_safefree(conn->sasl_authzid);
   Curl_safefree(conn->options);
   Curl_safefree(conn->oauth_bearer);
-#ifndef CURL_DISABLE_HTTP
-  Curl_dyn_free(&conn->trailer);
-#endif
   Curl_safefree(conn->host.rawalloc); /* host name buffer */
   Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
   Curl_safefree(conn->hostname_resolve);
   Curl_safefree(conn->secondaryhostname);
   Curl_safefree(conn->localdev);
-  Curl_free_primary_ssl_config(&conn->ssl_config);
+  Curl_ssl_conn_config_cleanup(conn);
 
 #ifdef USE_UNIX_SOCKETS
   Curl_safefree(conn->unix_domain_socket);
@@ -1059,11 +925,11 @@
                  bool *force_reuse,
                  bool *waitpipe)
 {
-  struct connectdata *check;
-  struct connectdata *chosen = 0;
+  struct connectdata *chosen = NULL;
   bool foundPendingCandidate = FALSE;
-  bool canmultiplex = IsMultiplexingPossible(data, needle);
+  bool canmultiplex = FALSE;
   struct connectbundle *bundle;
+  struct Curl_llist_element *curr;
 
 #ifdef USE_NTLM
   bool wantNTLMhttp = ((data->state.authhost.want &
@@ -1082,395 +948,368 @@
   bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
     (needle->handler->protocol & CURLPROTO_HTTP);
 
+  *usethis = NULL;
   *force_reuse = FALSE;
   *waitpipe = FALSE;
 
   /* Look up the bundle with all the connections to this particular host.
      Locks the connection cache, beware of early returns! */
   bundle = Curl_conncache_find_bundle(data, needle, data->state.conn_cache);
-  if(bundle) {
-    /* Max pipe length is zero (unlimited) for multiplexed connections */
-    struct Curl_llist_element *curr;
+  if(!bundle) {
+    CONNCACHE_UNLOCK(data);
+    return FALSE;
+  }
+  infof(data, "Found bundle for host: %p [%s]",
+        (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
+                         "can multiplex" : "serially"));
 
-    infof(data, "Found bundle for host: %p [%s]",
-          (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
-                           "can multiplex" : "serially"));
-
-    /* We can't multiplex if we don't know anything about the server */
-    if(canmultiplex) {
-      if(bundle->multiuse == BUNDLE_UNKNOWN) {
-        if(data->set.pipewait) {
-          infof(data, "Server doesn't support multiplex yet, wait");
-          *waitpipe = TRUE;
-          CONNCACHE_UNLOCK(data);
-          return FALSE; /* no reuse */
-        }
-
-        infof(data, "Server doesn't support multiplex (yet)");
-        canmultiplex = FALSE;
+  /* We can only multiplex iff the transfer allows it AND we know
+   * that the server we want to talk to supports it as well. */
+  canmultiplex = FALSE;
+  if(IsMultiplexingPossible(data, needle)) {
+    if(bundle->multiuse == BUNDLE_UNKNOWN) {
+      if(data->set.pipewait) {
+        infof(data, "Server doesn't support multiplex yet, wait");
+        *waitpipe = TRUE;
+        CONNCACHE_UNLOCK(data);
+        return FALSE; /* no reuse */
       }
-      if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
-         !Curl_multiplex_wanted(data->multi)) {
+      infof(data, "Server doesn't support multiplex (yet)");
+    }
+    else if(bundle->multiuse == BUNDLE_MULTIPLEX) {
+      if(Curl_multiplex_wanted(data->multi))
+        canmultiplex = TRUE;
+      else
         infof(data, "Could multiplex, but not asked to");
-        canmultiplex = FALSE;
-      }
-      if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
-        infof(data, "Can not multiplex, even if we wanted to");
-        canmultiplex = FALSE;
-      }
+    }
+    else if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
+      infof(data, "Can not multiplex, even if we wanted to");
+    }
+  }
+
+  curr = bundle->conn_list.head;
+  while(curr) {
+    struct connectdata *check = curr->ptr;
+    /* Get next node now. We might remove a dead `check` connection which
+     * would invalidate `curr` as well. */
+    curr = curr->next;
+
+    /* Note that if we use an HTTP proxy in normal mode (no tunneling), we
+     * check connections to that proxy and not to the actual remote server.
+     */
+    if(check->connect_only || check->bits.close)
+      /* connect-only or to-be-closed connections will not be reused */
+      continue;
+
+    if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
+       && data->set.ipver != check->ip_version) {
+      /* skip because the connection is not via the requested IP version */
+      continue;
     }
 
-    curr = bundle->conn_list.head;
-    while(curr) {
-      bool match = FALSE;
-      size_t multiplexed = 0;
-
-      /*
-       * Note that if we use an HTTP proxy in normal mode (no tunneling), we
-       * check connections to that proxy and not to the actual remote server.
-       */
-      check = curr->ptr;
-      curr = curr->next;
-
-      if(check->connect_only || check->bits.close)
-        /* connect-only or to-be-closed connections will not be reused */
+    if(!canmultiplex) {
+      if(Curl_resolver_asynch() &&
+         /* primary_ip[0] is NUL only if the resolving of the name hasn't
+            completed yet and until then we don't reuse this connection */
+         !check->primary_ip[0])
         continue;
+    }
 
-      if(extract_if_dead(check, data)) {
-        /* disconnect it */
-        Curl_disconnect(data, check, TRUE);
-        continue;
-      }
-
-      if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
-         && data->set.ipver != check->ip_version) {
-        /* skip because the connection is not via the requested IP version */
-        continue;
-      }
-
-      if(bundle->multiuse == BUNDLE_MULTIPLEX)
-        multiplexed = CONN_INUSE(check);
-
+    if(CONN_INUSE(check)) {
       if(!canmultiplex) {
-        if(multiplexed) {
-          /* can only happen within multi handles, and means that another easy
-             handle is using this connection */
-          continue;
-        }
-
-        if(Curl_resolver_asynch() &&
-           /* primary_ip[0] is NUL only if the resolving of the name hasn't
-              completed yet and until then we don't reuse this connection */
-           !check->primary_ip[0])
-          continue;
-      }
-
-      if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
-        foundPendingCandidate = TRUE;
-        /* Don't pick a connection that hasn't connected yet */
-        infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T
-              " isn't open enough, can't reuse", check->connection_id);
+        /* transfer can't be multiplexed and check is in use */
         continue;
       }
-
-#ifdef USE_UNIX_SOCKETS
-      if(needle->unix_domain_socket) {
-        if(!check->unix_domain_socket)
-          continue;
-        if(strcmp(needle->unix_domain_socket, check->unix_domain_socket))
-          continue;
-        if(needle->bits.abstract_unix_socket !=
-           check->bits.abstract_unix_socket)
-          continue;
-      }
-      else if(check->unix_domain_socket)
-        continue;
-#endif
-
-      if((needle->handler->flags&PROTOPT_SSL) !=
-         (check->handler->flags&PROTOPT_SSL))
-        /* don't do mixed SSL and non-SSL connections */
-        if(get_protocol_family(check->handler) !=
-           needle->handler->protocol || !check->bits.tls_upgraded)
-          /* except protocols that have been upgraded via TLS */
-          continue;
-
-#ifndef CURL_DISABLE_PROXY
-      if(needle->bits.httpproxy != check->bits.httpproxy ||
-         needle->bits.socksproxy != check->bits.socksproxy)
-        continue;
-
-      if(needle->bits.socksproxy &&
-        !socks_proxy_info_matches(&needle->socks_proxy,
-                                  &check->socks_proxy))
-        continue;
-#endif
-      if(needle->bits.conn_to_host != check->bits.conn_to_host)
-        /* don't mix connections that use the "connect to host" feature and
-         * connections that don't use this feature */
-        continue;
-
-      if(needle->bits.conn_to_port != check->bits.conn_to_port)
-        /* don't mix connections that use the "connect to port" feature and
-         * connections that don't use this feature */
-        continue;
-
-#ifndef CURL_DISABLE_PROXY
-      if(needle->bits.httpproxy) {
-        if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy))
-          continue;
-
-        if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy)
-          continue;
-
-        if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
-          /* use https proxy */
-          if(needle->http_proxy.proxytype !=
-             check->http_proxy.proxytype)
-            continue;
-          else if(needle->handler->flags&PROTOPT_SSL) {
-            /* use double layer ssl */
-            if(!Curl_ssl_config_matches(&needle->proxy_ssl_config,
-                                        &check->proxy_ssl_config))
-              continue;
-          }
-          else if(!Curl_ssl_config_matches(&needle->ssl_config,
-                                           &check->ssl_config))
-            continue;
-        }
-      }
-#endif
-
-      if(h2upgrade && !check->httpversion && canmultiplex) {
-        if(data->set.pipewait) {
-          infof(data, "Server upgrade doesn't support multiplex yet, wait");
-          *waitpipe = TRUE;
-          CONNCACHE_UNLOCK(data);
-          return FALSE; /* no reuse */
-        }
-        infof(data, "Server upgrade cannot be used");
-        continue; /* can't be used atm */
-      }
-
-      if(!canmultiplex && CONN_INUSE(check))
-        /* this request can't be multiplexed but the checked connection is
-           already in use so we skip it */
-        continue;
-
-      if(CONN_INUSE(check)) {
-        /* Subject for multiplex use if 'checks' belongs to the same multi
-           handle as 'data' is. */
+      else {
+        /* Could multiplex, but not when check belongs to another multi */
         struct Curl_llist_element *e = check->easyq.head;
         struct Curl_easy *entry = e->ptr;
         if(entry->multi != data->multi)
           continue;
       }
+    }
 
-      if(needle->localdev || needle->localport) {
-        /* If we are bound to a specific local end (IP+port), we must not
-           reuse a random other one, although if we didn't ask for a
-           particular one we can reuse one that was bound.
+    if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
+      foundPendingCandidate = TRUE;
+      /* Don't pick a connection that hasn't connected yet */
+      infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T
+            " isn't open enough, can't reuse", check->connection_id);
+      continue;
+    }
 
-           This comparison is a bit rough and too strict. Since the input
-           parameters can be specified in numerous ways and still end up the
-           same it would take a lot of processing to make it really accurate.
-           Instead, this matching will assume that reuses of bound connections
-           will most likely also reuse the exact same binding parameters and
-           missing out a few edge cases shouldn't hurt anyone very much.
-        */
-        if((check->localport != needle->localport) ||
-           (check->localportrange != needle->localportrange) ||
-           (needle->localdev &&
-            (!check->localdev || strcmp(check->localdev, needle->localdev))))
-          continue;
-      }
+    /* `check` is connected. if it is in use and does not support multiplex,
+     * we cannot use it. */
+    if(!check->bits.multiplex && CONN_INUSE(check))
+      continue;
 
-      if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
-        /* This protocol requires credentials per connection,
-           so verify that we're using the same name and password as well */
-        if(Curl_timestrcmp(needle->user, check->user) ||
-           Curl_timestrcmp(needle->passwd, check->passwd) ||
-           Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) ||
-           Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) {
-          /* one of them was different */
-          continue;
-        }
-      }
+#ifdef USE_UNIX_SOCKETS
+    if(needle->unix_domain_socket) {
+      if(!check->unix_domain_socket)
+        continue;
+      if(strcmp(needle->unix_domain_socket, check->unix_domain_socket))
+        continue;
+      if(needle->bits.abstract_unix_socket !=
+         check->bits.abstract_unix_socket)
+        continue;
+    }
+    else if(check->unix_domain_socket)
+      continue;
+#endif
 
-      /* GSS delegation differences do not actually affect every connection
-         and auth method, but this check takes precaution before efficiency */
-      if(needle->gssapi_delegation != check->gssapi_delegation)
+    if((needle->handler->flags&PROTOPT_SSL) !=
+       (check->handler->flags&PROTOPT_SSL))
+      /* don't do mixed SSL and non-SSL connections */
+      if(get_protocol_family(check->handler) !=
+         needle->handler->protocol || !check->bits.tls_upgraded)
+        /* except protocols that have been upgraded via TLS */
         continue;
 
-      /* If multiplexing isn't enabled on the h2 connection and h1 is
-         explicitly requested, handle it: */
-      if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
-         (((check->httpversion >= 20) &&
-           (data->state.httpwant < CURL_HTTP_VERSION_2_0))
-          || ((check->httpversion >= 30) &&
-              (data->state.httpwant < CURL_HTTP_VERSION_3))))
+    if(needle->bits.conn_to_host != check->bits.conn_to_host)
+      /* don't mix connections that use the "connect to host" feature and
+       * connections that don't use this feature */
+      continue;
+
+    if(needle->bits.conn_to_port != check->bits.conn_to_port)
+      /* don't mix connections that use the "connect to port" feature and
+       * connections that don't use this feature */
+      continue;
+
+#ifndef CURL_DISABLE_PROXY
+    if(needle->bits.httpproxy != check->bits.httpproxy ||
+       needle->bits.socksproxy != check->bits.socksproxy)
+      continue;
+
+    if(needle->bits.socksproxy &&
+      !socks_proxy_info_matches(&needle->socks_proxy,
+                                &check->socks_proxy))
+      continue;
+
+    if(needle->bits.httpproxy) {
+      if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy)
         continue;
-#ifdef USE_SSH
-      else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
-        if(!ssh_config_matches(needle, check))
+
+      if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy))
+        continue;
+
+      if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
+        /* https proxies come in different types, http/1.1, h2, ... */
+        if(needle->http_proxy.proxytype != check->http_proxy.proxytype)
           continue;
-      }
-#endif
-#ifndef CURL_DISABLE_FTP
-      else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
-        /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
-        if(Curl_timestrcmp(needle->proto.ftpc.account,
-                           check->proto.ftpc.account) ||
-           Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
-                           check->proto.ftpc.alternative_to_user) ||
-           (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) ||
-           (needle->proto.ftpc.ccc != check->proto.ftpc.ccc))
-          continue;
-      }
-#endif
-
-      if((needle->handler->flags&PROTOPT_SSL)
-#ifndef CURL_DISABLE_PROXY
-         || !needle->bits.httpproxy || needle->bits.tunnel_proxy
-#endif
-        ) {
-        /* The requested connection does not use an HTTP proxy or it uses SSL
-           or it is a non-SSL protocol tunneled or it is a non-SSL protocol
-           which is allowed to be upgraded via TLS */
-
-        if((strcasecompare(needle->handler->scheme, check->handler->scheme) ||
-            (get_protocol_family(check->handler) ==
-             needle->handler->protocol && check->bits.tls_upgraded)) &&
-           (!needle->bits.conn_to_host || strcasecompare(
-            needle->conn_to_host.name, check->conn_to_host.name)) &&
-           (!needle->bits.conn_to_port ||
-             needle->conn_to_port == check->conn_to_port) &&
-           strcasecompare(needle->host.name, check->host.name) &&
-           needle->remote_port == check->remote_port) {
-          /* The schemes match or the protocol family is the same and the
-             previous connection was TLS upgraded, and the hostname and host
-             port match */
-          if(needle->handler->flags & PROTOPT_SSL) {
-            /* This is a SSL connection so verify that we're using the same
-               SSL options as well */
-            if(!Curl_ssl_config_matches(&needle->ssl_config,
-                                        &check->ssl_config)) {
-              DEBUGF(infof(data,
-                           "Connection #%" CURL_FORMAT_CURL_OFF_T
-                           " has different SSL parameters, can't reuse",
-                           check->connection_id));
-              continue;
-            }
-          }
-          match = TRUE;
-        }
-      }
-      else {
-        /* The requested connection is using the same HTTP proxy in normal
-           mode (no tunneling) */
-        match = TRUE;
-      }
-
-      if(match) {
-#if defined(USE_NTLM)
-        /* If we are looking for an HTTP+NTLM connection, check if this is
-           already authenticating with the right credentials. If not, keep
-           looking so that we can reuse NTLM connections if
-           possible. (Especially we must not reuse the same connection if
-           partway through a handshake!) */
-        if(wantNTLMhttp) {
-          if(Curl_timestrcmp(needle->user, check->user) ||
-             Curl_timestrcmp(needle->passwd, check->passwd)) {
-
-            /* we prefer a credential match, but this is at least a connection
-               that can be reused and "upgraded" to NTLM */
-            if(check->http_ntlm_state == NTLMSTATE_NONE)
-              chosen = check;
-            continue;
-          }
-        }
-        else if(check->http_ntlm_state != NTLMSTATE_NONE) {
-          /* Connection is using NTLM auth but we don't want NTLM */
+        /* match SSL config to proxy */
+        if(!Curl_ssl_conn_config_match(data, check, TRUE)) {
+          DEBUGF(infof(data,
+            "Connection #%" CURL_FORMAT_CURL_OFF_T
+            " has different SSL proxy parameters, can't reuse",
+            check->connection_id));
           continue;
         }
-
-#ifndef CURL_DISABLE_PROXY
-        /* Same for Proxy NTLM authentication */
-        if(wantProxyNTLMhttp) {
-          /* Both check->http_proxy.user and check->http_proxy.passwd can be
-           * NULL */
-          if(!check->http_proxy.user || !check->http_proxy.passwd)
-            continue;
-
-          if(Curl_timestrcmp(needle->http_proxy.user,
-                             check->http_proxy.user) ||
-             Curl_timestrcmp(needle->http_proxy.passwd,
-                             check->http_proxy.passwd))
-            continue;
-        }
-        else if(check->proxy_ntlm_state != NTLMSTATE_NONE) {
-          /* Proxy connection is using NTLM auth but we don't want NTLM */
-          continue;
-        }
-#endif
-        if(wantNTLMhttp || wantProxyNTLMhttp) {
-          /* Credentials are already checked, we can use this connection */
-          chosen = check;
-
-          if((wantNTLMhttp &&
-             (check->http_ntlm_state != NTLMSTATE_NONE)) ||
-              (wantProxyNTLMhttp &&
-               (check->proxy_ntlm_state != NTLMSTATE_NONE))) {
-            /* We must use this connection, no other */
-            *force_reuse = TRUE;
-            break;
-          }
-
-          /* Continue look up for a better connection */
-          continue;
-        }
-#endif
-        if(canmultiplex) {
-          /* We can multiplex if we want to. Let's continue looking for
-             the optimal connection to use. */
-
-          if(!multiplexed) {
-            /* We have the optimal connection. Let's stop looking. */
-            chosen = check;
-            break;
-          }
-
-#ifdef USE_NGHTTP2
-          /* If multiplexed, make sure we don't go over concurrency limit */
-          if(check->bits.multiplex) {
-            if(multiplexed >= Curl_conn_get_max_concurrent(data, check,
-                                                           FIRSTSOCKET)) {
-              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
-                    multiplexed);
-              continue;
-            }
-            else if(multiplexed >=
-                    Curl_multi_max_concurrent_streams(data->multi)) {
-              infof(data, "client side MAX_CONCURRENT_STREAMS reached"
-                    ", skip (%zu)",
-                    multiplexed);
-              continue;
-            }
-          }
-#endif
-          /* When not multiplexed, we have a match here! */
-          chosen = check;
-          infof(data, "Multiplexed connection found");
-          break;
-        }
-        else {
-          /* We have found a connection. Let's stop searching. */
-          chosen = check;
-          break;
-        }
+        /* the SSL config to the server, which may apply here is checked
+         * further below */
       }
     }
-  }
+#endif
+
+    if(h2upgrade && !check->httpversion && canmultiplex) {
+      if(data->set.pipewait) {
+        infof(data, "Server upgrade doesn't support multiplex yet, wait");
+        *waitpipe = TRUE;
+        CONNCACHE_UNLOCK(data);
+        return FALSE; /* no reuse */
+      }
+      infof(data, "Server upgrade cannot be used");
+      continue; /* can't be used atm */
+    }
+
+    if(needle->localdev || needle->localport) {
+      /* If we are bound to a specific local end (IP+port), we must not
+         reuse a random other one, although if we didn't ask for a
+         particular one we can reuse one that was bound.
+
+         This comparison is a bit rough and too strict. Since the input
+         parameters can be specified in numerous ways and still end up the
+         same it would take a lot of processing to make it really accurate.
+         Instead, this matching will assume that reuses of bound connections
+         will most likely also reuse the exact same binding parameters and
+         missing out a few edge cases shouldn't hurt anyone very much.
+      */
+      if((check->localport != needle->localport) ||
+         (check->localportrange != needle->localportrange) ||
+         (needle->localdev &&
+          (!check->localdev || strcmp(check->localdev, needle->localdev))))
+        continue;
+    }
+
+    if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+      /* This protocol requires credentials per connection,
+         so verify that we're using the same name and password as well */
+      if(Curl_timestrcmp(needle->user, check->user) ||
+         Curl_timestrcmp(needle->passwd, check->passwd) ||
+         Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) ||
+         Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) {
+        /* one of them was different */
+        continue;
+      }
+    }
+
+    /* GSS delegation differences do not actually affect every connection
+       and auth method, but this check takes precaution before efficiency */
+    if(needle->gssapi_delegation != check->gssapi_delegation)
+      continue;
+
+    /* If looking for HTTP and the HTTP version  we want is less
+     * than the HTTP version of the check connection, continue looking */
+    if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
+       (((check->httpversion >= 20) &&
+         (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+        || ((check->httpversion >= 30) &&
+            (data->state.httpwant < CURL_HTTP_VERSION_3))))
+      continue;
+#ifdef USE_SSH
+    else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
+      if(!ssh_config_matches(needle, check))
+        continue;
+    }
+#endif
+#ifndef CURL_DISABLE_FTP
+    else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
+      /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
+      if(Curl_timestrcmp(needle->proto.ftpc.account,
+                         check->proto.ftpc.account) ||
+         Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
+                         check->proto.ftpc.alternative_to_user) ||
+         (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) ||
+         (needle->proto.ftpc.ccc != check->proto.ftpc.ccc))
+        continue;
+    }
+#endif
+
+    /* Additional match requirements if talking TLS OR
+     * not talking to a HTTP proxy OR using a tunnel through a proxy */
+    if((needle->handler->flags&PROTOPT_SSL)
+#ifndef CURL_DISABLE_PROXY
+       || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+#endif
+      ) {
+      /* Talking the same protocol scheme or a TLS upgraded protocol in the
+       * same protocol family? */
+      if(!strcasecompare(needle->handler->scheme, check->handler->scheme) &&
+         (get_protocol_family(check->handler) !=
+          needle->handler->protocol || !check->bits.tls_upgraded))
+        continue;
+
+      /* If needle has "conn_to_*" set, check must match this */
+      if((needle->bits.conn_to_host && !strcasecompare(
+          needle->conn_to_host.name, check->conn_to_host.name)) ||
+         (needle->bits.conn_to_port &&
+           needle->conn_to_port != check->conn_to_port))
+        continue;
+
+      /* hostname and port must match */
+      if(!strcasecompare(needle->host.name, check->host.name) ||
+         needle->remote_port != check->remote_port)
+        continue;
+
+      /* If talking TLS, check needs to use the same SSL options. */
+      if((needle->handler->flags & PROTOPT_SSL) &&
+         !Curl_ssl_conn_config_match(data, check, FALSE)) {
+        DEBUGF(infof(data,
+                     "Connection #%" CURL_FORMAT_CURL_OFF_T
+                     " has different SSL parameters, can't reuse",
+                     check->connection_id));
+        continue;
+      }
+    }
+
+#if defined(USE_NTLM)
+    /* If we are looking for an HTTP+NTLM connection, check if this is
+       already authenticating with the right credentials. If not, keep
+       looking so that we can reuse NTLM connections if
+       possible. (Especially we must not reuse the same connection if
+       partway through a handshake!) */
+    if(wantNTLMhttp) {
+      if(Curl_timestrcmp(needle->user, check->user) ||
+         Curl_timestrcmp(needle->passwd, check->passwd)) {
+
+        /* we prefer a credential match, but this is at least a connection
+           that can be reused and "upgraded" to NTLM */
+        if(check->http_ntlm_state == NTLMSTATE_NONE)
+          chosen = check;
+        continue;
+      }
+    }
+    else if(check->http_ntlm_state != NTLMSTATE_NONE) {
+      /* Connection is using NTLM auth but we don't want NTLM */
+      continue;
+    }
+
+#ifndef CURL_DISABLE_PROXY
+    /* Same for Proxy NTLM authentication */
+    if(wantProxyNTLMhttp) {
+      /* Both check->http_proxy.user and check->http_proxy.passwd can be
+       * NULL */
+      if(!check->http_proxy.user || !check->http_proxy.passwd)
+        continue;
+
+      if(Curl_timestrcmp(needle->http_proxy.user,
+                         check->http_proxy.user) ||
+         Curl_timestrcmp(needle->http_proxy.passwd,
+                         check->http_proxy.passwd))
+        continue;
+    }
+    else if(check->proxy_ntlm_state != NTLMSTATE_NONE) {
+      /* Proxy connection is using NTLM auth but we don't want NTLM */
+      continue;
+    }
+#endif
+    if(wantNTLMhttp || wantProxyNTLMhttp) {
+      /* Credentials are already checked, we may use this connection.
+       * With NTLM being weird as it is, we MUST use a
+       * connection where it has already been fully negotiated.
+       * If it has not, we keep on looking for a better one. */
+      chosen = check;
+
+      if((wantNTLMhttp &&
+         (check->http_ntlm_state != NTLMSTATE_NONE)) ||
+          (wantProxyNTLMhttp &&
+           (check->proxy_ntlm_state != NTLMSTATE_NONE))) {
+        /* We must use this connection, no other */
+        *force_reuse = TRUE;
+        break;
+      }
+      /* Continue look up for a better connection */
+      continue;
+    }
+#endif
+
+    if(CONN_INUSE(check)) {
+      DEBUGASSERT(canmultiplex);
+      DEBUGASSERT(check->bits.multiplex);
+      /* If multiplexed, make sure we don't go over concurrency limit */
+      if(CONN_INUSE(check) >=
+              Curl_multi_max_concurrent_streams(data->multi)) {
+        infof(data, "client side MAX_CONCURRENT_STREAMS reached"
+              ", skip (%zu)", CONN_INUSE(check));
+        continue;
+      }
+      if(CONN_INUSE(check) >=
+              Curl_conn_get_max_concurrent(data, check, FIRSTSOCKET)) {
+        infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
+              CONN_INUSE(check));
+        continue;
+      }
+      /* When not multiplexed, we have a match here! */
+      infof(data, "Multiplexed connection found");
+    }
+    else if(extract_if_dead(check, data)) {
+      /* disconnect it */
+      Curl_disconnect(data, check, TRUE);
+      continue;
+    }
+
+    /* We have found a connection. Let's stop searching. */
+    chosen = check;
+    break;
+  } /* loop over connection bundle */
 
   if(chosen) {
     /* mark it as used before releasing the lock */
@@ -1516,6 +1355,8 @@
 
   conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;     /* no file descriptor */
   conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
+  conn->sockfd = CURL_SOCKET_BAD;
+  conn->writesockfd = CURL_SOCKET_BAD;
   conn->connection_id = -1;    /* no ID */
   conn->port = -1; /* unknown at this point */
   conn->remote_port = -1; /* unknown at this point */
@@ -1561,17 +1402,6 @@
   conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
 #endif
-  conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus;
-  conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer;
-  conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost;
-  conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options;
-#ifndef CURL_DISABLE_PROXY
-  conn->proxy_ssl_config.verifystatus =
-    data->set.proxy_ssl.primary.verifystatus;
-  conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer;
-  conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost;
-  conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options;
-#endif
   conn->ip_version = data->set.ipver;
   conn->connect_only = data->set.connect_only;
   conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
@@ -1615,30 +1445,231 @@
   return NULL;
 }
 
-/* returns the handler if the given scheme is built-in */
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
-                                               size_t schemelen)
+const struct Curl_handler *Curl_get_scheme_handler(const char *scheme)
 {
-  const struct Curl_handler * const *pp;
-  const struct Curl_handler *p;
-  /* Scan protocol handler table and match against 'scheme'. The handler may
-     be changed later when the protocol specific setup function is called. */
-  if(schemelen == CURL_ZERO_TERMINATED)
-    schemelen = strlen(scheme);
-  for(pp = protocols; (p = *pp) != NULL; pp++)
-    if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen])
-      /* Protocol found in table. */
-      return p;
-  return NULL; /* not found */
+  return Curl_getn_scheme_handler(scheme, strlen(scheme));
 }
 
+/* returns the handler if the given scheme is built-in */
+const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
+                                                    size_t len)
+{
+  /* table generated by schemetable.c:
+     1. gcc schemetable.c && ./a.out
+     2. check how small the table gets
+     3. tweak the hash algorithm, then rerun from 1
+     4. when the table is good enough
+     5. copy the table into this source code
+     6. make sure this function uses the same hash function that worked for
+     schemetable.c
+     7. if needed, adjust the #ifdefs in schemetable.c and rerun
+     */
+  static const struct Curl_handler * const protocols[67] = {
+#ifndef CURL_DISABLE_FILE
+    &Curl_handler_file,
+#else
+    NULL,
+#endif
+    NULL, NULL,
+#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER)
+    &Curl_handler_gophers,
+#else
+    NULL,
+#endif
+    NULL,
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmpe,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_SMTP
+    &Curl_handler_smtp,
+#else
+    NULL,
+#endif
+#if defined(USE_SSH)
+    &Curl_handler_sftp,
+#else
+    NULL,
+#endif
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
+  (SIZEOF_CURL_OFF_T > 4)
+    &Curl_handler_smb,
+#else
+    NULL,
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)
+    &Curl_handler_smtps,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_TELNET
+    &Curl_handler_telnet,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_GOPHER
+    &Curl_handler_gopher,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_TFTP
+    &Curl_handler_tftp,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
+    &Curl_handler_ftps,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_HTTP
+    &Curl_handler_http,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_IMAP
+    &Curl_handler_imap,
+#else
+    NULL,
+#endif
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmps,
+#else
+    NULL,
+#endif
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmpt,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#if !defined(CURL_DISABLE_LDAP) && \
+  !defined(CURL_DISABLE_LDAPS) && \
+  ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
+   (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
+    &Curl_handler_ldaps,
+#else
+    NULL,
+#endif
+#if defined(USE_WEBSOCKETS) && \
+  defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+    &Curl_handler_wss,
+#else
+    NULL,
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+    &Curl_handler_https,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+#ifndef CURL_DISABLE_RTSP
+    &Curl_handler_rtsp,
+#else
+    NULL,
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \
+  defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4)
+    &Curl_handler_smbs,
+#else
+    NULL,
+#endif
+#if defined(USE_SSH) && !defined(USE_WOLFSSH)
+    &Curl_handler_scp,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#ifndef CURL_DISABLE_POP3
+    &Curl_handler_pop3,
+#else
+    NULL,
+#endif
+    NULL, NULL,
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmp,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmpte,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#ifndef CURL_DISABLE_DICT
+    &Curl_handler_dict,
+#else
+    NULL,
+#endif
+    NULL, NULL, NULL,
+#ifndef CURL_DISABLE_MQTT
+    &Curl_handler_mqtt,
+#else
+    NULL,
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
+    &Curl_handler_pop3s,
+#else
+    NULL,
+#endif
+#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)
+    &Curl_handler_imaps,
+#else
+    NULL,
+#endif
+    NULL,
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+    &Curl_handler_ws,
+#else
+    NULL,
+#endif
+    NULL,
+#ifdef USE_LIBRTMP
+    &Curl_handler_rtmpts,
+#else
+    NULL,
+#endif
+#ifndef CURL_DISABLE_LDAP
+    &Curl_handler_ldap,
+#else
+    NULL,
+#endif
+    NULL, NULL,
+#ifndef CURL_DISABLE_FTP
+    &Curl_handler_ftp,
+#else
+    NULL,
+#endif
+  };
+
+  if(len && (len <= 7)) {
+    const char *s = scheme;
+    size_t l = len;
+    const struct Curl_handler *h;
+    unsigned int c = 978;
+    while(l) {
+      c <<= 5;
+      c += Curl_raw_tolower(*s);
+      s++;
+      l--;
+    }
+
+    h = protocols[c % 67];
+    if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len])
+      return h;
+  }
+  return NULL;
+}
 
 static CURLcode findprotocol(struct Curl_easy *data,
                              struct connectdata *conn,
                              const char *protostr)
 {
-  const struct Curl_handler *p = Curl_builtin_scheme(protostr,
-                                                     CURL_ZERO_TERMINATED);
+  const struct Curl_handler *p = Curl_get_scheme_handler(protostr);
 
   if(p && /* Protocol found in table. Check if allowed */
      (data->set.allowed_protocols & p->protocol)) {
@@ -1652,7 +1683,6 @@
     else {
       /* Perform setup complement if some. */
       conn->handler = conn->given = p;
-
       /* 'port' and 'remote_port' are set in setup_connection_internals() */
       return CURLE_OK;
     }
@@ -1661,8 +1691,9 @@
   /* The protocol was not found in the table, but we don't have to assign it
      to anything since it is already assigned to a dummy-struct in the
      create_conn() function when the connectdata struct is allocated. */
-  failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME,
-        protostr);
+  failf(data, "Protocol \"%s\" %s%s", protostr,
+        p ? "disabled" : "not supported",
+        data->state.this_is_a_follow ? " (in redirect)":"");
 
   return CURLE_UNSUPPORTED_PROTOCOL;
 }
@@ -1705,14 +1736,14 @@
       conn->scope_id = (unsigned int)scope;
 #if defined(HAVE_IF_NAMETOINDEX)
     else {
-#elif defined(WIN32)
+#elif defined(_WIN32)
     else if(Curl_if_nametoindex) {
 #endif
 
-#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32)
+#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32)
       /* Zone identifier is not numeric */
       unsigned int scopeidx = 0;
-#if defined(WIN32)
+#if defined(_WIN32)
       scopeidx = Curl_if_nametoindex(zoneid);
 #else
       scopeidx = if_nametoindex(zoneid);
@@ -1727,7 +1758,7 @@
       else
         conn->scope_id = scopeidx;
     }
-#endif /* HAVE_IF_NAMETOINDEX || WIN32 */
+#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */
 
     free(zoneid);
   }
@@ -3596,85 +3627,10 @@
   conn->send[SECONDARYSOCKET] = Curl_conn_send;
   conn->bits.tcp_fastopen = data->set.tcp_fastopen;
 
-  /* Get a cloned copy of the SSL config situation stored in the
-     connection struct. But to get this going nicely, we must first make
-     sure that the strings in the master copy are pointing to the correct
-     strings in the session handle strings array!
-
-     Keep in mind that the pointers in the master copy are pointing to strings
-     that will be freed as part of the Curl_easy struct, but all cloned
-     copies will be separately allocated.
-  */
-  data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
-  data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
-  data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
-  data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
-  data->set.ssl.primary.cipher_list =
-    data->set.str[STRING_SSL_CIPHER_LIST];
-  data->set.ssl.primary.cipher_list13 =
-    data->set.str[STRING_SSL_CIPHER13_LIST];
-  data->set.ssl.primary.pinned_key =
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
-  data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
-  data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
-  data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES];
-
-#ifndef CURL_DISABLE_PROXY
-  data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
-  data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
-  data->set.proxy_ssl.primary.cipher_list =
-    data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
-  data->set.proxy_ssl.primary.cipher_list13 =
-    data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
-  data->set.proxy_ssl.primary.pinned_key =
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
-  data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
-  data->set.proxy_ssl.primary.ca_info_blob =
-    data->set.blobs[BLOB_CAINFO_PROXY];
-  data->set.proxy_ssl.primary.issuercert =
-    data->set.str[STRING_SSL_ISSUERCERT_PROXY];
-  data->set.proxy_ssl.primary.issuercert_blob =
-    data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
-  data->set.proxy_ssl.primary.CRLfile =
-    data->set.str[STRING_SSL_CRLFILE_PROXY];
-  data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
-  data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
-  data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
-  data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
-  data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY];
-  data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
-#endif
-  data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE];
-  data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
-  data->set.ssl.key = data->set.str[STRING_KEY];
-  data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
-  data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD];
-  data->set.ssl.primary.clientcert = data->set.str[STRING_CERT];
-#ifdef USE_TLS_SRP
-  data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
-  data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
-#ifndef CURL_DISABLE_PROXY
-  data->set.proxy_ssl.primary.username =
-    data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
-  data->set.proxy_ssl.primary.password =
-    data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
-#endif
-#endif
-  data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
-
-  if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
-                                    &conn->ssl_config)) {
-    result = CURLE_OUT_OF_MEMORY;
+  /* Complete the easy's SSL configuration for connection cache matching */
+  result = Curl_ssl_easy_config_complete(data);
+  if(result)
     goto out;
-  }
-
-#ifndef CURL_DISABLE_PROXY
-  if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary,
-                                    &conn->proxy_ssl_config)) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-#endif
 
   prune_dead_connections(data);
 
@@ -3789,6 +3745,12 @@
        * This is a brand new connection, so let's store it in the connection
        * cache of ours!
        */
+      result = Curl_ssl_conn_config_init(data, conn);
+      if(result) {
+        DEBUGF(fprintf(stderr, "Error: init connection ssl config\n"));
+        goto out;
+      }
+
       Curl_attach_connection(data, conn);
       result = Curl_conncache_add_conn(data);
       if(result)
@@ -3976,6 +3938,7 @@
   k->bytecount = 0;
   k->ignorebody = FALSE;
 
+  Curl_client_cleanup(data);
   Curl_speedinit(data);
   Curl_pgrsSetUploadCounter(data, 0);
   Curl_pgrsSetDownloadCounter(data, 0);
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h
index f6a5b25..7c1a29b 100644
--- a/Utilities/cmcurl/lib/url.h
+++ b/Utilities/cmcurl/lib/url.h
@@ -46,8 +46,13 @@
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
 
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
-                                               size_t schemelen);
+/* Get protocol handler for a URI scheme
+ * @param scheme URI scheme, case-insensitive
+ * @return NULL of handler not found
+ */
+const struct Curl_handler *Curl_get_scheme_handler(const char *scheme);
+const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
+                                                    size_t len);
 
 #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
 #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index 4efab61..3cd0362 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -126,6 +126,9 @@
   return sep < query ? sep : query;
 }
 
+/* convert CURLcode to CURLUcode */
+#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE :   \
+                  CURLUE_OUT_OF_MEMORY)
 /*
  * Decide whether a character in a URL must be escaped.
  */
@@ -146,6 +149,7 @@
   bool left = !query;
   const unsigned char *iptr;
   const unsigned char *host_sep = (const unsigned char *) url;
+  CURLcode result;
 
   if(!relative)
     host_sep = (const unsigned char *) find_host_sep(url);
@@ -154,20 +158,19 @@
       len; iptr++, len--) {
 
     if(iptr < host_sep) {
-      if(Curl_dyn_addn(o, iptr, 1))
-        return CURLUE_OUT_OF_MEMORY;
+      result = Curl_dyn_addn(o, iptr, 1);
+      if(result)
+        return cc2cu(result);
       continue;
     }
 
     if(*iptr == ' ') {
-      if(left) {
-        if(Curl_dyn_addn(o, "%20", 3))
-          return CURLUE_OUT_OF_MEMORY;
-      }
-      else {
-        if(Curl_dyn_addn(o, "+", 1))
-          return CURLUE_OUT_OF_MEMORY;
-      }
+      if(left)
+        result = Curl_dyn_addn(o, "%20", 3);
+      else
+        result = Curl_dyn_addn(o, "+", 1);
+      if(result)
+        return cc2cu(result);
       continue;
     }
 
@@ -178,13 +181,12 @@
       char out[3]={'%'};
       out[1] = hexdigits[*iptr>>4];
       out[2] = hexdigits[*iptr & 0xf];
-      if(Curl_dyn_addn(o, out, 3))
-        return CURLUE_OUT_OF_MEMORY;
+      result = Curl_dyn_addn(o, out, 3);
     }
-    else {
-      if(Curl_dyn_addn(o, iptr, 1))
-        return CURLUE_OUT_OF_MEMORY;
-    }
+    else
+      result = Curl_dyn_addn(o, iptr, 1);
+    if(result)
+      return cc2cu(result);
   }
 
   return CURLUE_OK;
@@ -206,7 +208,7 @@
   (void)buflen; /* only used in debug-builds */
   if(buf)
     buf[0] = 0; /* always leave a defined value in buf */
-#ifdef WIN32
+#ifdef _WIN32
   if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url))
     return 0;
 #endif
@@ -248,7 +250,7 @@
  *
  * Note that this function destroys the 'base' string.
  */
-static char *concat_url(char *base, const char *relurl)
+static CURLcode concat_url(char *base, const char *relurl, char **newurl)
 {
   /***
    TRY to append this new path to the old URL
@@ -260,6 +262,9 @@
   char *pathsep;
   bool host_changed = FALSE;
   const char *useurl = relurl;
+  CURLcode result = CURLE_OK;
+  CURLUcode uc;
+  *newurl = NULL;
 
   /* protsep points to the start of the host name */
   protsep = strstr(base, "//");
@@ -360,21 +365,27 @@
   Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
 
   /* copy over the root url part */
-  if(Curl_dyn_add(&newest, base))
-    return NULL;
+  result = Curl_dyn_add(&newest, base);
+  if(result)
+    return result;
 
   /* check if we need to append a slash */
   if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
     ;
   else {
-    if(Curl_dyn_addn(&newest, "/", 1))
-      return NULL;
+    result = Curl_dyn_addn(&newest, "/", 1);
+    if(result)
+      return result;
   }
 
   /* then append the new piece on the right side */
-  urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE);
+  uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed,
+                     FALSE);
+  if(uc)
+    return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY;
 
-  return Curl_dyn_ptr(&newest);
+  *newurl = Curl_dyn_ptr(&newest);
+  return CURLE_OK;
 }
 
 /* scan for byte values <= 31, 127 and sometimes space */
@@ -446,7 +457,7 @@
 
   /* if this is a known scheme, get some details */
   if(u->scheme)
-    h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+    h = Curl_get_scheme_handler(u->scheme);
 
   /* We could use the login information in the URL so extract it. Only parse
      options if the handler says we should. Note that 'h' might be NULL! */
@@ -712,24 +723,30 @@
     Curl_dyn_reset(host);
 
     result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           parts[0] >> 24, (parts[0] >> 16) & 0xff,
-                           (parts[0] >> 8) & 0xff, parts[0] & 0xff);
+                           (unsigned int)(parts[0] >> 24),
+                           (unsigned int)((parts[0] >> 16) & 0xff),
+                           (unsigned int)((parts[0] >> 8) & 0xff),
+                           (unsigned int)(parts[0] & 0xff));
     break;
   case 1: /* a.b -- 8.24 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xffffff))
       return HOST_NAME;
     Curl_dyn_reset(host);
     result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           parts[0], (parts[1] >> 16) & 0xff,
-                           (parts[1] >> 8) & 0xff, parts[1] & 0xff);
+                           (unsigned int)(parts[0]),
+                           (unsigned int)((parts[1] >> 16) & 0xff),
+                           (unsigned int)((parts[1] >> 8) & 0xff),
+                           (unsigned int)(parts[1] & 0xff));
     break;
   case 2: /* a.b.c -- 8.8.16 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
       return HOST_NAME;
     Curl_dyn_reset(host);
     result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           parts[0], parts[1], (parts[2] >> 8) & 0xff,
-                           parts[2] & 0xff);
+                           (unsigned int)(parts[0]),
+                           (unsigned int)(parts[1]),
+                           (unsigned int)((parts[2] >> 8) & 0xff),
+                           (unsigned int)(parts[2] & 0xff));
     break;
   case 3: /* a.b.c.d -- 8.8.8.8 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
@@ -737,7 +754,10 @@
       return HOST_NAME;
     Curl_dyn_reset(host);
     result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           parts[0], parts[1], parts[2], parts[3]);
+                           (unsigned int)(parts[0]),
+                           (unsigned int)(parts[1]),
+                           (unsigned int)(parts[2]),
+                           (unsigned int)(parts[3]));
     break;
   }
   if(result)
@@ -766,7 +786,7 @@
     result = Curl_dyn_addn(host, decoded, dlen);
     free(decoded);
     if(result)
-      return CURLUE_OUT_OF_MEMORY;
+      return cc2cu(result);
   }
 
   return CURLUE_OK;
@@ -779,22 +799,24 @@
                                  bool has_scheme)
 {
   size_t offset;
-  CURLUcode result;
+  CURLUcode uc;
+  CURLcode result;
 
   /*
    * Parse the login details and strip them out of the host name.
    */
-  result = parse_hostname_login(u, auth, authlen, flags, &offset);
-  if(result)
+  uc = parse_hostname_login(u, auth, authlen, flags, &offset);
+  if(uc)
     goto out;
 
-  if(Curl_dyn_addn(host, auth + offset, authlen - offset)) {
-    result = CURLUE_OUT_OF_MEMORY;
+  result = Curl_dyn_addn(host, auth + offset, authlen - offset);
+  if(result) {
+    uc = cc2cu(result);
     goto out;
   }
 
-  result = Curl_parse_port(u, host, has_scheme);
-  if(result)
+  uc = Curl_parse_port(u, host, has_scheme);
+  if(uc)
     goto out;
 
   if(!Curl_dyn_len(host))
@@ -804,24 +826,24 @@
   case HOST_IPV4:
     break;
   case HOST_IPV6:
-    result = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+    uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
     break;
   case HOST_NAME:
-    result = urldecode_host(host);
-    if(!result)
-      result = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+    uc = urldecode_host(host);
+    if(!uc)
+      uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
     break;
   case HOST_ERROR:
-    result = CURLUE_OUT_OF_MEMORY;
+    uc = CURLUE_OUT_OF_MEMORY;
     break;
   case HOST_BAD:
   default:
-    result = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */
+    uc = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */
     break;
   }
 
 out:
-  return result;
+  return uc;
 }
 
 CURLUcode Curl_url_set_authority(CURLU *u, const char *authority,
@@ -1056,7 +1078,7 @@
           ptr += 9; /* now points to the slash after the host */
         }
         else {
-#if defined(WIN32)
+#if defined(_WIN32)
           size_t len;
 
           /* the host name, NetBIOS computer name, can not contain disallowed
@@ -1070,8 +1092,9 @@
 
           len = path - ptr;
           if(len) {
-            if(Curl_dyn_addn(&host, ptr, len)) {
-              result = CURLUE_OUT_OF_MEMORY;
+            CURLcode code = Curl_dyn_addn(&host, ptr, len);
+            if(code) {
+              result = cc2cu(code);
               goto fail;
             }
             uncpath = TRUE;
@@ -1095,7 +1118,7 @@
       /* no host for file: URLs by default */
       Curl_dyn_reset(&host);
 
-#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
+#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__)
     /* Don't allow Windows drive letters when not in Windows.
      * This catches both "file:/c:" and "file:c:" */
     if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) ||
@@ -1129,7 +1152,7 @@
       }
 
       schemep = schemebuf;
-      if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) &&
+      if(!Curl_get_scheme_handler(schemep) &&
          !(flags & CURLU_NON_SUPPORT_SCHEME)) {
         result = CURLUE_UNSUPPORTED_SCHEME;
         goto fail;
@@ -1224,14 +1247,13 @@
       if(flags & CURLU_URLENCODE) {
         struct dynbuf enc;
         Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-        if(urlencode_str(&enc, fragment + 1, fraglen, TRUE, FALSE)) {
-          result = CURLUE_OUT_OF_MEMORY;
+        result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE);
+        if(result)
           goto fail;
-        }
         u->fragment = Curl_dyn_ptr(&enc);
       }
       else {
-        u->fragment = Curl_memdup(fragment + 1, fraglen);
+        u->fragment = Curl_memdup0(fragment + 1, fraglen - 1);
         if(!u->fragment) {
           result = CURLUE_OUT_OF_MEMORY;
           goto fail;
@@ -1242,7 +1264,6 @@
     pathlen -= fraglen;
   }
 
-  DEBUGASSERT(pathlen < urllen);
   query = memchr(path, '?', pathlen);
   if(query) {
     size_t qlen = fragment ? (size_t)(fragment - query) :
@@ -1253,19 +1274,17 @@
         struct dynbuf enc;
         Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
         /* skip the leading question mark */
-        if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) {
-          result = CURLUE_OUT_OF_MEMORY;
+        result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE);
+        if(result)
           goto fail;
-        }
         u->query = Curl_dyn_ptr(&enc);
       }
       else {
-        u->query = Curl_memdup(query + 1, qlen);
+        u->query = Curl_memdup0(query + 1, qlen - 1);
         if(!u->query) {
           result = CURLUE_OUT_OF_MEMORY;
           goto fail;
         }
-        u->query[qlen - 1] = 0;
       }
     }
     else {
@@ -1281,10 +1300,9 @@
   if(pathlen && (flags & CURLU_URLENCODE)) {
     struct dynbuf enc;
     Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-    if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) {
-      result = CURLUE_OUT_OF_MEMORY;
+    result = urlencode_str(&enc, path, pathlen, TRUE, FALSE);
+    if(result)
       goto fail;
-    }
     pathlen = Curl_dyn_len(&enc);
     path = u->path = Curl_dyn_ptr(&enc);
   }
@@ -1295,12 +1313,11 @@
   }
   else {
     if(!u->path) {
-      u->path = Curl_memdup(path, pathlen + 1);
+      u->path = Curl_memdup0(path, pathlen);
       if(!u->path) {
         result = CURLUE_OUT_OF_MEMORY;
         goto fail;
       }
-      u->path[pathlen] = 0;
       path = u->path;
     }
     else if(flags & CURLU_URLENCODE)
@@ -1352,7 +1369,7 @@
  */
 CURLU *curl_url(void)
 {
-  return calloc(sizeof(struct Curl_URL), 1);
+  return calloc(1, sizeof(struct Curl_URL));
 }
 
 void curl_url_cleanup(CURLU *u)
@@ -1374,7 +1391,7 @@
 
 CURLU *curl_url_dup(const CURLU *in)
 {
-  struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1);
+  struct Curl_URL *u = calloc(1, sizeof(struct Curl_URL));
   if(u) {
     DUP(u, in, scheme);
     DUP(u, in, user);
@@ -1447,8 +1464,7 @@
     if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
       /* there's no stored port number, but asked to deliver
          a default one for the scheme */
-      const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+      const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
       if(h) {
         msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
         ptr = portbuf;
@@ -1457,8 +1473,7 @@
     else if(ptr && u->scheme) {
       /* there is a stored port number, but ask to inhibit if
          it matches the default one for the scheme */
-      const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
+      const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
       if(h && (h->defport == u->portnum) &&
          (flags & CURLU_NO_DEFAULT_PORT))
         ptr = NULL;
@@ -1503,7 +1518,7 @@
       else
         return CURLUE_NO_SCHEME;
 
-      h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
+      h = Curl_get_scheme_handler(scheme);
       if(!port && (flags & CURLU_DEFAULT_PORT)) {
         /* there's no stored port number, but asked to deliver
            a default one for the scheme */
@@ -1596,7 +1611,7 @@
   if(ptr) {
     size_t partlen = strlen(ptr);
     size_t i = 0;
-    *part = Curl_memdup(ptr, partlen + 1);
+    *part = Curl_memdup0(ptr, partlen);
     if(!*part)
       return CURLUE_OUT_OF_MEMORY;
     if(plusdecode) {
@@ -1623,10 +1638,11 @@
     }
     if(urlencode) {
       struct dynbuf enc;
+      CURLUcode uc;
       Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-      if(urlencode_str(&enc, *part, partlen, TRUE,
-                       what == CURLUPART_QUERY))
-        return CURLUE_OUT_OF_MEMORY;
+      uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY);
+      if(uc)
+        return uc;
       free(*part);
       *part = Curl_dyn_ptr(&enc);
     }
@@ -1743,9 +1759,8 @@
     if((plen > MAX_SCHEME_LEN) || (plen < 1))
       /* too long or too short */
       return CURLUE_BAD_SCHEME;
-    if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
-       /* verify that it is a fine scheme */
-       !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED))
+   /* verify that it is a fine scheme */
+    if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
       return CURLUE_UNSUPPORTED_SCHEME;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
@@ -1812,7 +1827,8 @@
      * If the existing contents is enough for a URL, allow a relative URL to
      * replace it.
      */
-    CURLUcode result;
+    CURLcode result;
+    CURLUcode uc;
     char *oldurl;
     char *redired_url;
 
@@ -1832,14 +1848,14 @@
 
     /* apply the relative part to create a new URL
      * and replace the existing one with it. */
-    redired_url = concat_url(oldurl, part);
+    result = concat_url(oldurl, part, &redired_url);
     free(oldurl);
-    if(!redired_url)
-      return CURLUE_OUT_OF_MEMORY;
+    if(result)
+      return cc2cu(result);
 
-    result = parseurl_and_replace(redired_url, u, flags);
+    uc = parseurl_and_replace(redired_url, u, flags);
     free(redired_url);
-    return result;
+    return uc;
   }
   default:
     return CURLUE_UNKNOWN_PART;
@@ -1853,7 +1869,7 @@
     if(leadingslash && (part[0] != '/')) {
       CURLcode result = Curl_dyn_addn(&enc, "/", 1);
       if(result)
-        return CURLUE_OUT_OF_MEMORY;
+        return cc2cu(result);
     }
     if(urlencode) {
       const unsigned char *i;
@@ -1873,7 +1889,7 @@
             equalsencode = FALSE;
           result = Curl_dyn_addn(&enc, i, 1);
           if(result)
-            return CURLUE_OUT_OF_MEMORY;
+            return cc2cu(result);
         }
         else {
           char out[3]={'%'};
@@ -1881,7 +1897,7 @@
           out[2] = hexdigits[*i & 0xf];
           result = Curl_dyn_addn(&enc, out, 3);
           if(result)
-            return CURLUE_OUT_OF_MEMORY;
+            return cc2cu(result);
         }
       }
     }
@@ -1889,7 +1905,7 @@
       char *p;
       CURLcode result = Curl_dyn_add(&enc, part);
       if(result)
-        return CURLUE_OUT_OF_MEMORY;
+        return cc2cu(result);
       p = Curl_dyn_ptr(&enc);
       while(*p) {
         /* make sure percent encoded are lower case */
@@ -1905,7 +1921,7 @@
     }
     newp = Curl_dyn_ptr(&enc);
 
-    if(appendquery) {
+    if(appendquery && newp) {
       /* Append the 'newp' string onto the old query. Add a '&' separator if
          none is present at the end of the existing query already */
 
@@ -1934,8 +1950,8 @@
       }
     }
 
-    if(what == CURLUPART_HOST) {
-      size_t n = strlen(newp);
+    else if(what == CURLUPART_HOST) {
+      size_t n = Curl_dyn_len(&enc);
       if(!n && (flags & CURLU_NO_AUTHORITY)) {
         /* Skip hostname check, it's allowed to be empty. */
       }
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index dff26e6..9dcccc7 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -266,6 +266,13 @@
 /* SSL backend-specific data; declared differently by each SSL backend */
 struct ssl_backend_data;
 
+struct ssl_peer {
+  char *hostname;        /* hostname for verification */
+  char *dispname;        /* display version of hostname */
+  char *sni;             /* SNI version of hostname or NULL if not usable */
+  BIT(is_ip_address);    /* if hostname is an IPv4|6 address */
+};
+
 struct ssl_primary_config {
   char *CApath;          /* certificate dir (doesn't work on windows) */
   char *CAfile;          /* certificate to verify peer against */
@@ -571,6 +578,13 @@
 #define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE)
 #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
 
+/* transfer wants to send is not PAUSE or HOLD */
+#define CURL_WANT_SEND(data) \
+  (((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND)
+/* transfer receive is not on PAUSE or HOLD */
+#define CURL_WANT_RECV(data) \
+  (((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV)
+
 #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
 #define USE_CURL_ASYNC
 struct Curl_async {
@@ -589,6 +603,15 @@
 #define FIRSTSOCKET     0
 #define SECONDARYSOCKET 1
 
+/* Polling requested by an easy handle.
+ * `action` is CURL_POLL_IN, CURL_POLL_OUT or CURL_POLL_INOUT.
+ */
+struct easy_pollset {
+  curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
+  unsigned int num;
+  unsigned char actions[MAX_SOCKSPEREASYHANDLE];
+};
+
 enum expect100 {
   EXP100_SEND_DATA,           /* enough waiting, just send the body now */
   EXP100_AWAITING_CONTINUE,   /* waiting for the 100 Continue header */
@@ -649,16 +672,8 @@
                                      counter to make only a 100 reply (without
                                      a following second response code) result
                                      in a CURLE_GOT_NOTHING error code */
-  enum {
-    HEADER_NORMAL,              /* no bad header at all */
-    HEADER_PARTHEADER,          /* part of the chunk is a bad header, the rest
-                                   is normal data */
-    HEADER_ALLBAD               /* all was believed to be header */
-  } badheader;                  /* the header was deemed bad and will be
-                                   written as body */
   int headerline;               /* counts header lines to better track the
                                    first one */
-  char *str;                    /* within buf */
   curl_off_t offset;            /* possible resume offset read from the
                                    Content-Range: header */
   int httpcode;                 /* error code from the 'HTTP/1.? XXX' or
@@ -668,8 +683,9 @@
   enum expect100 exp100;        /* expect 100 continue state */
   enum upgrade101 upgr101;      /* 101 upgrade state */
 
-  /* Content unencoding stack. See sec 3.5, RFC2616. */
-  struct contenc_writer *writer_stack;
+  /* Client Writer stack, handles trasnfer- and content-encodings, protocol
+   * checks, pausing by client callbacks. */
+  struct Curl_cwriter *writer_stack;
   time_t timeofdoc;
   long bodywrites;
   char *location;   /* This points to an allocated version of the Location:
@@ -706,16 +722,19 @@
 #ifndef CURL_DISABLE_DOH
   struct dohdata *doh; /* DoH specific data for this request */
 #endif
-#if defined(WIN32) && defined(USE_WINSOCK)
+#if defined(_WIN32) && defined(USE_WINSOCK)
   struct curltime last_sndbuf_update;  /* last time readwrite_upload called
                                           win_update_buffer_size */
 #endif
+  char fread_eof[2]; /* the body read callback (index 0) returned EOF or
+                        the trailer read callback (index 1) returned EOF */
 #ifndef CURL_DISABLE_COOKIES
   unsigned char setcookies;
 #endif
-  unsigned char writer_stack_depth; /* Unencoding stack depth. */
   BIT(header);        /* incoming data has HTTP header */
   BIT(content_range); /* set TRUE if Content-Range: was found */
+  BIT(download_done); /* set to TRUE when download is complete */
+  BIT(eos_written);   /* iff EOS has been written to client */
   BIT(upload_done);   /* set to TRUE when doing chunked transfer-encoding
                          upload and we're uploading the last chunk */
   BIT(ignorebody);    /* we read a response-body but we ignore it! */
@@ -797,9 +816,10 @@
                          bool dead_connection);
 
   /* If used, this function gets called from transfer.c:readwrite_data() to
-     allow the protocol to do extra reads/writes */
-  CURLcode (*readwrite)(struct Curl_easy *data, struct connectdata *conn,
-                        ssize_t *nread, bool *readmore);
+     allow the protocol to do extra handling in writing response to
+     the client. */
+  CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen,
+                         bool is_eos, bool *done);
 
   /* This function can perform various checks on the connection. See
      CONNCHECK_* for more information about the checks that can be performed,
@@ -878,11 +898,6 @@
 struct connectdata {
   struct Curl_llist_element bundle_node; /* conncache */
 
-  /* chunk is for HTTP chunked encoding, but is in the general connectdata
-     struct only because we can do just about any protocol through an HTTP
-     proxy and an HTTP proxy may in fact respond using chunked encoding */
-  struct Curl_chunker chunk;
-
   curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
   void *closesocket_client;
 
@@ -1005,11 +1020,6 @@
   struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
 #endif
 
-#ifndef CURL_DISABLE_HTTP
-  /* for chunked-encoded trailer */
-  struct dynbuf trailer;
-#endif
-
   union {
 #ifndef CURL_DISABLE_FTP
     struct ftp_conn ftpc;
@@ -1080,7 +1090,6 @@
   unsigned short localport;
   unsigned short secondary_port; /* secondary socket remote port to connect to
                                     (ftp) */
-  unsigned char cselect_bits; /* bitmask of socket events */
   unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
                          value */
 #ifndef CURL_DISABLE_PROXY
@@ -1170,6 +1179,7 @@
   curl_off_t dlspeed;
   curl_off_t ulspeed;
 
+  timediff_t t_postqueue;
   timediff_t t_nslookup;
   timediff_t t_connect;
   timediff_t t_appconnect;
@@ -1325,7 +1335,8 @@
   curl_off_t recent_conn_id; /* The most recent connection used, might no
                               * longer exist */
   struct dynbuf headerb; /* buffer to store headers in */
-
+  struct curl_slist *hstslist; /* list of HSTS files set by
+                                  curl_easy_setopt(HSTS) calls */
   char *buffer; /* download buffer */
   char *ulbuf; /* allocated upload buffer or NULL */
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
@@ -1373,7 +1384,7 @@
 
   /* a place to store the most recently set (S)FTP entrypath */
   char *most_recent_ftp_entrypath;
-#if !defined(WIN32) && !defined(MSDOS) && !defined(__EMX__)
+#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
 /* do FTP line-end conversions on most platforms */
 #define CURL_DO_LINEEND_CONV
   /* for FTP downloads: track CRLF sequences that span blocks */
@@ -1411,7 +1422,7 @@
                                  this should be dealt with in pretransfer */
 #ifndef CURL_DISABLE_HTTP
   curl_mimepart *mimepost;
-  curl_mimepart *formp; /* storage for old API form-posting, alloced on
+  curl_mimepart *formp; /* storage for old API form-posting, allocated on
                            demand */
   size_t trailers_bytes_sent;
   struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
@@ -1422,6 +1433,10 @@
   trailers_state trailers_state; /* whether we are sending trailers
                                     and what stage are we at */
 #endif
+#ifndef CURL_DISABLE_COOKIES
+  struct curl_slist *cookielist; /* list of cookie files set by
+                                    curl_easy_setopt(COOKIEFILE) calls */
+#endif
 #ifdef USE_HYPER
   bool hconnect;  /* set if a CONNECT request */
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
@@ -1454,7 +1469,7 @@
                                 server involved in this request */
   unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
                             is this */
-  unsigned char dselect_bits; /* != 0 -> bitmask of socket events for this
+  unsigned char select_bits; /* != 0 -> bitmask of socket events for this
                                  transfer overriding anything the socket may
                                  report */
 #ifdef CURLDEBUG
@@ -1498,6 +1513,9 @@
                            though it will be discarded. We must call the data
                            rewind callback before trying to send again. */
   BIT(upload);         /* upload request */
+  BIT(internal); /* internal: true if this easy handle was created for
+                    internal use and the user does not have ownership of the
+                    handle. */
 };
 
 /*
@@ -1674,13 +1692,7 @@
   void *prereq_userp; /* pre-initial request user data */
 
   void *seek_client;    /* pointer to pass to the seek callback */
-#ifndef CURL_DISABLE_COOKIES
-  struct curl_slist *cookielist; /* list of cookie files set by
-                                    curl_easy_setopt(COOKIEFILE) calls */
-#endif
 #ifndef CURL_DISABLE_HSTS
-  struct curl_slist *hstslist; /* list of HSTS files set by
-                                  curl_easy_setopt(HSTS) calls */
   curl_hstsread_callback hsts_read;
   void *hsts_read_userp;
   curl_hstswrite_callback hsts_write;
@@ -1780,9 +1792,6 @@
 #endif
   curl_prot_t allowed_protocols;
   curl_prot_t redir_protocols;
-#ifndef CURL_DISABLE_MIME
-  unsigned int mime_options;      /* Mime option flags. */
-#endif
 #ifndef CURL_DISABLE_RTSP
   void *rtp_out;     /* write RTP to this if non-NULL */
   /* Common RTSP header options */
@@ -1805,8 +1814,6 @@
   int tcp_keepidle;     /* seconds in idle before sending keepalive probe */
   int tcp_keepintvl;    /* seconds between TCP keepalive probes */
 
-  size_t maxconnects;    /* Max idle connections in the connection cache */
-
   long expect_100_timeout; /* in milliseconds */
 #if defined(USE_HTTP2) || defined(USE_HTTP3)
   struct Curl_data_priority priority;
@@ -1831,10 +1838,14 @@
   BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
                                 recipients */
 #endif
+  unsigned int maxconnects; /* Max idle connections in the connection cache */
   unsigned char use_ssl;   /* if AUTH TLS is to be attempted etc, for FTP or
                               IMAP or POP3 or others! (type: curl_usessl)*/
   unsigned char connect_only; /* make connection/request, then let
                                  application use the socket */
+#ifndef CURL_DISABLE_MIME
+  BIT(mime_formescape);
+#endif
   BIT(is_fread_set); /* has read callback been set to non-NULL? */
 #ifndef CURL_DISABLE_TFTP
   BIT(tftp_no_options); /* do not send TFTP options requests */
@@ -1971,10 +1982,7 @@
      particular order. Note that all sockets are added to the sockhash, where
      the state etc are also kept. This array is mostly used to detect when a
      socket is to be removed from the hash. See singlesocket(). */
-  curl_socket_t sockets[MAX_SOCKSPEREASYHANDLE];
-  unsigned char actions[MAX_SOCKSPEREASYHANDLE]; /* action for each socket in
-                                                    sockets[] */
-  int numsocks;
+  struct easy_pollset last_poll;
 
   struct Names dns;
   struct Curl_multi *multi;    /* if non-NULL, points to the multi handle
@@ -2013,10 +2021,6 @@
 #ifdef USE_HYPER
   struct hyptransfer hyp;
 #endif
-
-  /* internal: true if this easy handle was created for internal use and the
-     user does not have ownership of the handle. */
-  bool internal;
 };
 
 #define LIBCURL_NAME "libcurl"
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index 12c6f7d..416da0f 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -125,7 +125,6 @@
         }
         else
           return FALSE;
-        break;
       }
     }
 
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 02e36ea..4696f29 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -211,8 +211,10 @@
     if(status == SEC_E_INSUFFICIENT_MEMORY)
       return CURLE_OUT_OF_MEMORY;
 
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     infof(data, "schannel: InitializeSecurityContext failed: %s",
           Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+#endif
 
     return CURLE_AUTH_ERROR;
   }
@@ -603,8 +605,10 @@
       if(status == SEC_E_INSUFFICIENT_MEMORY)
         return CURLE_OUT_OF_MEMORY;
 
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
       infof(data, "schannel: InitializeSecurityContext failed: %s",
             Curl_sspi_strerror(status, buffer, sizeof(buffer)));
+#endif
 
       return CURLE_AUTH_ERROR;
     }
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index 65eb3e1..16b6e40 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -226,7 +226,8 @@
   /* Extract the security layer and the maximum message size */
   indata = output_token.value;
   sec_layer = indata[0];
-  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+  max_size = ((unsigned int)indata[1] << 16) |
+             ((unsigned int)indata[2] << 8) | indata[3];
 
   /* Free the challenge as it is not required anymore */
   gss_release_buffer(&unused_status, &output_token);
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index c487149..17a517a 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -319,7 +319,8 @@
   /* Extract the security layer and the maximum message size */
   indata = input_buf[1].pvBuffer;
   sec_layer = indata[0];
-  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+  max_size = ((unsigned long)indata[1] << 16) |
+             ((unsigned long)indata[2] << 8) | indata[3];
 
   /* Free the challenge as it is not required anymore */
   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index ed7cee8..018e6a6 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -44,6 +44,7 @@
 #include "warnless.h"
 #include "rand.h"
 #include "vtls/vtls.h"
+#include "strdup.h"
 
 #define BUILDING_CURL_NTLM_MSGS_C
 #include "vauth/vauth.h"
@@ -184,11 +185,10 @@
       }
 
       free(ntlm->target_info); /* replace any previous data */
-      ntlm->target_info = malloc(target_info_len);
+      ntlm->target_info = Curl_memdup(&type2[target_info_offset],
+                                      target_info_len);
       if(!ntlm->target_info)
         return CURLE_OUT_OF_MEMORY;
-
-      memcpy(ntlm->target_info, &type2[target_info_offset], target_info_len);
     }
   }
 
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index 5118963..9205431 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -34,6 +34,7 @@
 #include "warnless.h"
 #include "curl_multibyte.h"
 #include "sendf.h"
+#include "strdup.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -213,11 +214,10 @@
   }
 
   /* Store the challenge for later use */
-  ntlm->input_token = malloc(Curl_bufref_len(type2) + 1);
+  ntlm->input_token = Curl_memdup0((const char *)Curl_bufref_ptr(type2),
+                                   Curl_bufref_len(type2));
   if(!ntlm->input_token)
     return CURLE_OUT_OF_MEMORY;
-  memcpy(ntlm->input_token, Curl_bufref_ptr(type2), Curl_bufref_len(type2));
-  ntlm->input_token[Curl_bufref_len(type2)] = '\0';
   ntlm->input_token_len = Curl_bufref_len(type2);
 
   return CURLE_OK;
@@ -314,7 +314,7 @@
                                                &type_3_desc,
                                                &attrs, &expiry);
   if(status != SEC_E_OK) {
-    infof(data, "NTLM handshake failure (type-3 message): Status=%x",
+    infof(data, "NTLM handshake failure (type-3 message): Status=%lx",
           status);
 
     if(status == SEC_E_INSUFFICIENT_MEMORY)
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index f99dd38..34d4d7c 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -39,7 +39,7 @@
 
 #ifdef USE_ARES
 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
-  defined(WIN32)
+  defined(_WIN32)
 #    define CARES_STATICLIB
 #  endif
 #  include <ares.h>
@@ -211,8 +211,12 @@
 #endif
 
 #ifdef USE_LIBPSL
-  msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", psl_get_version());
-  src[i++] = psl_version;
+  {
+    int num = psl_check_version_number(0);
+    msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d",
+              num >> 16, (num >> 8) & 0xff, num & 0xff);
+    src[i++] = psl_version;
+  }
 #endif
 
 #ifdef USE_SSH
@@ -409,7 +413,8 @@
 #define idn_present     NULL
 #endif
 
-#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY)
+#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
+  !defined(CURL_DISABLE_HTTP)
 static int https_proxy_present(curl_version_info_data *info)
 {
   (void) info;
@@ -454,13 +459,14 @@
 #ifndef CURL_DISABLE_HSTS
   FEATURE("HSTS",        NULL,                CURL_VERSION_HSTS),
 #endif
-#if defined(USE_NGHTTP2) || defined(USE_HYPER)
+#if defined(USE_NGHTTP2)
   FEATURE("HTTP2",       NULL,                CURL_VERSION_HTTP2),
 #endif
 #if defined(ENABLE_QUIC)
   FEATURE("HTTP3",       NULL,                CURL_VERSION_HTTP3),
 #endif
-#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY)
+#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
+  !defined(CURL_DISABLE_HTTP)
   FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY),
 #endif
 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
@@ -510,7 +516,7 @@
 #ifdef CURLDEBUG
   FEATURE("TrackMemory", NULL,                CURL_VERSION_CURLDEBUG),
 #endif
-#if defined(WIN32) && defined(UNICODE) && defined(_UNICODE)
+#if defined(_WIN32) && defined(UNICODE) && defined(_UNICODE)
   FEATURE("Unicode",     NULL,                CURL_VERSION_UNICODE),
 #endif
 #ifdef USE_UNIX_SOCKETS
diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c
index 872d5b4..e0f239e 100644
--- a/Utilities/cmcurl/lib/version_win32.c
+++ b/Utilities/cmcurl/lib/version_win32.c
@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 #include <curl/curl.h>
 #include "version_win32.h"
@@ -316,4 +316,4 @@
   return matched;
 }
 
-#endif /* WIN32 */
+#endif /* _WIN32 */
diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h
index 3899174..95c0661 100644
--- a/Utilities/cmcurl/lib/version_win32.h
+++ b/Utilities/cmcurl/lib/version_win32.h
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 /* Version condition */
 typedef enum {
@@ -51,6 +51,6 @@
                                   const PlatformIdentifier platform,
                                   const VersionCondition condition);
 
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 #endif /* HEADER_CURL_VERSION_WIN32_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c
index 6bd0d23..7674bc1 100644
--- a/Utilities/cmcurl/lib/vquic/curl_msh3.c
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -38,6 +38,7 @@
 #include "http1.h"
 #include "curl_msh3.h"
 #include "socketpair.h"
+#include "vtls/vtls.h"
 #include "vquic/vquic.h"
 
 /* The last 3 #include files should be in this order */
@@ -45,6 +46,10 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifdef CURL_DISABLE_SOCKETPAIR
+#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
+#endif
+
 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
 #define H3_STREAM_CHUNK_SIZE   (16 * 1024)
 #define H3_STREAM_RECV_CHUNKS \
@@ -199,8 +204,8 @@
   bits = CURL_CSELECT_IN;
   if(stream && !stream->upload_done)
     bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    data->state.dselect_bits = bits;
+  if(data->state.select_bits != bits) {
+    data->state.select_bits = bits;
     /* cannot expire from other thread */
   }
 }
@@ -215,8 +220,8 @@
   bits = CURL_CSELECT_IN;
   if(stream && !stream->upload_done)
     bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    data->state.dselect_bits = bits;
+  if(data->state.select_bits != bits) {
+    data->state.select_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
 }
@@ -672,31 +677,25 @@
   return nwritten;
 }
 
-static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    curl_socket_t *socks)
+static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct easy_pollset *ps)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
-  int bitmap = GETSOCK_BLANK;
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
   if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
-    socks[0] = ctx->sock[SP_LOCAL];
-
     if(stream->recv_error) {
-      bitmap |= GETSOCK_READSOCK(0);
+      Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
       drain_stream(cf, data);
     }
     else if(stream->req) {
-      bitmap |= GETSOCK_READSOCK(0);
+      Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
       drain_stream(cf, data);
     }
   }
-  CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap);
-  CF_DATA_RESTORE(cf, save);
-  return bitmap;
 }
 
 static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
@@ -802,14 +801,20 @@
                                  struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  bool verify = !!cf->conn->ssl_config.verifypeer;
+  struct ssl_primary_config *conn_config;
   MSH3_ADDR addr = {0};
   CURLcode result;
+  bool verify;
+
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config)
+    return CURLE_FAILED_INIT;
+  verify = !!conn_config->verifypeer;
 
   memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
   MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
 
-  if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) {
+  if(verify && (conn_config->CAfile || conn_config->CApath)) {
     /* TODO: need a way to provide trust anchors to MSH3 */
 #ifdef DEBUGBUILD
     /* we need this for our test cases to run */
@@ -1025,7 +1030,7 @@
   cf_msh3_connect,
   cf_msh3_close,
   Curl_cf_def_get_host,
-  cf_msh3_get_select_socks,
+  cf_msh3_adjust_pollset,
   cf_msh3_data_pending,
   cf_msh3_send,
   cf_msh3_recv,
@@ -1047,7 +1052,7 @@
   (void)data;
   (void)conn;
   (void)ai; /* TODO: msh3 resolves itself? */
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
index 7d681e5..a26b3e4 100644
--- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -41,7 +41,6 @@
 #include "vtls/gtls.h"
 #elif defined(USE_WOLFSSL)
 #include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
 #endif
 
 #include "urldata.h"
@@ -61,6 +60,7 @@
 #include "inet_pton.h"
 #include "vquic.h"
 #include "vquic_int.h"
+#include "vquic-tls.h"
 #include "vtls/keylog.h"
 #include "vtls/vtls.h"
 #include "curl_ngtcp2.h"
@@ -73,12 +73,8 @@
 #include "memdebug.h"
 
 
-#define H3_ALPN_H3_29 "\x5h3-29"
-#define H3_ALPN_H3 "\x2h3"
-
 #define QUIC_MAX_STREAMS (256*1024)
 #define QUIC_MAX_DATA (1*1024*1024)
-#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
 #define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
 
 /* A stream window is the maximum amount we need to buffer for
@@ -102,25 +98,6 @@
           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
 
 
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS                                                          \
-  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
-  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
-  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
-  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
-  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
-  "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
-#define QUIC_CIPHERS                                                          \
-  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
-  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-
 /*
  * Store ngtcp2 version info in this buffer.
  */
@@ -134,6 +111,8 @@
 
 struct cf_ngtcp2_ctx {
   struct cf_quic_ctx q;
+  struct ssl_peer peer;
+  struct quic_tls_ctx tls;
   ngtcp2_path connected_path;
   ngtcp2_conn *qconn;
   ngtcp2_cid dcid;
@@ -143,29 +122,16 @@
   ngtcp2_transport_params transport_params;
   ngtcp2_ccerr last_error;
   ngtcp2_crypto_conn_ref conn_ref;
-#ifdef USE_OPENSSL
-  SSL_CTX *sslctx;
-  SSL *ssl;
-#elif defined(USE_GNUTLS)
-  struct gtls_instance *gtls;
-#elif defined(USE_WOLFSSL)
-  WOLFSSL_CTX *sslctx;
-  WOLFSSL *ssl;
-#endif
   struct cf_call_data call_data;
   nghttp3_conn *h3conn;
   nghttp3_settings h3settings;
   struct curltime started_at;        /* time the current attempt started */
   struct curltime handshake_at;      /* time connect handshake finished */
-  struct curltime first_byte_at;     /* when first byte was recvd */
   struct curltime reconnect_at;      /* time the next attempt should start */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
   size_t max_stream_window;          /* max flow window for one stream */
+  uint64_t max_idle_ms;              /* max idle time for QUIC connection */
   int qlogfd;
-  BIT(got_first_byte);               /* if first byte was received */
-#ifdef USE_OPENSSL
-  BIT(x509_store_setup);             /* if x509 store has been set up */
-#endif
 };
 
 /* How to access `call_data` from a cf_ngtcp2 filter */
@@ -191,6 +157,7 @@
   bool closed; /* TRUE on stream close */
   bool reset;  /* TRUE on stream reset */
   bool send_closed; /* stream is local closed */
+  BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
 };
 
 #define H3_STREAM_CTX(d)  ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
@@ -236,11 +203,21 @@
 
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
+    if(ctx->h3conn && !stream->closed) {
+      nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream->id);
+      nghttp3_conn_close_stream(ctx->h3conn, stream->id,
+                                NGHTTP3_H3_REQUEST_CANCELLED);
+      nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL);
+      ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL);
+      stream->closed = TRUE;
+    }
+
     Curl_bufq_free(&stream->sendbuf);
     Curl_bufq_free(&stream->recvbuf);
     Curl_h1_req_parse_free(&stream->h1);
@@ -249,6 +226,43 @@
   }
 }
 
+static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
+                                         struct Curl_easy *data,
+                                         int64_t stream_id)
+{
+  struct Curl_easy *sdata;
+
+  (void)cf;
+  if(H3_STREAM_ID(data) == stream_id) {
+    return data;
+  }
+  else {
+    DEBUGASSERT(data->multi);
+    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+      if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) {
+        return sdata;
+      }
+    }
+  }
+  return NULL;
+}
+
+static void h3_drain_stream(struct Curl_cfilter *cf,
+                            struct Curl_easy *data)
+{
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  unsigned char bits;
+
+  (void)cf;
+  bits = CURL_CSELECT_IN;
+  if(stream && stream->upload_left && !stream->send_closed)
+    bits |= CURL_CSELECT_OUT;
+  if(data->state.select_bits != bits) {
+    data->state.select_bits = bits;
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+}
+
 /* ngtcp2 default congestion controller does not perform pacing. Limit
    the maximum packet burst to MAX_PKT_BURST packets. */
 #define MAX_PKT_BURST 10
@@ -261,10 +275,14 @@
   ngtcp2_path_storage ps;
 };
 
-static ngtcp2_tstamp timestamp(void)
+static void pktx_update_time(struct pkt_io_ctx *pktx,
+                             struct Curl_cfilter *cf)
 {
-  struct curltime ct = Curl_now();
-  return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
+  vquic_ctx_update_time(&ctx->q);
+  pktx->ts = ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
+             ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
 }
 
 static void pktx_init(struct pkt_io_ctx *pktx,
@@ -273,9 +291,9 @@
 {
   pktx->cf = cf;
   pktx->data = data;
-  pktx->ts = timestamp();
   pktx->pkt_count = 0;
   ngtcp2_path_storage_zero(&pktx->ps);
+  pktx_update_time(pktx, cf);
 }
 
 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
@@ -354,383 +372,14 @@
   t->initial_max_stream_data_uni = ctx->max_stream_window;
   t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
   t->initial_max_streams_uni = QUIC_MAX_STREAMS;
-  t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
+  t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS);
   if(ctx->qlogfd != -1) {
     s->qlog_write = qlog_callback;
   }
 }
 
-#ifdef USE_OPENSSL
-static void keylog_callback(const SSL *ssl, const char *line)
-{
-  (void)ssl;
-  Curl_tls_keylog_write_line(line);
-}
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
-                    const gnutls_datum_t *secret)
-{
-  gnutls_datum_t crandom;
-  gnutls_datum_t srandom;
-
-  gnutls_session_get_random(session, &crandom, &srandom);
-  if(crandom.size != 32) {
-    return -1;
-  }
-
-  Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
-  return 0;
-}
-#elif defined(USE_WOLFSSL)
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
-  (void)ssl;
-  Curl_tls_keylog_write_line(line);
-}
-#endif
-#endif
-
 static int init_ngh3_conn(struct Curl_cfilter *cf);
 
-#ifdef USE_OPENSSL
-static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
-                             struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct connectdata *conn = cf->conn;
-  CURLcode result = CURLE_FAILED_INIT;
-  SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
-  if(!ssl_ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-  if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
-    failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
-    goto out;
-  }
-#else
-  if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
-    failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
-    goto out;
-  }
-#endif
-
-  SSL_CTX_set_default_verify_paths(ssl_ctx);
-
-  {
-    const char *curves = conn->ssl_config.curves ?
-      conn->ssl_config.curves : QUIC_GROUPS;
-    if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) {
-      failf(data, "failed setting curves list for QUIC: '%s'", curves);
-      return CURLE_SSL_CIPHER;
-    }
-  }
-
-#ifndef OPENSSL_IS_BORINGSSL
-  {
-    const char *ciphers13 = conn->ssl_config.cipher_list13 ?
-      conn->ssl_config.cipher_list13 : QUIC_CIPHERS;
-    if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) {
-      failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
-      return CURLE_SSL_CIPHER;
-    }
-    infof(data, "QUIC cipher selection: %s", ciphers13);
-  }
-#endif
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-  }
-
-  /* OpenSSL always tries to verify the peer, this only says whether it should
-   * fail to connect if the verification fails, or if it should continue
-   * anyway. In the latter case the result of the verification is checked with
-   * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ?
-                     SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-
-  /* give application a chance to interfere with SSL set up. */
-  if(data->set.ssl.fsslctx) {
-    /* When a user callback is installed to modify the SSL_CTX,
-     * we need to do the full initialization before calling it.
-     * See: #11800 */
-    if(!ctx->x509_store_setup) {
-      result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
-      if(result)
-        goto out;
-      ctx->x509_store_setup = TRUE;
-    }
-    Curl_set_in_callback(data, true);
-    result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
-                                      data->set.ssl.fsslctxp);
-    Curl_set_in_callback(data, false);
-    if(result) {
-      failf(data, "error signaled by ssl ctx callback");
-      goto out;
-    }
-  }
-  result = CURLE_OK;
-
-out:
-  *pssl_ctx = result? NULL : ssl_ctx;
-  if(result && ssl_ctx)
-    SSL_CTX_free(ssl_ctx);
-  return result;
-}
-
-static CURLcode quic_set_client_cert(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  SSL_CTX *ssl_ctx = ctx->sslctx;
-  const struct ssl_config_data *ssl_config;
-
-  ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
-  DEBUGASSERT(ssl_config);
-
-  if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
-     || ssl_config->cert_type) {
-    return Curl_ossl_set_client_cert(
-        data, ssl_ctx, ssl_config->primary.clientcert,
-        ssl_config->primary.cert_blob, ssl_config->cert_type,
-        ssl_config->key, ssl_config->key_blob,
-        ssl_config->key_type, ssl_config->key_passwd);
-  }
-
-  return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  const uint8_t *alpn = NULL;
-  size_t alpnlen = 0;
-  unsigned char checkip[16];
-
-  DEBUGASSERT(!ctx->ssl);
-  ctx->ssl = SSL_new(ctx->sslctx);
-
-  SSL_set_app_data(ctx->ssl, &ctx->conn_ref);
-  SSL_set_connect_state(ctx->ssl);
-  SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-
-  alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
-  alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
-  if(alpn)
-    SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
-
-  /* set SNI */
-  if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
-#ifdef ENABLE_IPV6
-     && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
-#endif
-     ) {
-    char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
-    if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
-      failf(data, "Failed set SNI");
-      SSL_free(ctx->ssl);
-      ctx->ssl = NULL;
-      return CURLE_QUIC_CONNECT_ERROR;
-    }
-  }
-  return CURLE_OK;
-}
-#elif defined(USE_GNUTLS)
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  CURLcode result;
-  gnutls_datum_t alpn[2];
-  /* this will need some attention when HTTPS proxy over QUIC get fixed */
-  const char * const hostname = cf->conn->host.name;
-  long * const pverifyresult = &data->set.ssl.certverifyresult;
-  int rc;
-
-  DEBUGASSERT(ctx->gtls == NULL);
-  ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
-  if(!ctx->gtls)
-    return CURLE_OUT_OF_MEMORY;
-
-  result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl,
-                            hostname, ctx->gtls, pverifyresult);
-  if(result)
-    return result;
-
-  gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
-
-  if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
-    CURL_TRC_CF(data, cf,
-                "ngtcp2_crypto_gnutls_configure_client_session failed\n");
-    return CURLE_QUIC_CONNECT_ERROR;
-  }
-
-  rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
-  if(rc < 0) {
-    CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
-                gnutls_strerror(rc));
-    return CURLE_QUIC_CONNECT_ERROR;
-  }
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
-  }
-
-  /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
-  alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
-  alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
-  alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
-  alpn[1].size = sizeof(H3_ALPN_H3) - 2;
-
-  gnutls_alpn_set_protocols(ctx->gtls->session,
-                            alpn, 2, GNUTLS_ALPN_MANDATORY);
-  return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
-
-static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx,
-                             struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct connectdata *conn = cf->conn;
-  CURLcode result = CURLE_FAILED_INIT;
-  WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
-
-  if(!ssl_ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
-    failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
-    goto out;
-  }
-
-  wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
-
-  if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn->ssl_config.cipher_list13 ?
-                                 conn->ssl_config.cipher_list13 :
-                                 QUIC_CIPHERS) != 1) {
-    char error_buffer[256];
-    ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
-    failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
-    goto out;
-  }
-
-  if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn->ssl_config.curves ?
-                                  conn->ssl_config.curves :
-                                  (char *)QUIC_GROUPS) != 1) {
-    failf(data, "wolfSSL failed to set curves");
-    goto out;
-  }
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
-    wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-#else
-    failf(data, "wolfSSL was built without keylog callback");
-    goto out;
-#endif
-  }
-
-  if(conn->ssl_config.verifypeer) {
-    const char * const ssl_cafile = conn->ssl_config.CAfile;
-    const char * const ssl_capath = conn->ssl_config.CApath;
-
-    wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
-    if(ssl_cafile || ssl_capath) {
-      /* tell wolfSSL where to find CA certificates that are used to verify
-         the server's certificate. */
-      int rc =
-        wolfSSL_CTX_load_verify_locations_ex(ssl_ctx, ssl_cafile, ssl_capath,
-                                             WOLFSSL_LOAD_FLAG_IGNORE_ERR);
-      if(SSL_SUCCESS != rc) {
-        /* Fail if we insist on successfully verifying the server. */
-        failf(data, "error setting certificate verify locations:"
-              "  CAfile: %s CApath: %s",
-              ssl_cafile ? ssl_cafile : "none",
-              ssl_capath ? ssl_capath : "none");
-        goto out;
-      }
-      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
-      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
-    }
-#ifdef CURL_CA_FALLBACK
-    else {
-      /* verifying the peer without any CA certificates won't work so
-         use wolfssl's built-in default as fallback */
-      wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
-    }
-#endif
-  }
-  else {
-    wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
-  }
-
-  /* give application a chance to interfere with SSL set up. */
-  if(data->set.ssl.fsslctx) {
-    Curl_set_in_callback(data, true);
-    result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
-                                      data->set.ssl.fsslctxp);
-    Curl_set_in_callback(data, false);
-    if(result) {
-      failf(data, "error signaled by ssl ctx callback");
-      goto out;
-    }
-  }
-  result = CURLE_OK;
-
-out:
-  *pssl_ctx = result? NULL : ssl_ctx;
-  if(result && ssl_ctx)
-    SSL_CTX_free(ssl_ctx);
-  return result;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  const uint8_t *alpn = NULL;
-  size_t alpnlen = 0;
-  /* this will need some attention when HTTPS proxy over QUIC get fixed */
-  const char * const hostname = cf->conn->host.name;
-
-  (void)data;
-  DEBUGASSERT(!ctx->ssl);
-  ctx->ssl = wolfSSL_new(ctx->sslctx);
-
-  wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref);
-  wolfSSL_set_connect_state(ctx->ssl);
-  wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-
-  alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
-  alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
-  if(alpn)
-    wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
-
-  /* set SNI */
-  wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
-                 hostname, (unsigned short)strlen(hostname));
-
-  return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
 {
   (void)user_data;
@@ -786,6 +435,12 @@
   CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd",
               stream_id, buflen, nconsumed);
   if(nconsumed < 0) {
+    if(!data) {
+      struct Curl_easy *cdata = CF_DATA_CURRENT(cf);
+      CURL_TRC_CF(cdata, cf, "[%" PRId64 "] nghttp3 error on stream not "
+                  "used by us, ignored", stream_id);
+      return 0;
+    }
     ngtcp2_ccerr_set_application_error(
       &ctx->last_error,
       nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0);
@@ -816,7 +471,7 @@
   (void)stream_user_data;
 
   rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen);
-  if(rv) {
+  if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -844,7 +499,7 @@
                                  app_error_code);
   CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%"
               PRIu64 ") -> %d", stream3_id, app_error_code, rv);
-  if(rv) {
+  if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     ngtcp2_ccerr_set_application_error(
       &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
     return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -868,7 +523,7 @@
 
   rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
   CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
-  if(rv) {
+  if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -887,7 +542,7 @@
   (void)stream_user_data;
 
   rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
-  if(rv) {
+  if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -911,16 +566,25 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
+  struct Curl_easy *data = CF_DATA_CURRENT(cf);
+  struct Curl_easy *s_data;
+  struct h3_stream_ctx *stream;
   int rv;
   (void)tconn;
   (void)max_data;
   (void)stream_user_data;
 
   rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
-  if(rv) {
+  if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
-
+  s_data = get_stream_easy(cf, data, stream_id);
+  stream = H3_STREAM_CTX(s_data);
+  if(stream && stream->quic_flow_blocked) {
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] unblock quic flow", stream_id);
+    stream->quic_flow_blocked = FALSE;
+    h3_drain_stream(cf, data);
+  }
   return 0;
 }
 
@@ -1038,7 +702,7 @@
     pktx = &local_pktx;
   }
   else {
-    pktx->ts = timestamp();
+    pktx_update_time(pktx, cf);
   }
 
   expiry = ngtcp2_conn_get_expiry(ctx->qconn);
@@ -1073,46 +737,33 @@
   return CURLE_OK;
 }
 
-static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
+static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
-                                      curl_socket_t *socks)
+                                      struct easy_pollset *ps)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct SingleRequest *k = &data->req;
-  int rv = GETSOCK_BLANK;
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
-  struct cf_call_data save;
+  bool want_recv, want_send;
 
-  CF_DATA_SAVE(save, cf, data);
-  socks[0] = ctx->q.sockfd;
+  if(!ctx->qconn)
+    return;
 
-  /* in HTTP/3 we can always get a frame, so check read */
-  rv |= GETSOCK_READSOCK(0);
+  Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+  if(want_recv || want_send) {
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+    struct cf_call_data save;
+    bool c_exhaust, s_exhaust;
 
-  /* we're still uploading or the HTTP/2 layer wants to send data */
-  if((k->keepon & KEEP_SENDBITS) == KEEP_SEND &&
-     ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
-     ngtcp2_conn_get_max_data_left(ctx->qconn) &&
-     stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
-    rv |= GETSOCK_WRITESOCK(0);
+    CF_DATA_SAVE(save, cf, data);
+    c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) ||
+                !ngtcp2_conn_get_max_data_left(ctx->qconn));
+    s_exhaust = want_send && stream && stream->id >= 0 &&
+                stream->quic_flow_blocked;
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                 !Curl_bufq_is_empty(&ctx->q.sendbuf);
 
-  CF_DATA_RESTORE(cf, save);
-  return rv;
-}
-
-static void h3_drain_stream(struct Curl_cfilter *cf,
-                            struct Curl_easy *data)
-{
-  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
-  unsigned char bits;
-
-  (void)cf;
-  bits = CURL_CSELECT_IN;
-  if(stream && stream->upload_left && !stream->send_closed)
-    bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    data->state.dselect_bits = bits;
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+    Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send);
+    CF_DATA_RESTORE(cf, save);
   }
 }
 
@@ -1141,7 +792,6 @@
   else {
     CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id);
   }
-  data->req.keepon &= ~KEEP_SEND_HOLD;
   h3_drain_stream(cf, data);
   return 0;
 }
@@ -1570,15 +1220,9 @@
   /* Everything ACKed, we resume upload processing */
   if(!stream->sendbuf_len_in_flight) {
     int rv = nghttp3_conn_resume_stream(conn, stream_id);
-    if(rv) {
+    if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
       return NGTCP2_ERR_CALLBACK_FAILURE;
     }
-    if((data->req.keepon & KEEP_SEND_HOLD) &&
-       (data->req.keepon & KEEP_SEND)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
-      h3_drain_stream(cf, data);
-      CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id);
-    }
   }
   return 0;
 }
@@ -1676,6 +1320,10 @@
     goto out;
   stream = H3_STREAM_CTX(data);
   DEBUGASSERT(stream);
+  if(!stream) {
+    *err = CURLE_FAILED_INIT;
+    goto out;
+  }
 
   nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
   if(nwritten < 0)
@@ -1711,7 +1359,7 @@
     nva[i].flags = NGHTTP3_NV_FLAG_NONE;
   }
 
-  rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL);
+  rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, data);
   if(rc) {
     failf(data, "can get bidi streams");
     *err = CURLE_SEND_ERROR;
@@ -1835,6 +1483,8 @@
       sent = (ssize_t)len;
       goto out;
     }
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                "-> stream closed", stream->id, len);
     *err = CURLE_HTTP3;
     sent = -1;
     goto out;
@@ -1860,15 +1510,13 @@
   if(stream && sent > 0 && stream->sendbuf_len_in_flight) {
     /* We have unacknowledged DATA and cannot report success to our
      * caller. Instead we EAGAIN and remember how much we have already
-     * "written" into our various internal connection buffers.
-     * We put the stream upload on HOLD, until this gets ACKed. */
+     * "written" into our various internal connection buffers. */
     stream->upload_blocked_len = sent;
     CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
                 "%zu bytes in flight -> EGAIN", stream->id, len,
                 stream->sendbuf_len_in_flight);
     *err = CURLE_AGAIN;
     sent = -1;
-    data->req.keepon |= KEEP_SEND_HOLD;
   }
 
 out:
@@ -1887,52 +1535,12 @@
                                 struct Curl_easy *data)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  const char *hostname, *disp_hostname;
-  int port;
-  char *snihost;
-
-  Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port);
-  snihost = Curl_ssl_snihost(data, hostname, NULL);
-  if(!snihost)
-    return CURLE_PEER_FAILED_VERIFICATION;
 
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   cf->conn->httpversion = 30;
   cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
 
-  if(cf->conn->ssl_config.verifyhost) {
-#ifdef USE_OPENSSL
-    X509 *server_cert;
-    server_cert = SSL_get_peer_certificate(ctx->ssl);
-    if(!server_cert) {
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
-    X509_free(server_cert);
-    if(result)
-      return result;
-#elif defined(USE_GNUTLS)
-    result = Curl_gtls_verifyserver(data, ctx->gtls->session,
-                                    &cf->conn->ssl_config, &data->set.ssl,
-                                    hostname, disp_hostname,
-                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
-    if(result)
-      return result;
-#elif defined(USE_WOLFSSL)
-    if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE)
-      return CURLE_PEER_FAILED_VERIFICATION;
-#endif
-    infof(data, "Verified certificate just fine");
-  }
-  else
-    infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
-  if(data->set.ssl.certinfo)
-    /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, ctx->ssl);
-#endif
-  return result;
+  return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
 }
 
 static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
@@ -1955,8 +1563,8 @@
 
   rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
   if(rv) {
-    CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s",
-                ngtcp2_strerror(rv));
+    CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)",
+                ngtcp2_strerror(rv), rv);
     if(!ctx->last_error.error_code) {
       if(rv == NGTCP2_ERR_CRYPTO) {
         ngtcp2_ccerr_set_tls_alert(&ctx->last_error,
@@ -1993,17 +1601,12 @@
     pktx = &local_pktx;
   }
   else {
-    pktx->ts = timestamp();
+    pktx_update_time(pktx, cf);
   }
 
-#ifdef USE_OPENSSL
-  if(!ctx->x509_store_setup) {
-    result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx);
-    if(result)
-      return result;
-    ctx->x509_store_setup = TRUE;
-  }
-#endif
+  result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
+  if(result)
+    return result;
 
   for(i = 0; i < pkts_max; i += pkts_chunk) {
     pktx->pkt_count = 0;
@@ -2081,11 +1684,18 @@
     }
     else if(n < 0) {
       switch(n) {
-      case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+      case NGTCP2_ERR_STREAM_DATA_BLOCKED: {
+        struct h3_stream_ctx *stream = H3_STREAM_CTX(x->data);
         DEBUGASSERT(ndatalen == -1);
         nghttp3_conn_block_stream(ctx->h3conn, stream_id);
+        CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow",
+                    stream_id);
+        DEBUGASSERT(stream);
+        if(stream)
+          stream->quic_flow_blocked = TRUE;
         n = 0;
         break;
+      }
       case NGTCP2_ERR_STREAM_SHUT_WR:
         DEBUGASSERT(ndatalen == -1);
         nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id);
@@ -2145,7 +1755,7 @@
     pktx = &local_pktx;
   }
   else {
-    pktx->ts = timestamp();
+    pktx_update_time(pktx, cf);
     ngtcp2_path_storage_zero(&pktx->ps);
   }
 
@@ -2282,10 +1892,12 @@
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
     h3_data_done(cf, data);
     break;
-  }
+  case CF_CTRL_DATA_DONE:
+    h3_data_done(cf, data);
+    break;
   case CF_CTRL_DATA_DONE_SEND: {
     struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {
@@ -2319,31 +1931,14 @@
   if(ctx->qlogfd != -1) {
     close(ctx->qlogfd);
   }
-#ifdef USE_OPENSSL
-  if(ctx->ssl)
-    SSL_free(ctx->ssl);
-  if(ctx->sslctx)
-    SSL_CTX_free(ctx->sslctx);
-#elif defined(USE_GNUTLS)
-  if(ctx->gtls) {
-    if(ctx->gtls->cred)
-      gnutls_certificate_free_credentials(ctx->gtls->cred);
-    if(ctx->gtls->session)
-      gnutls_deinit(ctx->gtls->session);
-    free(ctx->gtls);
-  }
-#elif defined(USE_WOLFSSL)
-  if(ctx->ssl)
-    wolfSSL_free(ctx->ssl);
-  if(ctx->sslctx)
-    wolfSSL_CTX_free(ctx->sslctx);
-#endif
+  Curl_vquic_tls_cleanup(&ctx->tls);
   vquic_ctx_free(&ctx->q);
   if(ctx->h3conn)
     nghttp3_conn_del(ctx->h3conn);
   if(ctx->qconn)
     ngtcp2_conn_del(ctx->qconn);
   Curl_bufcp_free(&ctx->stream_bufcp);
+  Curl_ssl_peer_cleanup(&ctx->peer);
 
   memset(ctx, 0, sizeof(*ctx));
   ctx->qlogfd = -1;
@@ -2358,15 +1953,15 @@
   CF_DATA_SAVE(save, cf, data);
   if(ctx && ctx->qconn) {
     char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
-    ngtcp2_tstamp ts;
+    struct pkt_io_ctx pktx;
     ngtcp2_ssize rc;
 
     CURL_TRC_CF(data, cf, "close");
-    ts = timestamp();
+    pktx_init(&pktx, cf, data);
     rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
                                             NULL, /* pkt_info */
                                             (uint8_t *)buffer, sizeof(buffer),
-                                            &ctx->last_error, ts);
+                                            &ctx->last_error, pktx.ts);
     if(rc > 0) {
       while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) &&
             SOCKERRNO == EINTR);
@@ -2395,6 +1990,37 @@
   (void)save;
 }
 
+static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx,
+                              struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  (void)cf;
+#ifdef USE_OPENSSL
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+  if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) {
+    failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
+    return CURLE_FAILED_INIT;
+  }
+#else
+  if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) {
+    failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
+    return CURLE_FAILED_INIT;
+  }
+#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+#elif defined(USE_GNUTLS)
+  if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+    failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed");
+    return CURLE_FAILED_INIT;
+  }
+#elif defined(USE_WOLFSSL)
+  if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->ssl_ctx) != 0) {
+    failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
+    return CURLE_FAILED_INIT;
+  }
+#endif
+  return CURLE_OK;
+}
+
 /*
  * Might be called twice for happy eyeballs.
  */
@@ -2411,24 +2037,18 @@
 
   ctx->version = NGTCP2_PROTO_VER_MAX;
   ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
+  ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
 
-#ifdef USE_OPENSSL
-  result = quic_ssl_ctx(&ctx->sslctx, cf, data);
+  result = Curl_ssl_peer_init(&ctx->peer, cf);
   if(result)
     return result;
 
-  result = quic_set_client_cert(cf, data);
-  if(result)
-    return result;
-#elif defined(USE_WOLFSSL)
-  result = quic_ssl_ctx(&ctx->sslctx, cf, data);
-  if(result)
-    return result;
-#endif
-
-  result = quic_init_ssl(cf, data);
+#define H3_ALPN "\x2h3\x5h3-29"
+  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+                               H3_ALPN, sizeof(H3_ALPN) - 1,
+                               tls_ctx_setup, &ctx->conn_ref);
   if(result)
     return result;
 
@@ -2475,9 +2095,9 @@
     return CURLE_QUIC_CONNECT_ERROR;
 
 #ifdef USE_GNUTLS
-  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session);
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session);
 #else
-  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl);
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl);
 #endif
 
   ngtcp2_ccerr_default(&ctx->last_error);
@@ -2559,27 +2179,9 @@
      ngtcp2_conn_in_draining_period(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
-     * state.
-     * This may be a stopping of the service or it may be that the server
-     * is reloading and a new instance will start serving soon.
-     * In any case, we tear down our socket and start over with a new one.
-     * We re-open the underlying UDP cf right now, but do not start
-     * connecting until called again.
-     */
-    int reconn_delay_ms = 200;
-
-    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                reconn_delay_ms);
-    Curl_conn_cf_close(cf->next, data);
-    cf_ngtcp2_ctx_clear(ctx);
-    result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
-    if(!result && *done) {
-      *done = FALSE;
-      ctx->reconnect_at = now;
-      ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
-      Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
-      result = CURLE_OK;
-    }
+     * state. The CONNECT may work in the near future again. Indicate
+     * that as a "weird" reply. */
+    result = CURLE_WEIRD_SERVER_REPLY;
   }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -2626,8 +2228,8 @@
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
-    if(ctx->got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+    if(ctx->q.got_first_byte) {
+      timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
     }
     else
@@ -2635,8 +2237,8 @@
     return CURLE_OK;
   case CF_QUERY_TIMER_CONNECT: {
     struct curltime *when = pres2;
-    if(ctx->got_first_byte)
-      *when = ctx->first_byte_at;
+    if(ctx->q.got_first_byte)
+      *when = ctx->q.first_byte_at;
     return CURLE_OK;
   }
   case CF_QUERY_TIMER_APPCONNECT: {
@@ -2657,24 +2259,51 @@
                                     struct Curl_easy *data,
                                     bool *input_pending)
 {
-  bool alive = TRUE;
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+  bool alive = FALSE;
+  const ngtcp2_transport_params *rp;
+  struct cf_call_data save;
 
+    CF_DATA_SAVE(save, cf, data);
   *input_pending = FALSE;
-  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
-    return FALSE;
+  if(!ctx->qconn)
+    goto out;
 
+  /* Both sides of the QUIC connection announce they max idle times in
+   * the transport parameters. Look at the minimum of both and if
+   * we exceed this, regard the connection as dead. The other side
+   * may have completely purged it and will no longer respond
+   * to any packets from us. */
+  rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
+  if(rp) {
+    timediff_t idletime;
+    uint64_t idle_ms = ctx->max_idle_ms;
+
+    if(rp->max_idle_timeout &&
+      (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms)
+      idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS);
+    idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
+    if(idletime > 0 && (uint64_t)idletime > idle_ms)
+      goto out;
+  }
+
+  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+    goto out;
+
+  alive = TRUE;
   if(*input_pending) {
+    CURLcode result;
     /* This happens before we've sent off a request and the connection is
        not in use by any other transfer, there shouldn't be any data here,
        only "protocol frames" */
     *input_pending = FALSE;
-    if(cf_progress_ingress(cf, data, NULL))
-      alive = FALSE;
-    else {
-      alive = TRUE;
-    }
+    result = cf_progress_ingress(cf, data, NULL);
+    CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
+    alive = result? FALSE : TRUE;
   }
 
+out:
+  CF_DATA_RESTORE(cf, save);
   return alive;
 }
 
@@ -2686,7 +2315,7 @@
   cf_ngtcp2_connect,
   cf_ngtcp2_close,
   Curl_cf_def_get_host,
-  cf_ngtcp2_get_select_socks,
+  cf_ngtcp2_adjust_pollset,
   cf_ngtcp2_data_pending,
   cf_ngtcp2_send,
   cf_ngtcp2_recv,
@@ -2706,7 +2335,7 @@
   CURLcode result;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c
new file mode 100644
index 0000000..c499a00
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_osslq.c
@@ -0,0 +1,2237 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <nghttp3/nghttp3.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "dynbuf.h"
+#include "http1.h"
+#include "select.h"
+#include "inet_pton.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "vquic-tls.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "vtls/openssl.h"
+#include "curl_osslq.h"
+
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* A stream window is the maximum amount we need to buffer for
+ * each active transfer. We use HTTP/3 flow control and only ACK
+ * when we take things out of the buffer.
+ * Chunk size is large enough to take a full DATA frame */
+#define H3_STREAM_WINDOW_SIZE (128 * 1024)
+#define H3_STREAM_CHUNK_SIZE   (16 * 1024)
+/* The pool keeps spares around and half of a full stream windows
+ * seems good. More does not seem to improve performance.
+ * The benefit of the pool is that stream buffer to not keep
+ * spares. So memory consumption goes down when streams run empty,
+ * have a large upload done, etc. */
+#define H3_STREAM_POOL_SPARES \
+          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
+/* Receive and Send max number of chunks just follows from the
+ * chunk size and window size */
+#define H3_STREAM_RECV_CHUNKS \
+          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
+#define H3_STREAM_SEND_CHUNKS \
+          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+typedef uint32_t sslerr_t;
+#else
+typedef unsigned long sslerr_t;
+#endif
+
+
+/* How to access `call_data` from a cf_osslq filter */
+#undef CF_CTX_CALL_DATA
+#define CF_CTX_CALL_DATA(cf)  \
+  ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data);
+
+static const char *SSL_ERROR_to_str(int err)
+{
+  switch(err) {
+  case SSL_ERROR_NONE:
+    return "SSL_ERROR_NONE";
+  case SSL_ERROR_SSL:
+    return "SSL_ERROR_SSL";
+  case SSL_ERROR_WANT_READ:
+    return "SSL_ERROR_WANT_READ";
+  case SSL_ERROR_WANT_WRITE:
+    return "SSL_ERROR_WANT_WRITE";
+  case SSL_ERROR_WANT_X509_LOOKUP:
+    return "SSL_ERROR_WANT_X509_LOOKUP";
+  case SSL_ERROR_SYSCALL:
+    return "SSL_ERROR_SYSCALL";
+  case SSL_ERROR_ZERO_RETURN:
+    return "SSL_ERROR_ZERO_RETURN";
+  case SSL_ERROR_WANT_CONNECT:
+    return "SSL_ERROR_WANT_CONNECT";
+  case SSL_ERROR_WANT_ACCEPT:
+    return "SSL_ERROR_WANT_ACCEPT";
+#if defined(SSL_ERROR_WANT_ASYNC)
+  case SSL_ERROR_WANT_ASYNC:
+    return "SSL_ERROR_WANT_ASYNC";
+#endif
+#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+  case SSL_ERROR_WANT_ASYNC_JOB:
+    return "SSL_ERROR_WANT_ASYNC_JOB";
+#endif
+#if defined(SSL_ERROR_WANT_EARLY)
+  case SSL_ERROR_WANT_EARLY:
+    return "SSL_ERROR_WANT_EARLY";
+#endif
+  default:
+    return "SSL_ERROR unknown";
+  }
+}
+
+/* Return error string for last OpenSSL error */
+static char *ossl_strerror(unsigned long error, char *buf, size_t size)
+{
+  DEBUGASSERT(size);
+  *buf = '\0';
+
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+  ERR_error_string_n((uint32_t)error, buf, size);
+#else
+  ERR_error_string_n(error, buf, size);
+#endif
+
+  if(!*buf) {
+    const char *msg = error ? "Unknown error" : "No error";
+    if(strlen(msg) < size)
+      strcpy(buf, msg);
+  }
+
+  return buf;
+}
+
+static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
+                              const struct Curl_sockaddr_ex *addr)
+{
+  BIO_ADDR *ba;
+  CURLcode result = CURLE_FAILED_INIT;
+
+  ba = BIO_ADDR_new();
+  if(!ba) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  switch(addr->family) {
+  case AF_INET: {
+    struct sockaddr_in * const sin =
+      (struct sockaddr_in * const)(void *)&addr->sa_addr;
+    if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
+                         sizeof(sin->sin_addr), sin->sin_port)) {
+      goto out;
+    }
+    result = CURLE_OK;
+    break;
+  }
+#ifdef ENABLE_IPV6
+  case AF_INET6: {
+    struct sockaddr_in6 * const sin =
+      (struct sockaddr_in6 * const)(void *)&addr->sa_addr;
+    if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
+                         sizeof(sin->sin6_addr), sin->sin6_port)) {
+    }
+    result = CURLE_OK;
+    break;
+  }
+#endif /* ENABLE_IPV6 */
+  default:
+    /* sunsupported */
+    DEBUGASSERT(0);
+    break;
+  }
+
+out:
+  if(result && ba) {
+    BIO_ADDR_free(ba);
+    ba = NULL;
+  }
+  *pbio_addr = ba;
+  return result;
+}
+
+/* QUIC stream (not necessarily H3) */
+struct cf_osslq_stream {
+  int64_t id;
+  SSL *ssl;
+  struct bufq recvbuf; /* QUIC war data recv buffer */
+  BIT(recvd_eos);
+  BIT(closed);
+  BIT(reset);
+  BIT(send_blocked);
+};
+
+static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
+                                     SSL *conn,
+                                     uint64_t flags,
+                                     struct bufc_pool *bufcp,
+                                     void *user_data)
+{
+  DEBUGASSERT(!s->ssl);
+  Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
+  s->ssl = SSL_new_stream(conn, flags);
+  if(!s->ssl) {
+    return CURLE_FAILED_INIT;
+  }
+  s->id = SSL_get_stream_id(s->ssl);
+  SSL_set_app_data(s->ssl, user_data);
+  return CURLE_OK;
+}
+
+static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
+{
+  if(s->ssl) {
+    SSL_set_app_data(s->ssl, NULL);
+    SSL_free(s->ssl);
+  }
+  Curl_bufq_free(&s->recvbuf);
+  memset(s, 0, sizeof(*s));
+}
+
+static void cf_osslq_stream_close(struct cf_osslq_stream *s)
+{
+  if(s->ssl) {
+    SSL_free(s->ssl);
+    s->ssl = NULL;
+  }
+}
+
+struct cf_osslq_h3conn {
+  nghttp3_conn *conn;
+  nghttp3_settings settings;
+  struct cf_osslq_stream s_ctrl;
+  struct cf_osslq_stream s_qpack_enc;
+  struct cf_osslq_stream s_qpack_dec;
+  struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
+  size_t remote_ctrl_n; /* number of peer streams opened */
+};
+
+static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
+{
+  size_t i;
+
+  if(h3->conn)
+    nghttp3_conn_del(h3->conn);
+  cf_osslq_stream_cleanup(&h3->s_ctrl);
+  cf_osslq_stream_cleanup(&h3->s_qpack_enc);
+  cf_osslq_stream_cleanup(&h3->s_qpack_dec);
+  for(i = 0; i < h3->remote_ctrl_n; ++i) {
+    cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
+  }
+}
+
+struct cf_osslq_ctx {
+  struct cf_quic_ctx q;
+  struct ssl_peer peer;
+  struct quic_tls_ctx tls;
+  struct cf_call_data call_data;
+  struct cf_osslq_h3conn h3;
+  struct curltime started_at;        /* time the current attempt started */
+  struct curltime handshake_at;      /* time connect handshake finished */
+  struct curltime first_byte_at;     /* when first byte was recvd */
+  struct curltime reconnect_at;      /* time the next attempt should start */
+  struct bufc_pool stream_bufcp;     /* chunk pool for streams */
+  size_t max_stream_window;          /* max flow window for one stream */
+  uint64_t max_idle_ms;              /* max idle time for QUIC connection */
+  BIT(got_first_byte);               /* if first byte was received */
+#ifdef USE_OPENSSL
+  BIT(x509_store_setup);             /* if x509 store has been set up */
+  BIT(protocol_shutdown);            /* QUIC connection is shut down */
+#endif
+};
+
+static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
+{
+  struct cf_call_data save = ctx->call_data;
+
+  cf_osslq_h3conn_cleanup(&ctx->h3);
+  Curl_vquic_tls_cleanup(&ctx->tls);
+  vquic_ctx_free(&ctx->q);
+  Curl_bufcp_free(&ctx->stream_bufcp);
+  Curl_ssl_peer_cleanup(&ctx->peer);
+
+  memset(ctx, 0, sizeof(*ctx));
+  ctx->call_data = save;
+}
+
+static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  if(ctx && ctx->tls.ssl) {
+    /* TODO: send connection close */
+    CURL_TRC_CF(data, cf, "cf_osslq_close()");
+    cf_osslq_ctx_clear(ctx);
+  }
+
+  cf->connected = FALSE;
+  CF_DATA_RESTORE(cf, save);
+}
+
+static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  CURL_TRC_CF(data, cf, "destroy");
+  if(ctx) {
+    CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
+    cf_osslq_ctx_clear(ctx);
+    free(ctx);
+  }
+  cf->ctx = NULL;
+  /* No CF_DATA_RESTORE(cf, save) possible */
+  (void)save;
+}
+
+static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
+                                           SSL *stream_ssl,
+                                           struct Curl_cfilter *cf,
+                                           struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  int64_t stream_id = SSL_get_stream_id(stream_ssl);
+
+  if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
+    /* rejected, we are full */
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream",
+                stream_id);
+    SSL_free(stream_ssl);
+    return CURLE_FAILED_INIT;
+  }
+  switch(SSL_get_stream_type(stream_ssl)) {
+    case SSL_STREAM_TYPE_READ: {
+      struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
+      nstream->id = stream_id;
+      nstream->ssl = stream_ssl;
+      Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted new remote uni stream",
+                  stream_id);
+      break;
+    }
+    default:
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read"
+                  " stream", stream_id);
+      SSL_free(stream_ssl);
+      return CURLE_FAILED_INIT;
+  }
+  return CURLE_OK;
+
+}
+
+static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              int detail, CURLcode def_result)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = def_result;
+  sslerr_t errdetail;
+  char ebuf[256] = "unknown";
+  const char *err_descr = ebuf;
+  long lerr;
+  int lib;
+  int reason;
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+
+  errdetail = ERR_get_error();
+  lib = ERR_GET_LIB(errdetail);
+  reason = ERR_GET_REASON(errdetail);
+
+  if((lib == ERR_LIB_SSL) &&
+     ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
+      (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+    result = CURLE_PEER_FAILED_VERIFICATION;
+
+    lerr = SSL_get_verify_result(ctx->tls.ssl);
+    if(lerr != X509_V_OK) {
+      ssl_config->certverifyresult = lerr;
+      msnprintf(ebuf, sizeof(ebuf),
+                "SSL certificate problem: %s",
+                X509_verify_cert_error_string(lerr));
+    }
+    else
+      err_descr = "SSL certificate verification failed";
+  }
+#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
+  /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
+     OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
+  else if((lib == ERR_LIB_SSL) &&
+          (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
+    /* If client certificate is required, communicate the
+       error to client */
+    result = CURLE_SSL_CLIENTCERT;
+    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+  }
+#endif
+  else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
+    ctx->protocol_shutdown = TRUE;
+    err_descr = "QUIC connectin has been shut down";
+    result = def_result;
+  }
+  else {
+    result = def_result;
+    ossl_strerror(errdetail, ebuf, sizeof(ebuf));
+  }
+
+  /* detail is already set to the SSL error above */
+
+  /* If we e.g. use SSLv2 request-method and the server doesn't like us
+   * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
+   * the SO_ERROR is also lost.
+   */
+  if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
+    char extramsg[80]="";
+    int sockerr = SOCKERRNO;
+    const char *r_ip = NULL;
+    int r_port = 0;
+
+    Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+                        &r_ip, &r_port, NULL, NULL);
+    if(sockerr && detail == SSL_ERROR_SYSCALL)
+      Curl_strerror(sockerr, extramsg, sizeof(extramsg));
+    failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
+          extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
+          ctx->peer.dispname, r_port, r_ip);
+  }
+  else {
+    /* Could be a CERT problem */
+    failf(data, "%s", err_descr);
+  }
+  return result;
+}
+
+static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+
+  cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+  cf->conn->httpversion = 30;
+  cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+  return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
+}
+
+/**
+ * All about the H3 internals of a stream
+ */
+struct h3_stream_ctx {
+  struct cf_osslq_stream s;
+  struct bufq sendbuf;   /* h3 request body */
+  struct bufq recvbuf;   /* h3 response body */
+  struct h1_req_parser h1; /* h1 request parsing */
+  size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
+  size_t upload_blocked_len; /* the amount written last and EGAINed */
+  size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
+  uint64_t error3; /* HTTP/3 stream error code */
+  curl_off_t upload_left; /* number of request bytes left to upload */
+  curl_off_t download_recvd; /* number of response DATA bytes received */
+  int status_code; /* HTTP status code */
+  bool resp_hds_complete; /* we have a complete, final response */
+  bool closed; /* TRUE on stream close */
+  bool reset;  /* TRUE on stream reset */
+  bool send_closed; /* stream is local closed */
+  BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
+};
+
+#define H3_STREAM_CTX(d)  ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
+                           ((struct HTTP *)(d)->req.p.http)->h3_ctx \
+                             : NULL))
+#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
+#define H3_STREAM_ID(d)   (H3_STREAM_CTX(d)? \
+                           H3_STREAM_CTX(d)->s.id : -2)
+
+static CURLcode h3_data_setup(struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+  if(!data || !data->req.p.http) {
+    failf(data, "initialization failure, transfer not http initialized");
+    return CURLE_FAILED_INIT;
+  }
+
+  if(stream)
+    return CURLE_OK;
+
+  stream = calloc(1, sizeof(*stream));
+  if(!stream)
+    return CURLE_OUT_OF_MEMORY;
+
+  stream->s.id = -1;
+  /* on send, we control how much we put into the buffer */
+  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
+                  H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
+  stream->sendbuf_len_in_flight = 0;
+  /* on recv, we need a flexible buffer limit since we also write
+   * headers to it that are not counted against the nghttp3 flow limits. */
+  Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
+                  H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
+  stream->recv_buf_nonflow = 0;
+  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
+
+  H3_STREAM_LCTX(data) = stream;
+  return CURLE_OK;
+}
+
+static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+  (void)cf;
+  if(stream) {
+    CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id);
+    if(ctx->h3.conn && !stream->closed) {
+      nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
+      nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
+                                NGHTTP3_H3_REQUEST_CANCELLED);
+      nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
+      stream->closed = TRUE;
+    }
+
+    cf_osslq_stream_cleanup(&stream->s);
+    Curl_bufq_free(&stream->sendbuf);
+    Curl_bufq_free(&stream->recvbuf);
+    Curl_h1_req_parse_free(&stream->h1);
+    free(stream);
+    H3_STREAM_LCTX(data) = NULL;
+  }
+}
+
+static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
+                                                    struct Curl_easy *data,
+                                                    int64_t stream_id)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  struct Curl_easy *sdata;
+
+  if(stream && stream->s.id == stream_id) {
+    return &stream->s;
+  }
+  else if(ctx->h3.s_ctrl.id == stream_id) {
+    return &ctx->h3.s_ctrl;
+  }
+  else if(ctx->h3.s_qpack_enc.id == stream_id) {
+    return &ctx->h3.s_qpack_enc;
+  }
+  else if(ctx->h3.s_qpack_dec.id == stream_id) {
+    return &ctx->h3.s_qpack_dec;
+  }
+  else {
+    DEBUGASSERT(data->multi);
+    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+      if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) {
+        stream = H3_STREAM_CTX(sdata);
+        return stream? &stream->s : NULL;
+      }
+    }
+  }
+  return NULL;
+}
+
+static void h3_drain_stream(struct Curl_cfilter *cf,
+                            struct Curl_easy *data)
+{
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  unsigned char bits;
+
+  (void)cf;
+  bits = CURL_CSELECT_IN;
+  if(stream && stream->upload_left && !stream->send_closed)
+    bits |= CURL_CSELECT_OUT;
+  if(data->state.select_bits != bits) {
+    data->state.select_bits = bits;
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+}
+
+static CURLcode h3_data_pause(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              bool pause)
+{
+  if(!pause) {
+    /* unpaused. make it run again right away */
+    h3_drain_stream(cf, data);
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+  return CURLE_OK;
+}
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+                              uint64_t app_error_code, void *user_data,
+                              void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  (void)conn;
+  (void)stream_id;
+
+  /* we might be called by nghttp3 after we already cleaned up */
+  if(!stream)
+    return 0;
+
+  stream->closed = TRUE;
+  stream->error3 = app_error_code;
+  if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
+    stream->reset = TRUE;
+    stream->send_closed = TRUE;
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
+                stream->s.id, stream->error3);
+  }
+  else {
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id);
+  }
+  h3_drain_stream(cf, data);
+  return 0;
+}
+
+/*
+ * write_resp_raw() copies response data in raw format to the `data`'s
+  * receive buffer. If not enough space is available, it appends to the
+ * `data`'s overflow buffer.
+ */
+static CURLcode write_resp_raw(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               const void *mem, size_t memlen,
+                               bool flow)
+{
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  CURLcode result = CURLE_OK;
+  ssize_t nwritten;
+
+  (void)cf;
+  if(!stream) {
+    return CURLE_RECV_ERROR;
+  }
+  nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
+  if(nwritten < 0) {
+    return result;
+  }
+
+  if(!flow)
+    stream->recv_buf_nonflow += (size_t)nwritten;
+
+  if((size_t)nwritten < memlen) {
+    /* This MUST not happen. Our recbuf is dimensioned to hold the
+     * full max_stream_window and then some for this very reason. */
+    DEBUGASSERT(0);
+    return CURLE_RECV_ERROR;
+  }
+  return result;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
+                           const uint8_t *buf, size_t buflen,
+                           void *user_data, void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  CURLcode result;
+
+  (void)conn;
+  (void)stream3_id;
+
+  if(!stream)
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
+
+  result = write_resp_raw(cf, data, buf, buflen, TRUE);
+  if(result) {
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
+                stream->s.id, buflen, result);
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
+  }
+  stream->download_recvd += (curl_off_t)buflen;
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%zd",
+              stream->s.id, buflen, stream->download_recvd);
+  h3_drain_stream(cf, data);
+  return 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+                                  size_t consumed, void *user_data,
+                                  void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+
+  (void)conn;
+  (void)stream_id;
+  if(stream)
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes",
+                stream->s.id, consumed);
+  return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+                             int32_t token, nghttp3_rcbuf *name,
+                             nghttp3_rcbuf *value, uint8_t flags,
+                             void *user_data, void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+  nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)stream_id;
+  (void)token;
+  (void)flags;
+  (void)cf;
+
+  /* we might have cleaned up this transfer already */
+  if(!stream)
+    return 0;
+
+  if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
+    char line[14]; /* status line is always 13 characters long */
+    size_t ncopy;
+
+    result = Curl_http_decode_status(&stream->status_code,
+                                     (const char *)h3val.base, h3val.len);
+    if(result)
+      return -1;
+    ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
+                      stream->status_code);
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
+    result = write_resp_raw(cf, data, line, ncopy, FALSE);
+    if(result) {
+      return -1;
+    }
+  }
+  else {
+    /* store as an HTTP1-style header */
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
+                stream_id, (int)h3name.len, h3name.base,
+                (int)h3val.len, h3val.base);
+    result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
+    if(result) {
+      return -1;
+    }
+    result = write_resp_raw(cf, data, ": ", 2, FALSE);
+    if(result) {
+      return -1;
+    }
+    result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
+    if(result) {
+      return -1;
+    }
+    result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+    if(result) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+                             int fin, void *user_data, void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  CURLcode result = CURLE_OK;
+  (void)conn;
+  (void)stream_id;
+  (void)fin;
+  (void)cf;
+
+  if(!stream)
+    return 0;
+  /* add a CRLF only if we've received some headers */
+  result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+  if(result) {
+    return -1;
+  }
+
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
+              stream_id, stream->status_code);
+  if(stream->status_code / 100 != 1) {
+    stream->resp_hds_complete = TRUE;
+  }
+  h3_drain_stream(cf, data);
+  return 0;
+}
+
+static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+                              uint64_t app_error_code, void *user_data,
+                              void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  (void)conn;
+  (void)app_error_code;
+
+  if(!stream || !stream->s.ssl)
+    return 0;
+
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id);
+  cf_osslq_stream_close(&stream->s);
+  return 0;
+}
+
+static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
+                              uint64_t app_error_code, void *user_data,
+                              void *stream_user_data) {
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  int rv;
+  (void)conn;
+
+  if(stream && stream->s.ssl) {
+    SSL_STREAM_RESET_ARGS args = {0};
+    args.quic_error_code = app_error_code;
+    rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
+    if(!rv) {
+      return NGHTTP3_ERR_CALLBACK_FAILURE;
+    }
+  }
+  return 0;
+}
+
+static nghttp3_ssize
+cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
+                    nghttp3_vec *vec, size_t veccnt,
+                    uint32_t *pflags, void *user_data,
+                    void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  ssize_t nwritten = 0;
+  size_t nvecs = 0;
+  (void)cf;
+  (void)conn;
+  (void)stream_id;
+  (void)user_data;
+  (void)veccnt;
+
+  if(!stream)
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
+  /* nghttp3 keeps references to the sendbuf data until it is ACKed
+   * by the server (see `cb_h3_acked_req_body()` for updates).
+   * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
+   * that we have already passed to nghttp3, but which have not been
+   * ACKed yet.
+   * Any amount beyond `sendbuf_len_in_flight` we need still to pass
+   * to nghttp3. Do that now, if we can. */
+  if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
+    nvecs = 0;
+    while(nvecs < veccnt &&
+          Curl_bufq_peek_at(&stream->sendbuf,
+                            stream->sendbuf_len_in_flight,
+                            (const unsigned char **)&vec[nvecs].base,
+                            &vec[nvecs].len)) {
+      stream->sendbuf_len_in_flight += vec[nvecs].len;
+      nwritten += vec[nvecs].len;
+      ++nvecs;
+    }
+    DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
+  }
+
+  if(nwritten > 0 && stream->upload_left != -1)
+    stream->upload_left -= nwritten;
+
+  /* When we stopped sending and everything in `sendbuf` is "in flight",
+   * we are at the end of the request body. */
+  if(stream->upload_left == 0) {
+    *pflags = NGHTTP3_DATA_FLAG_EOF;
+    stream->send_closed = TRUE;
+  }
+  else if(!nwritten) {
+    /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
+                stream->s.id);
+    return NGHTTP3_ERR_WOULDBLOCK;
+  }
+
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
+              "%d vecs%s with %zu (buffered=%zu, left=%"
+              CURL_FORMAT_CURL_OFF_T ")",
+              stream->s.id, (int)nvecs,
+              *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+              nwritten, Curl_bufq_len(&stream->sendbuf),
+              stream->upload_left);
+  return (nghttp3_ssize)nvecs;
+}
+
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+                                   uint64_t datalen, void *user_data,
+                                   void *stream_user_data)
+{
+  struct Curl_cfilter *cf = user_data;
+  struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  size_t skiplen;
+
+  (void)cf;
+  if(!stream)
+    return 0;
+  /* The server acknowledged `datalen` of bytes from our request body.
+   * This is a delta. We have kept this data in `sendbuf` for
+   * re-transmissions and can free it now. */
+  if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
+    skiplen = stream->sendbuf_len_in_flight;
+  else
+    skiplen = (size_t)datalen;
+  Curl_bufq_skip(&stream->sendbuf, skiplen);
+  stream->sendbuf_len_in_flight -= skiplen;
+
+  /* Everything ACKed, we resume upload processing */
+  if(!stream->sendbuf_len_in_flight) {
+    int rv = nghttp3_conn_resume_stream(conn, stream_id);
+    if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+      return NGHTTP3_ERR_CALLBACK_FAILURE;
+    }
+  }
+  return 0;
+}
+
+static nghttp3_callbacks ngh3_callbacks = {
+  cb_h3_acked_stream_data,
+  cb_h3_stream_close,
+  cb_h3_recv_data,
+  cb_h3_deferred_consume,
+  NULL, /* begin_headers */
+  cb_h3_recv_header,
+  cb_h3_end_headers,
+  NULL, /* begin_trailers */
+  cb_h3_recv_header,
+  NULL, /* end_trailers */
+  cb_h3_stop_sending,
+  NULL, /* end_stream */
+  cb_h3_reset_stream,
+  NULL, /* shutdown */
+  NULL /* recv_settings */
+};
+
+static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
+                                  void *user_data)
+{
+  struct cf_osslq_h3conn *h3 = &ctx->h3;
+  CURLcode result;
+  int rc;
+
+  nghttp3_settings_default(&h3->settings);
+  rc = nghttp3_conn_client_new(&h3->conn,
+                               &ngh3_callbacks,
+                               &h3->settings,
+                               nghttp3_mem_default(),
+                               user_data);
+  if(rc) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  result = cf_osslq_stream_open(&h3->s_ctrl, conn,
+                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+                                &ctx->stream_bufcp, NULL);
+  if(result) {
+    result = CURLE_QUIC_CONNECT_ERROR;
+    goto out;
+  }
+  result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
+                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+                                &ctx->stream_bufcp, NULL);
+  if(result) {
+    result = CURLE_QUIC_CONNECT_ERROR;
+    goto out;
+  }
+  result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
+                                SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
+                                &ctx->stream_bufcp, NULL);
+  if(result) {
+    result = CURLE_QUIC_CONNECT_ERROR;
+    goto out;
+  }
+
+  rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
+  if(rc) {
+    result = CURLE_QUIC_CONNECT_ERROR;
+    goto out;
+  }
+  rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
+                                       h3->s_qpack_dec.id);
+  if(rc) {
+    result = CURLE_QUIC_CONNECT_ERROR;
+    goto out;
+  }
+
+  result = CURLE_OK;
+out:
+  return result;
+}
+
+static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result;
+  int rv;
+  const struct Curl_sockaddr_ex *peer_addr = NULL;
+  int peer_port;
+  BIO *bio = NULL;
+  BIO_ADDR *baddr = NULL;
+
+  Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
+                  H3_STREAM_POOL_SPARES);
+  result = Curl_ssl_peer_init(&ctx->peer, cf);
+  if(result)
+    goto out;
+
+#define H3_ALPN "\x2h3"
+  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+                               H3_ALPN, sizeof(H3_ALPN) - 1,
+                               NULL, NULL);
+  if(result)
+    goto out;
+
+  result = vquic_ctx_init(&ctx->q);
+  if(result)
+    goto out;
+
+  result = CURLE_QUIC_CONNECT_ERROR;
+  Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+                      &peer_addr, NULL, &peer_port, NULL, NULL);
+  if(!peer_addr)
+    goto out;
+
+  ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
+  rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
+                   &ctx->q.local_addrlen);
+  if(rv == -1)
+    goto out;
+
+  result = make_bio_addr(&baddr, peer_addr);
+  if(result) {
+    failf(data, "error creating BIO_ADDR from sockaddr");
+    goto out;
+  }
+
+  bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
+  if(!bio) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
+    failf(data, "failed to set the initial peer address");
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+  if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
+    failf(data, "failed to turn off blocking mode");
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  SSL_set_bio(ctx->tls.ssl, bio, bio);
+  bio = NULL;
+  SSL_set_connect_state(ctx->tls.ssl);
+  SSL_set_incoming_stream_policy(ctx->tls.ssl,
+                                 SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
+  /* setup the H3 things on top of the QUIC connection */
+  result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
+
+out:
+  if(bio)
+    BIO_free(bio);
+  if(baddr)
+    BIO_ADDR_free(baddr);
+  CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
+  return result;
+}
+
+struct h3_quic_recv_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_easy *data;
+  struct cf_osslq_stream *s;
+};
+
+static ssize_t h3_quic_recv(void *reader_ctx,
+                            unsigned char *buf, size_t len,
+                            CURLcode *err)
+{
+  struct h3_quic_recv_ctx *x = reader_ctx;
+  size_t nread;
+  int rv;
+
+  *err = CURLE_OK;
+  rv = SSL_read_ex(x->s->ssl, buf, len, &nread);
+  if(rv <= 0) {
+    int detail = SSL_get_error(x->s->ssl, rv);
+    if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
+      *err = CURLE_AGAIN;
+      return -1;
+    }
+    else if(detail == SSL_ERROR_ZERO_RETURN) {
+      CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS",
+                  x->s->id);
+      x->s->recvd_eos = TRUE;
+      return 0;
+    }
+    else if(SSL_get_stream_read_state(x->s->ssl) ==
+            SSL_STREAM_STATE_RESET_REMOTE) {
+      uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
+      SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
+      CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
+                  "rv=%d, app_err=%" PRIu64,
+                   x->s->id, rv, app_error_code);
+      if(app_error_code != NGHTTP3_H3_NO_ERROR) {
+        x->s->reset = TRUE;
+      }
+      x->s->recvd_eos = TRUE;
+      return 0;
+    }
+    else {
+      *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
+      return -1;
+    }
+  }
+  else {
+    /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes",
+                x->s->id, nread); */
+  }
+  return (ssize_t)nread;
+}
+
+static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
+                                     struct Curl_cfilter *cf,
+                                     struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+  ssize_t nread;
+  struct h3_quic_recv_ctx x;
+  int rv, eagain = FALSE;
+  size_t total_recv_len = 0;
+
+  DEBUGASSERT(s);
+  if(s->closed)
+    return CURLE_OK;
+
+  x.cf = cf;
+  x.data = data;
+  x.s = s;
+  while(s->ssl && !s->closed && !eagain &&
+        (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
+    if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
+      while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
+        nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result);
+        if(nread < 0) {
+          if(result != CURLE_AGAIN)
+            goto out;
+          result = CURLE_OK;
+          eagain = TRUE;
+        }
+      }
+    }
+
+    /* Forward what we have to nghttp3 */
+    if(!Curl_bufq_is_empty(&s->recvbuf)) {
+      const unsigned char *buf;
+      size_t blen;
+
+      while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
+        nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
+                                         buf, blen, 0);
+        CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes "
+                    "to nghttp3 -> %zd", s->id, blen, nread);
+        if(nread < 0) {
+          failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
+                blen, nghttp3_strerror((int)nread));
+          result = CURLE_RECV_ERROR;
+          goto out;
+        }
+        /* success, `nread` is the flow for QUIC to count as "consumed",
+         * not sure how that will work with OpenSSL. Anyways, without error,
+         * all data that we passed is not owned by nghttp3. */
+        Curl_bufq_skip(&s->recvbuf, blen);
+        total_recv_len += blen;
+      }
+    }
+
+    /* When we forwarded everything, handle RESET/EOS */
+    if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
+      result = CURLE_OK;
+      if(s->reset) {
+        uint64_t app_error;
+        if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
+          failf(data, "SSL_get_stream_read_error_code returned error");
+          result = CURLE_RECV_ERROR;
+          goto out;
+        }
+        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
+        s->closed = TRUE;
+        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+          failf(data, "nghttp3_conn_close_stream returned error: %s",
+                nghttp3_strerror(rv));
+          result = CURLE_RECV_ERROR;
+          goto out;
+        }
+      }
+      else if(s->recvd_eos) {
+        rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
+                                       NGHTTP3_H3_NO_ERROR);
+        s->closed = TRUE;
+        CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d",
+                    s->id, rv);
+        if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+          failf(data, "nghttp3_conn_close_stream returned error: %s",
+                nghttp3_strerror(rv));
+          result = CURLE_RECV_ERROR;
+          goto out;
+        }
+      }
+    }
+  }
+out:
+  if(result)
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d",
+                s->id, result);
+  return result;
+}
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  if(!ctx->tls.ssl)
+    goto out;
+
+  ERR_clear_error();
+
+  /* 1. Check for new incoming streams */
+  while(1) {
+    SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
+    if(!snew)
+      break;
+
+    (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
+  }
+
+  if(!SSL_handle_events(ctx->tls.ssl)) {
+    int detail = SSL_get_error(ctx->tls.ssl, 0);
+    result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
+  }
+
+  if(ctx->h3.conn) {
+    size_t i;
+    for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
+      result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
+      if(result)
+        goto out;
+    }
+  }
+
+  if(ctx->h3.conn) {
+    struct Curl_easy *sdata;
+    struct h3_stream_ctx *stream;
+    /* PULL all open streams */
+    DEBUGASSERT(data->multi);
+    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+      if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
+        stream = H3_STREAM_CTX(sdata);
+        if(stream && !stream->closed &&
+           !Curl_bufq_is_full(&stream->recvbuf)) {
+          result = cf_osslq_stream_recv(&stream->s, cf, sdata);
+          if(result)
+            goto out;
+        }
+      }
+    }
+  }
+
+out:
+  CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
+  return result;
+}
+
+/* Iterate over all streams and check if blocked can be unblocked */
+static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
+                                           struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct Curl_easy *sdata;
+  struct h3_stream_ctx *stream;
+
+  if(ctx->h3.conn) {
+    for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+      if(sdata->conn == data->conn) {
+        stream = H3_STREAM_CTX(sdata);
+        if(stream && stream->s.ssl && stream->s.send_blocked &&
+           !SSL_want_write(stream->s.ssl)) {
+          nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
+          stream->s.send_blocked = FALSE;
+          h3_drain_stream(cf, sdata);
+          CURL_TRC_CF(sdata, cf, "unblocked");
+        }
+      }
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode h3_send_streams(struct Curl_cfilter *cf,
+                                struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  if(!ctx->tls.ssl || !ctx->h3.conn)
+    goto out;
+
+  for(;;) {
+    struct cf_osslq_stream *s = NULL;
+    nghttp3_vec vec[16];
+    nghttp3_ssize n, i;
+    int64_t stream_id;
+    size_t written;
+    int eos, ok, rv;
+    size_t total_len, acked_len = 0;
+    bool blocked = FALSE;
+
+    n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
+                                   vec, ARRAYSIZE(vec));
+    if(n < 0) {
+      failf(data, "nghttp3_conn_writev_stream returned error: %s",
+            nghttp3_strerror((int)n));
+      result = CURLE_SEND_ERROR;
+      goto out;
+    }
+    if(stream_id < 0) {
+      result = CURLE_OK;
+      goto out;
+    }
+
+    /* Get the stream for this data */
+    s = cf_osslq_get_qstream(cf, data, stream_id);
+    if(!s) {
+      failf(data, "nghttp3_conn_writev_stream gave unknown stream %" PRId64,
+            stream_id);
+      result = CURLE_SEND_ERROR;
+      goto out;
+    }
+    /* Now write the data to the stream's SSL*, it may not all fit! */
+    DEBUGASSERT(s->id == stream_id);
+    for(i = 0, total_len = 0; i < n; ++i) {
+      total_len += vec[i].len;
+    }
+    for(i = 0; (i < n) && !blocked; ++i) {
+      /* Without stream->s.ssl, we closed that already, so
+       * pretend the write did succeed. */
+      written = vec[i].len;
+      ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
+                                   &written);
+      if(ok) {
+        /* As OpenSSL buffers the data, we count this as acknowledged
+         * from nghttp3's point of view */
+        CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok",
+              s->id, vec[i].len);
+        acked_len += vec[i].len;
+      }
+      else {
+        int detail = SSL_get_error(s->ssl, 0);
+        switch(detail) {
+        case SSL_ERROR_WANT_WRITE:
+        case SSL_ERROR_WANT_READ:
+          /* QUIC blocked us from writing more */
+          CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
+                s->id, vec[i].len);
+          written = 0;
+          nghttp3_conn_block_stream(ctx->h3.conn, s->id);
+          s->send_blocked = blocked = TRUE;
+          break;
+        default:
+          failf(data, "[%"PRId64"] send %zu bytes to QUIC, SSL error %d",
+                s->id, vec[i].len, detail);
+          result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
+          goto out;
+        }
+      }
+    }
+
+    if(acked_len > 0 || (eos && !s->send_blocked)) {
+      /* Since QUIC buffers the data written internally, we can tell
+       * nghttp3 that it can move forward on it */
+      rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
+      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+        failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+              nghttp3_strerror(rv));
+        result = CURLE_SEND_ERROR;
+        goto out;
+      }
+      rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
+      if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
+        failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n",
+              nghttp3_strerror(rv));
+        result = CURLE_SEND_ERROR;
+        goto out;
+      }
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes "
+                  "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
+    }
+
+    if(eos && !s->send_blocked) {
+      /* wrote everything and H3 indicates end of stream */
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
+      SSL_stream_conclude(s->ssl, 0);
+    }
+  }
+
+out:
+  CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
+  return result;
+}
+
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  if(!ctx->tls.ssl)
+    goto out;
+
+  ERR_clear_error();
+  result = h3_send_streams(cf, data);
+  if(result)
+    goto out;
+
+  if(!SSL_handle_events(ctx->tls.ssl)) {
+    int detail = SSL_get_error(ctx->tls.ssl, 0);
+    result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
+  }
+
+  result = cf_osslq_check_and_unblock(cf, data);
+
+out:
+  CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
+  return result;
+}
+
+static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+  struct timeval tv;
+  timediff_t timeoutms;
+  int is_infinite = TRUE;
+
+  if(ctx->tls.ssl &&
+    SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
+    !is_infinite) {
+    timeoutms = curlx_tvtoms(&tv);
+    /* QUIC want to be called again latest at the returned timeout */
+    if(timeoutms <= 0) {
+      result = cf_progress_ingress(cf, data);
+      if(result)
+        goto out;
+      result = cf_progress_egress(cf, data);
+      if(result)
+        goto out;
+      if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
+        timeoutms = curlx_tvtoms(&tv);
+      }
+    }
+    if(!is_infinite) {
+      Curl_expire(data, timeoutms, EXPIRE_QUIC);
+      CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
+    }
+  }
+out:
+  return result;
+}
+
+static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool blocking, bool *done)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+  struct cf_call_data save;
+  struct curltime now;
+  int err;
+
+  if(cf->connected) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  /* Connect the UDP filter first */
+  if(!cf->next->connected) {
+    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    if(result || !*done)
+      return result;
+  }
+
+  *done = FALSE;
+  now = Curl_now();
+  CF_DATA_SAVE(save, cf, data);
+
+  if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+    /* Not time yet to attempt the next connect */
+    CURL_TRC_CF(data, cf, "waiting for reconnect time");
+    goto out;
+  }
+
+  if(!ctx->tls.ssl) {
+    ctx->started_at = now;
+    result = cf_osslq_ctx_start(cf, data);
+    if(result)
+      goto out;
+  }
+
+  if(!ctx->got_first_byte) {
+    int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
+    if(readable > 0 && (readable & CURL_CSELECT_IN)) {
+      ctx->got_first_byte = TRUE;
+      ctx->first_byte_at = Curl_now();
+    }
+  }
+
+  ERR_clear_error();
+  err = SSL_do_handshake(ctx->tls.ssl);
+
+  if(err == 1) {
+    /* connected */
+    ctx->handshake_at = now;
+    CURL_TRC_CF(data, cf, "handshake complete after %dms",
+               (int)Curl_timediff(now, ctx->started_at));
+    result = cf_osslq_verify_peer(cf, data);
+    if(!result) {
+      CURL_TRC_CF(data, cf, "peer verified");
+      cf->connected = TRUE;
+      cf->conn->alpn = CURL_HTTP_VERSION_3;
+      *done = TRUE;
+      connkeep(cf->conn, "HTTP/3 default");
+    }
+  }
+  else {
+    int detail = SSL_get_error(ctx->tls.ssl, err);
+    switch(detail) {
+    case SSL_ERROR_WANT_READ:
+      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
+      result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
+      goto out;
+    case SSL_ERROR_WANT_WRITE:
+      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
+      result = CURLE_OK;
+      goto out;
+#ifdef SSL_ERROR_WANT_ASYNC
+    case SSL_ERROR_WANT_ASYNC:
+      CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
+      result = CURLE_OK;
+      goto out;
+#endif
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+    case SSL_ERROR_WANT_RETRY_VERIFY:
+      result = CURLE_OK;
+      goto out;
+#endif
+    default:
+      result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
+      goto out;
+    }
+  }
+
+out:
+  if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
+    /* When a QUIC server instance is shutting down, it may send us a
+     * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+     * state. The CONNECT may work in the near future again. Indicate
+     * that as a "weird" reply. */
+    result = CURLE_WEIRD_SERVER_REPLY;
+  }
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  if(result) {
+    const char *r_ip = NULL;
+    int r_port = 0;
+
+    Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+                        &r_ip, &r_port, NULL, NULL);
+    infof(data, "QUIC connect to %s port %u failed: %s",
+          r_ip, r_port, curl_easy_strerror(result));
+  }
+#endif
+  if(!result)
+    result = check_and_set_expiry(cf, data);
+  if(result || *done)
+    CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
+  CF_DATA_RESTORE(cf, save);
+  return result;
+}
+
+static ssize_t h3_stream_open(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              const void *buf, size_t len,
+                              CURLcode *err)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = NULL;
+  struct dynhds h2_headers;
+  size_t nheader;
+  nghttp3_nv *nva = NULL;
+  int rc = 0;
+  unsigned int i;
+  ssize_t nwritten = -1;
+  nghttp3_data_reader reader;
+  nghttp3_data_reader *preader = NULL;
+
+  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
+
+  *err = h3_data_setup(cf, data);
+  if(*err)
+    goto out;
+  stream = H3_STREAM_CTX(data);
+  DEBUGASSERT(stream);
+  if(!stream) {
+    *err = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
+  if(nwritten < 0)
+    goto out;
+  if(!stream->h1.done) {
+    /* need more data */
+    goto out;
+  }
+  DEBUGASSERT(stream->h1.req);
+
+  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
+  if(*err) {
+    nwritten = -1;
+    goto out;
+  }
+  /* no longer needed */
+  Curl_h1_req_parse_free(&stream->h1);
+
+  nheader = Curl_dynhds_count(&h2_headers);
+  nva = malloc(sizeof(nghttp3_nv) * nheader);
+  if(!nva) {
+    *err = CURLE_OUT_OF_MEMORY;
+    nwritten = -1;
+    goto out;
+  }
+
+  for(i = 0; i < nheader; ++i) {
+    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
+    nva[i].name = (unsigned char *)e->name;
+    nva[i].namelen = e->namelen;
+    nva[i].value = (unsigned char *)e->value;
+    nva[i].valuelen = e->valuelen;
+    nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+  }
+
+  DEBUGASSERT(stream->s.id == -1);
+  *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
+                              &ctx->stream_bufcp, data);
+  if(*err) {
+    failf(data, "can't get bidi streams");
+    *err = CURLE_SEND_ERROR;
+    goto out;
+  }
+
+  switch(data->state.httpreq) {
+  case HTTPREQ_POST:
+  case HTTPREQ_POST_FORM:
+  case HTTPREQ_POST_MIME:
+  case HTTPREQ_PUT:
+    /* known request body size or -1 */
+    if(data->state.infilesize != -1)
+      stream->upload_left = data->state.infilesize;
+    else
+      /* data sending without specifying the data amount up front */
+      stream->upload_left = -1; /* unknown */
+    break;
+  default:
+    /* there is not request body */
+    stream->upload_left = 0; /* no request body */
+    break;
+  }
+
+  stream->send_closed = (stream->upload_left == 0);
+  if(!stream->send_closed) {
+    reader.read_data = cb_h3_read_req_body;
+    preader = &reader;
+  }
+
+  rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id,
+                                   nva, nheader, preader, data);
+  if(rc) {
+    switch(rc) {
+    case NGHTTP3_ERR_CONN_CLOSING:
+      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+                  "connection is closing", stream->s.id);
+      break;
+    default:
+      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+                  stream->s.id, rc, nghttp3_strerror(rc));
+      break;
+    }
+    *err = CURLE_SEND_ERROR;
+    nwritten = -1;
+    goto out;
+  }
+
+  if(Curl_trc_is_verbose(data)) {
+    infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+          stream->s.id, data->state.url);
+    for(i = 0; i < nheader; ++i) {
+      infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id,
+            (int)nva[i].namelen, nva[i].name,
+            (int)nva[i].valuelen, nva[i].value);
+    }
+  }
+
+out:
+  free(nva);
+  Curl_dynhds_free(&h2_headers);
+  return nwritten;
+}
+
+static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                             const void *buf, size_t len, CURLcode *err)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  struct cf_call_data save;
+  ssize_t nwritten;
+  CURLcode result;
+
+  CF_DATA_SAVE(save, cf, data);
+  DEBUGASSERT(cf->connected);
+  DEBUGASSERT(ctx->tls.ssl);
+  DEBUGASSERT(ctx->h3.conn);
+  *err = CURLE_OK;
+
+  result = cf_progress_ingress(cf, data);
+  if(result) {
+    *err = result;
+    nwritten = -1;
+    goto out;
+  }
+
+  result = cf_progress_egress(cf, data);
+  if(result) {
+    *err = result;
+    nwritten = -1;
+    goto out;
+  }
+
+  if(!stream || stream->s.id < 0) {
+    nwritten = h3_stream_open(cf, data, buf, len, err);
+    if(nwritten < 0) {
+      CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
+      goto out;
+    }
+    stream = H3_STREAM_CTX(data);
+  }
+  else if(stream->upload_blocked_len) {
+    /* the data in `buf` has already been submitted or added to the
+     * buffers, but have been EAGAINed on the last invocation. */
+    DEBUGASSERT(len >= stream->upload_blocked_len);
+    if(len < stream->upload_blocked_len) {
+      /* Did we get called again with a smaller `len`? This should not
+       * happen. We are not prepared to handle that. */
+      failf(data, "HTTP/3 send again with decreased length");
+      *err = CURLE_HTTP3;
+      nwritten = -1;
+      goto out;
+    }
+    nwritten = (ssize_t)stream->upload_blocked_len;
+    stream->upload_blocked_len = 0;
+  }
+  else if(stream->closed) {
+    if(stream->resp_hds_complete) {
+      /* Server decided to close the stream after having sent us a final
+       * response. This is valid if it is not interested in the request
+       * body. This happens on 30x or 40x responses.
+       * We silently discard the data sent, since this is not a transport
+       * error situation. */
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
+                  "on closed stream with response", stream->s.id);
+      *err = CURLE_OK;
+      nwritten = (ssize_t)len;
+      goto out;
+    }
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                "-> stream closed", stream->s.id, len);
+    *err = CURLE_HTTP3;
+    nwritten = -1;
+    goto out;
+  }
+  else {
+    nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
+                "sendbuf(len=%zu) -> %zd, %d",
+                stream->s.id, len, nwritten, *err);
+    if(nwritten < 0) {
+      goto out;
+    }
+
+    (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
+  }
+
+  result = cf_progress_egress(cf, data);
+  if(result) {
+    *err = result;
+    nwritten = -1;
+  }
+
+  if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) {
+    /* We have unacknowledged DATA and cannot report success to our
+     * caller. Instead we EAGAIN and remember how much we have already
+     * "written" into our various internal connection buffers. */
+    stream->upload_blocked_len = nwritten;
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
+                "%zu bytes in flight -> EGAIN", stream->s.id, len,
+                stream->sendbuf_len_in_flight);
+    *err = CURLE_AGAIN;
+    nwritten = -1;
+  }
+
+out:
+  result = check_and_set_expiry(cf, data);
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+              stream? stream->s.id : -1, len, nwritten, *err);
+  CF_DATA_RESTORE(cf, save);
+  return nwritten;
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  struct h3_stream_ctx *stream,
+                                  CURLcode *err)
+{
+  ssize_t nread = -1;
+
+  (void)cf;
+  if(stream->reset) {
+    failf(data,
+          "HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
+    *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
+    goto out;
+  }
+  else if(!stream->resp_hds_complete) {
+    failf(data,
+          "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+          " all response header fields, treated as error",
+          stream->s.id);
+    *err = CURLE_HTTP3;
+    goto out;
+  }
+  *err = CURLE_OK;
+  nread = 0;
+
+out:
+  return nread;
+}
+
+static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                             char *buf, size_t len, CURLcode *err)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  ssize_t nread = -1;
+  struct cf_call_data save;
+  CURLcode result;
+
+  (void)ctx;
+  CF_DATA_SAVE(save, cf, data);
+  DEBUGASSERT(cf->connected);
+  DEBUGASSERT(ctx);
+  DEBUGASSERT(ctx->tls.ssl);
+  DEBUGASSERT(ctx->h3.conn);
+  *err = CURLE_OK;
+
+  if(!stream) {
+    *err = CURLE_RECV_ERROR;
+    goto out;
+  }
+
+  if(!Curl_bufq_is_empty(&stream->recvbuf)) {
+    nread = Curl_bufq_read(&stream->recvbuf,
+                           (unsigned char *)buf, len, err);
+    if(nread < 0) {
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                  "-> %zd, %d", stream->s.id, len, nread, *err);
+      goto out;
+    }
+  }
+
+  result = cf_progress_ingress(cf, data);
+  if(result) {
+    *err = result;
+    nread = -1;
+    goto out;
+  }
+
+  /* recvbuf had nothing before, maybe after progressing ingress? */
+  if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
+    nread = Curl_bufq_read(&stream->recvbuf,
+                           (unsigned char *)buf, len, err);
+    if(nread < 0) {
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                  "-> %zd, %d", stream->s.id, len, nread, *err);
+      goto out;
+    }
+  }
+
+  if(nread > 0) {
+    h3_drain_stream(cf, data);
+  }
+  else {
+    if(stream->closed) {
+      nread = recv_closed_stream(cf, data, stream, err);
+      goto out;
+    }
+    *err = CURLE_AGAIN;
+    nread = -1;
+  }
+
+out:
+  if(cf_progress_egress(cf, data)) {
+    *err = CURLE_SEND_ERROR;
+    nread = -1;
+  }
+  else {
+    CURLcode result2 = check_and_set_expiry(cf, data);
+    if(result2) {
+      *err = result2;
+      nread = -1;
+    }
+  }
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
+              stream? stream->s.id : -1, len, nread, *err);
+  CF_DATA_RESTORE(cf, save);
+  return nread;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
+                                  const struct Curl_easy *data)
+{
+  const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+  (void)cf;
+  return stream && !Curl_bufq_is_empty(&stream->recvbuf);
+}
+
+static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    int event, int arg1, void *arg2)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  (void)arg1;
+  (void)arg2;
+  switch(event) {
+  case CF_CTRL_DATA_SETUP:
+    break;
+  case CF_CTRL_DATA_PAUSE:
+    result = h3_data_pause(cf, data, (arg1 != 0));
+    break;
+  case CF_CTRL_DATA_DETACH:
+    h3_data_done(cf, data);
+    break;
+  case CF_CTRL_DATA_DONE:
+    h3_data_done(cf, data);
+    break;
+  case CF_CTRL_DATA_DONE_SEND: {
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+    if(stream && !stream->send_closed) {
+      stream->send_closed = TRUE;
+      stream->upload_left = Curl_bufq_len(&stream->sendbuf);
+      (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
+    }
+    break;
+  }
+  case CF_CTRL_DATA_IDLE: {
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+    CURL_TRC_CF(data, cf, "data idle");
+    if(stream && !stream->closed) {
+      result = check_and_set_expiry(cf, data);
+    }
+    break;
+  }
+  default:
+    break;
+  }
+  CF_DATA_RESTORE(cf, save);
+  return result;
+}
+
+static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   bool *input_pending)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  bool alive = FALSE;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  *input_pending = FALSE;
+  if(!ctx->tls.ssl)
+    goto out;
+
+  /* TODO: how to check negotiated connection idle time? */
+
+  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+    goto out;
+
+  alive = TRUE;
+  if(*input_pending) {
+    CURLcode result;
+    /* This happens before we've sent off a request and the connection is
+       not in use by any other transfer, there shouldn't be any data here,
+       only "protocol frames" */
+    *input_pending = FALSE;
+    result = cf_progress_ingress(cf, data);
+    CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
+    alive = result? FALSE : TRUE;
+  }
+
+out:
+  CF_DATA_RESTORE(cf, save);
+  return alive;
+}
+
+static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct easy_pollset *ps)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+
+  if(!ctx->tls.ssl) {
+    /* NOP */
+  }
+  else if(!cf->connected) {
+    /* during handshake, transfer has not started yet. we always
+     * add our socket for polling if SSL wants to send/recv */
+    Curl_pollset_set(data, ps, ctx->q.sockfd,
+                     SSL_net_read_desired(ctx->tls.ssl),
+                     SSL_net_write_desired(ctx->tls.ssl));
+  }
+  else {
+    /* once connected, we only modify the socket if it is present.
+     * this avoids adding it for paused transfers. */
+    bool want_recv, want_send;
+    Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+    if(want_recv || want_send) {
+      Curl_pollset_set(data, ps, ctx->q.sockfd,
+                       SSL_net_read_desired(ctx->tls.ssl),
+                       SSL_net_write_desired(ctx->tls.ssl));
+    }
+  }
+}
+
+static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               int query, int *pres1, void *pres2)
+{
+  struct cf_osslq_ctx *ctx = cf->ctx;
+  struct cf_call_data save;
+
+  switch(query) {
+  case CF_QUERY_MAX_CONCURRENT: {
+    /* TODO: how to get this? */
+    CF_DATA_SAVE(save, cf, data);
+    *pres1 = 100;
+    CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
+    CF_DATA_RESTORE(cf, save);
+    return CURLE_OK;
+  }
+  case CF_QUERY_CONNECT_REPLY_MS:
+    if(ctx->got_first_byte) {
+      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+      *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+    }
+    else
+      *pres1 = -1;
+    return CURLE_OK;
+  case CF_QUERY_TIMER_CONNECT: {
+    struct curltime *when = pres2;
+    if(ctx->got_first_byte)
+      *when = ctx->first_byte_at;
+    return CURLE_OK;
+  }
+  case CF_QUERY_TIMER_APPCONNECT: {
+    struct curltime *when = pres2;
+    if(cf->connected)
+      *when = ctx->handshake_at;
+    return CURLE_OK;
+  }
+  default:
+    break;
+  }
+  return cf->next?
+    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+    CURLE_UNKNOWN_OPTION;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+  "HTTP/3",
+  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+  0,
+  cf_osslq_destroy,
+  cf_osslq_connect,
+  cf_osslq_close,
+  Curl_cf_def_get_host,
+  cf_osslq_adjust_pollset,
+  cf_osslq_data_pending,
+  cf_osslq_send,
+  cf_osslq_recv,
+  cf_osslq_data_event,
+  cf_osslq_conn_is_alive,
+  Curl_cf_def_conn_keep_alive,
+  cf_osslq_query,
+};
+
+CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
+                              struct Curl_easy *data,
+                              struct connectdata *conn,
+                              const struct Curl_addrinfo *ai)
+{
+  struct cf_osslq_ctx *ctx = NULL;
+  struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+  CURLcode result;
+
+  (void)data;
+  ctx = calloc(1, sizeof(*ctx));
+  if(!ctx) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+  cf_osslq_ctx_clear(ctx);
+
+  result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+  if(result)
+    goto out;
+
+  result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+  if(result)
+    goto out;
+
+  cf->conn = conn;
+  udp_cf->conn = cf->conn;
+  udp_cf->sockindex = cf->sockindex;
+  cf->next = udp_cf;
+
+out:
+  *pcf = (!result)? cf : NULL;
+  if(result) {
+    if(udp_cf)
+      Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
+    Curl_safefree(cf);
+    Curl_safefree(ctx);
+  }
+  return result;
+}
+
+bool Curl_conn_is_osslq(const struct Curl_easy *data,
+                        const struct connectdata *conn,
+                        int sockindex)
+{
+  struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+  (void)data;
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_http3)
+      return TRUE;
+    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+      return FALSE;
+  }
+  return FALSE;
+}
+
+/*
+ * Store ngtcp2 version info in this buffer.
+ */
+void Curl_osslq_ver(char *p, size_t len)
+{
+  const nghttp3_info *ht3 = nghttp3_version(0);
+  (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str);
+}
+
+#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.h b/Utilities/cmcurl/lib/vquic/curl_osslq.h
new file mode 100644
index 0000000..0e12d70
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_osslq.h
@@ -0,0 +1,51 @@
+#ifndef HEADER_CURL_VQUIC_CURL_OSSLQ_H
+#define HEADER_CURL_VQUIC_CURL_OSSLQ_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
+
+struct Curl_cfilter;
+
+#include "urldata.h"
+
+void Curl_osslq_ver(char *p, size_t len);
+
+CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
+                              struct Curl_easy *data,
+                              struct connectdata *conn,
+                              const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_osslq(const struct Curl_easy *data,
+                        const struct connectdata *conn,
+                        int sockindex);
+#endif
+
+#endif /* HEADER_CURL_VQUIC_CURL_OSSLQ_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
index 3f5d327..fcb0eb8 100644
--- a/Utilities/cmcurl/lib/vquic/curl_quiche.c
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -43,6 +43,7 @@
 #include "http1.h"
 #include "vquic.h"
 #include "vquic_int.h"
+#include "vquic-tls.h"
 #include "curl_quiche.h"
 #include "transfer.h"
 #include "inet_pton.h"
@@ -55,10 +56,10 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* #define DEBUG_QUICHE */
+/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */
+#define CURL_H3_NO_ERROR  (0x0100)
 
 #define QUIC_MAX_STREAMS              (100)
-#define QUIC_IDLE_TIMEOUT        (60 * 1000) /* milliseconds */
 
 #define H3_STREAM_WINDOW_SIZE  (128 * 1024)
 #define H3_STREAM_CHUNK_SIZE    (16 * 1024)
@@ -84,30 +85,22 @@
   (void)msnprintf(p, len, "quiche/%s", quiche_version());
 }
 
-static void keylog_callback(const SSL *ssl, const char *line)
-{
-  (void)ssl;
-  Curl_tls_keylog_write_line(line);
-}
-
 struct cf_quiche_ctx {
   struct cf_quic_ctx q;
+  struct ssl_peer peer;
+  struct quic_tls_ctx tls;
   quiche_conn *qconn;
   quiche_config *cfg;
   quiche_h3_conn *h3c;
   quiche_h3_config *h3config;
   uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
-  SSL_CTX *sslctx;
-  SSL *ssl;
   struct curltime started_at;        /* time the current attempt started */
   struct curltime handshake_at;      /* time connect handshake finished */
-  struct curltime first_byte_at;     /* when first byte was recvd */
   struct curltime reconnect_at;      /* time the next attempt should start */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
   curl_off_t data_recvd;
-  size_t sends_on_hold;              /* # of streams with SEND_HOLD set */
+  uint64_t max_idle_ms;              /* max idle time for QUIC conn */
   BIT(goaway);                       /* got GOAWAY from server */
-  BIT(got_first_byte);               /* if first byte was received */
   BIT(x509_store_setup);             /* if x509 store has been set up */
 };
 
@@ -122,110 +115,25 @@
 static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
 {
   if(ctx) {
-    vquic_ctx_free(&ctx->q);
-    if(ctx->qconn)
-      quiche_conn_free(ctx->qconn);
-    if(ctx->h3config)
-      quiche_h3_config_free(ctx->h3config);
     if(ctx->h3c)
       quiche_h3_conn_free(ctx->h3c);
+    if(ctx->h3config)
+      quiche_h3_config_free(ctx->h3config);
+    if(ctx->qconn)
+      quiche_conn_free(ctx->qconn);
     if(ctx->cfg)
       quiche_config_free(ctx->cfg);
+    /* quiche just freed ctx->tls.ssl */
+    ctx->tls.ssl = NULL;
+    Curl_vquic_tls_cleanup(&ctx->tls);
+    Curl_ssl_peer_cleanup(&ctx->peer);
+    vquic_ctx_free(&ctx->q);
     Curl_bufcp_free(&ctx->stream_bufcp);
+
     memset(ctx, 0, sizeof(*ctx));
   }
 }
 
-static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-
-  if(!ctx->x509_store_setup) {
-    if(cf->conn->ssl_config.verifypeer) {
-      const char * const ssl_cafile = cf->conn->ssl_config.CAfile;
-      const char * const ssl_capath = cf->conn->ssl_config.CApath;
-      if(ssl_cafile || ssl_capath) {
-        SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL);
-        /* tell OpenSSL where to find CA certificates that are used to verify
-           the server's certificate. */
-        if(!SSL_CTX_load_verify_locations(ctx->sslctx, ssl_cafile,
-                                          ssl_capath)) {
-          /* Fail if we insist on successfully verifying the server. */
-          failf(data, "error setting certificate verify locations:"
-                "  CAfile: %s CApath: %s",
-                ssl_cafile ? ssl_cafile : "none",
-                ssl_capath ? ssl_capath : "none");
-          return CURLE_SSL_CACERT_BADFILE;
-        }
-        infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
-        infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
-      }
-#ifdef CURL_CA_FALLBACK
-      else {
-        /* verifying the peer without any CA certificates won't work so
-           use openssl's built-in default as fallback */
-        SSL_CTX_set_default_verify_paths(ctx->sslctx);
-      }
-#endif
-    }
-    ctx->x509_store_setup = TRUE;
-  }
-  return CURLE_OK;
-}
-
-static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-  unsigned char checkip[16];
-  struct connectdata *conn = data->conn;
-  const char *curves = conn->ssl_config.curves;
-
-  DEBUGASSERT(!ctx->sslctx);
-  ctx->sslctx = SSL_CTX_new(TLS_method());
-  if(!ctx->sslctx)
-    return CURLE_OUT_OF_MEMORY;
-
-  SSL_CTX_set_alpn_protos(ctx->sslctx,
-                          (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
-                          sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
-
-  SSL_CTX_set_default_verify_paths(ctx->sslctx);
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback);
-  }
-
-  if(curves && !SSL_CTX_set1_curves_list(ctx->sslctx, curves)) {
-    failf(data, "failed setting curves list for QUIC: '%s'", curves);
-    return CURLE_SSL_CIPHER;
-  }
-
-  ctx->ssl = SSL_new(ctx->sslctx);
-  if(!ctx->ssl)
-    return CURLE_QUIC_CONNECT_ERROR;
-
-  SSL_set_app_data(ctx->ssl, cf);
-
-  if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
-#ifdef ENABLE_IPV6
-     && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
-#endif
-     ) {
-    char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
-    if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
-      failf(data, "Failed set SNI");
-      SSL_free(ctx->ssl);
-      ctx->ssl = NULL;
-      return CURLE_QUIC_CONNECT_ERROR;
-    }
-  }
-
-  return CURLE_OK;
-}
-
 /**
  * All about the H3 internals of a stream
  */
@@ -240,6 +148,7 @@
   bool send_closed; /* stream is locally closed */
   bool resp_hds_complete;  /* complete, final response has been received */
   bool resp_got_header; /* TRUE when h3 stream has recvd some HEADER */
+  BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
 };
 
 #define H3_STREAM_CTX(d)    ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
@@ -249,56 +158,20 @@
 #define H3_STREAM_ID(d)     (H3_STREAM_CTX(d)? \
                              H3_STREAM_CTX(d)->id : -2)
 
-static bool stream_send_is_suspended(struct Curl_easy *data)
-{
-  return (data->req.keepon & KEEP_SEND_HOLD);
-}
-
-static void stream_send_suspend(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-
-  if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
-    data->req.keepon |= KEEP_SEND_HOLD;
-    ++ctx->sends_on_hold;
-    if(H3_STREAM_ID(data) >= 0)
-      CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending",
-                  H3_STREAM_ID(data));
-    else
-      CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url);
-  }
-}
-
-static void stream_send_resume(struct Curl_cfilter *cf,
-                               struct Curl_easy *data)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-
-  if(stream_send_is_suspended(data)) {
-    data->req.keepon &= ~KEEP_SEND_HOLD;
-    --ctx->sends_on_hold;
-    if(H3_STREAM_ID(data) >= 0)
-      CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending",
-                  H3_STREAM_ID(data));
-    else
-      CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url);
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
-  }
-}
-
 static void check_resumes(struct Curl_cfilter *cf,
                           struct Curl_easy *data)
 {
-  struct cf_quiche_ctx *ctx = cf->ctx;
   struct Curl_easy *sdata;
+  struct stream_ctx *stream;
 
-  if(ctx->sends_on_hold) {
-    DEBUGASSERT(data->multi);
-    for(sdata = data->multi->easyp;
-        sdata && ctx->sends_on_hold; sdata = sdata->next) {
-      if(stream_send_is_suspended(sdata)) {
-        stream_send_resume(cf, sdata);
+  DEBUGASSERT(data->multi);
+  for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
+    if(sdata->conn == data->conn) {
+      stream = H3_STREAM_CTX(sdata);
+      if(stream && stream->quic_flow_blocked) {
+        stream->quic_flow_blocked = FALSE;
+        Curl_expire(data, 0, EXPIRE_RUN_NOW);
+        CURL_TRC_CF(data, cf, "[%"PRId64"] unblock", stream->id);
       }
     }
   }
@@ -333,9 +206,15 @@
   (void)cf;
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
-    if(stream_send_is_suspended(data)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
-      --ctx->sends_on_hold;
+    if(ctx->qconn && !stream->closed) {
+      quiche_conn_stream_shutdown(ctx->qconn, stream->id,
+                                  QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR);
+      if(!stream->send_closed) {
+        quiche_conn_stream_shutdown(ctx->qconn, stream->id,
+                                    QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR);
+        stream->send_closed = TRUE;
+      }
+      stream->closed = TRUE;
     }
     Curl_bufq_free(&stream->recvbuf);
     Curl_h1_req_parse_free(&stream->h1);
@@ -354,8 +233,8 @@
   bits = CURL_CSELECT_IN;
   if(stream && !stream->send_closed && stream->upload_left)
     bits |= CURL_CSELECT_OUT;
-  if(data->state.dselect_bits != bits) {
-    data->state.dselect_bits = bits;
+  if(data->state.select_bits != bits) {
+    data->state.select_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
 }
@@ -590,7 +469,6 @@
     }
     stream->closed = TRUE;
     streamclose(cf->conn, "End of stream");
-    data->req.keepon &= ~KEEP_SEND_HOLD;
     break;
 
   case QUICHE_H3_EVENT_GOAWAY:
@@ -686,7 +564,7 @@
       return CURLE_OK;
     }
     else if(QUICHE_ERR_TLS_FAIL == nread) {
-      long verify_ok = SSL_get_verify_result(ctx->ssl);
+      long verify_ok = SSL_get_verify_result(ctx->tls.ssl);
       if(verify_ok != X509_V_OK) {
         failf(r->data, "SSL certificate problem: %s",
               X509_verify_cert_error_string(verify_ok));
@@ -714,7 +592,7 @@
   CURLcode result;
 
   DEBUGASSERT(ctx->qconn);
-  result = quic_x509_store_setup(cf, data);
+  result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
   if(result)
     return result;
 
@@ -854,7 +732,7 @@
   if(stream->reset) {
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
-    *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_HTTP3;
     CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d",
                 stream->id, *err);
   }
@@ -864,7 +742,7 @@
           " all response header fields, treated as error",
           stream->id);
     /* *err = CURLE_PARTIAL_FILE; */
-    *err = CURLE_RECV_ERROR;
+    *err = CURLE_HTTP3;
     CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete"
                 " -> %d", stream->id, *err);
   }
@@ -883,6 +761,8 @@
   ssize_t nread = -1;
   CURLcode result;
 
+  vquic_ctx_update_time(&ctx->q);
+
   if(!stream) {
     *err = CURLE_RECV_ERROR;
     return -1;
@@ -1035,9 +915,8 @@
     if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
       /* quiche seems to report this error if the connection window is
        * exhausted. Which happens frequently and intermittent. */
-      CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED",
-                  data->state.url);
-      stream_send_suspend(cf, data);
+      CURL_TRC_CF(data, cf, "[%"PRId64"] blocked", stream->id);
+      stream->quic_flow_blocked = TRUE;
       *err = CURLE_AGAIN;
       nwritten = -1;
       goto out;
@@ -1081,6 +960,8 @@
   CURLcode result;
   ssize_t nwritten;
 
+  vquic_ctx_update_time(&ctx->q);
+
   *err = cf_process_ingress(cf, data);
   if(*err) {
     nwritten = -1;
@@ -1093,25 +974,8 @@
       goto out;
     stream = H3_STREAM_CTX(data);
   }
-  else {
-    bool eof = (stream->upload_left >= 0 &&
-                (curl_off_t)len >= stream->upload_left);
-    nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
-                                   (uint8_t *)buf, len, eof);
-    if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
-      /* TODO: we seem to be blocked on flow control and should HOLD
-       * sending. But when do we open again? */
-      if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
-        CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
-                    "-> window exhausted", stream->id, len);
-        stream_send_suspend(cf, data);
-      }
-      *err = CURLE_AGAIN;
-      nwritten = -1;
-      goto out;
-    }
-    else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE &&
-            stream->closed && stream->resp_hds_complete) {
+  else if(stream->closed) {
+    if(stream->resp_hds_complete) {
       /* sending request body on a stream that has been closed by the
        * server. If the server has send us a final response, we should
        * silently discard the send data.
@@ -1126,6 +990,36 @@
       nwritten = (ssize_t)len;
       goto out;
     }
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                "-> stream closed", stream->id, len);
+    *err = CURLE_HTTP3;
+    nwritten = -1;
+    goto out;
+  }
+  else {
+    bool eof = (stream->upload_left >= 0 &&
+                (curl_off_t)len >= stream->upload_left);
+    nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
+                                   (uint8_t *)buf, len, eof);
+    if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
+      /* TODO: we seem to be blocked on flow control and should HOLD
+       * sending. But when do we open again? */
+      if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
+        CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                    "-> window exhausted", stream->id, len);
+        stream->quic_flow_blocked = TRUE;
+      }
+      *err = CURLE_AGAIN;
+      nwritten = -1;
+      goto out;
+    }
+    else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) {
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                  "-> invalid stream state", stream->id, len);
+      *err = CURLE_HTTP3;
+      nwritten = -1;
+      goto out;
+    }
     else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
       CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
                   "-> exceeds size", stream->id, len);
@@ -1173,30 +1067,35 @@
   struct cf_quiche_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H3_STREAM_CTX(data);
 
-  return stream &&
-         quiche_conn_stream_writable(ctx->qconn, (uint64_t)stream->id, 1);
+  return stream && (quiche_conn_stream_writable(ctx->qconn,
+                                                (uint64_t)stream->id, 1) > 0);
 }
 
-static int cf_quiche_get_select_socks(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      curl_socket_t *socks)
+static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct SingleRequest *k = &data->req;
-  int rv = GETSOCK_BLANK;
+  bool want_recv, want_send;
 
-  socks[0] = ctx->q.sockfd;
+  if(!ctx->qconn)
+    return;
 
-  /* in an HTTP/3 connection we can basically always get a frame so we should
-     always be ready for one */
-  rv |= GETSOCK_READSOCK(0);
+  Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
+  if(want_recv || want_send) {
+    struct stream_ctx *stream = H3_STREAM_CTX(data);
+    bool c_exhaust, s_exhaust;
 
-  /* we're still uploading or the HTTP/3 layer wants to send data */
-  if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
-     && stream_is_writeable(cf, data))
-    rv |= GETSOCK_WRITESOCK(0);
+    c_exhaust = FALSE; /* Have not found any call in quiche that tells
+                          us if the connection itself is blocked */
+    s_exhaust = want_send && stream && stream->id >= 0 &&
+                (stream->quic_flow_blocked || !stream_is_writeable(cf, data));
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                 !Curl_bufq_is_empty(&ctx->q.sendbuf);
 
-  return rv;
+    Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send);
+  }
 }
 
 /*
@@ -1238,10 +1137,12 @@
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
     h3_data_done(cf, data);
     break;
-  }
+  case CF_CTRL_DATA_DONE:
+    h3_data_done(cf, data);
+    break;
   case CF_CTRL_DATA_DONE_SEND: {
     struct stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {
@@ -1272,61 +1173,6 @@
   return result;
 }
 
-static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
-                               struct Curl_easy *data)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-  cf->conn->httpversion = 30;
-  cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-
-  if(cf->conn->ssl_config.verifyhost) {
-    X509 *server_cert;
-    server_cert = SSL_get_peer_certificate(ctx->ssl);
-    if(!server_cert) {
-      result = CURLE_PEER_FAILED_VERIFICATION;
-      goto out;
-    }
-    result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
-    X509_free(server_cert);
-    if(result)
-      goto out;
-  }
-  else
-    CURL_TRC_CF(data, cf, "Skipped certificate verification");
-
-  ctx->h3config = quiche_h3_config_new();
-  if(!ctx->h3config) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  /* Create a new HTTP/3 connection on the QUIC connection. */
-  ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
-  if(!ctx->h3c) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-  if(data->set.ssl.certinfo)
-    /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, ctx->ssl);
-
-out:
-  if(result) {
-    if(ctx->h3config) {
-      quiche_h3_config_free(ctx->h3config);
-      ctx->h3config = NULL;
-    }
-    if(ctx->h3c) {
-      quiche_h3_conn_free(ctx->h3c);
-      ctx->h3c = NULL;
-    }
-  }
-  return result;
-}
-
 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
                                  struct Curl_easy *data)
 {
@@ -1345,6 +1191,7 @@
     debug_log_init = 1;
   }
 #endif
+  ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
   ctx->data_recvd = 0;
@@ -1353,13 +1200,17 @@
   if(result)
     return result;
 
+  result = Curl_ssl_peer_init(&ctx->peer, cf);
+  if(result)
+    return result;
+
   ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
   if(!ctx->cfg) {
     failf(data, "can't create quiche config");
     return CURLE_FAILED_INIT;
   }
   quiche_config_enable_pacing(ctx->cfg, false);
-  quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT);
+  quiche_config_set_max_idle_timeout(ctx->cfg, ctx->max_idle_ms * 1000);
   quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024)
     /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */);
   quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
@@ -1381,9 +1232,10 @@
                                        sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
                                        - 1);
 
-  DEBUGASSERT(!ctx->ssl);
-  DEBUGASSERT(!ctx->sslctx);
-  result = quic_ssl_setup(cf, data);
+  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
+                               QUICHE_H3_APPLICATION_PROTOCOL,
+                               sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
+                               NULL, cf);
   if(result)
     return result;
 
@@ -1404,14 +1256,14 @@
                                       (struct sockaddr *)&ctx->q.local_addr,
                                       ctx->q.local_addrlen,
                                       &sockaddr->sa_addr, sockaddr->addrlen,
-                                      ctx->cfg, ctx->ssl, false);
+                                      ctx->cfg, ctx->tls.ssl, false);
   if(!ctx->qconn) {
     failf(data, "can't create quiche connection");
     return CURLE_OUT_OF_MEMORY;
   }
 
   /* Known to not work on Windows */
-#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
+#if !defined(_WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
   {
     int qfd;
     (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd);
@@ -1443,13 +1295,24 @@
   return CURLE_OK;
 }
 
+static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct cf_quiche_ctx *ctx = cf->ctx;
+
+  cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+  cf->conn->httpversion = 30;
+  cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+  return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
+}
+
 static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   bool blocking, bool *done)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
-  struct curltime now;
 
   if(cf->connected) {
     *done = TRUE;
@@ -1464,9 +1327,10 @@
   }
 
   *done = FALSE;
-  now = Curl_now();
+  vquic_ctx_update_time(&ctx->q);
 
-  if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+  if(ctx->reconnect_at.tv_sec &&
+     Curl_timediff(ctx->q.last_op, ctx->reconnect_at) < 0) {
     /* Not time yet to attempt the next connect */
     CURL_TRC_CF(data, cf, "waiting for reconnect time");
     goto out;
@@ -1476,7 +1340,7 @@
     result = cf_connect_start(cf, data);
     if(result)
       goto out;
-    ctx->started_at = now;
+    ctx->started_at = ctx->q.last_op;
     result = cf_flush_egress(cf, data);
     /* we do not expect to be able to recv anything yet */
     goto out;
@@ -1491,12 +1355,24 @@
     goto out;
 
   if(quiche_conn_is_established(ctx->qconn)) {
+    ctx->handshake_at = ctx->q.last_op;
     CURL_TRC_CF(data, cf, "handshake complete after %dms",
-                (int)Curl_timediff(now, ctx->started_at));
-    ctx->handshake_at = now;
-    result = cf_verify_peer(cf, data);
+                (int)Curl_timediff(ctx->handshake_at, ctx->started_at));
+    result = cf_quiche_verify_peer(cf, data);
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
+      ctx->h3config = quiche_h3_config_new();
+      if(!ctx->h3config) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+
+      /* Create a new HTTP/3 connection on the QUIC connection. */
+      ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
+      if(!ctx->h3c) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
       cf->connected = TRUE;
       cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
@@ -1506,27 +1382,9 @@
   else if(quiche_conn_is_draining(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
-     * state.
-     * This may be a stopping of the service or it may be that the server
-     * is reloading and a new instance will start serving soon.
-     * In any case, we tear down our socket and start over with a new one.
-     * We re-open the underlying UDP cf right now, but do not start
-     * connecting until called again.
-     */
-    int reconn_delay_ms = 200;
-
-    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                reconn_delay_ms);
-    Curl_conn_cf_close(cf->next, data);
-    cf_quiche_ctx_clear(ctx);
-    result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
-    if(!result && *done) {
-      *done = FALSE;
-      ctx->reconnect_at = Curl_now();
-      ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
-      Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
-      result = CURLE_OK;
-    }
+     * state. The CONNECT may work in the near future again. Indicate
+     * that as a "weird" reply. */
+    result = CURLE_WEIRD_SERVER_REPLY;
   }
 
 out:
@@ -1550,6 +1408,7 @@
 
   if(ctx) {
     if(ctx->qconn) {
+      vquic_ctx_update_time(&ctx->q);
       (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
       /* flushing the egress is not a failsafe way to deliver all the
          outstanding packets, but we also don't want to get stuck here... */
@@ -1586,8 +1445,8 @@
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
-    if(ctx->got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+    if(ctx->q.got_first_byte) {
+      timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
     }
     else
@@ -1595,8 +1454,8 @@
     return CURLE_OK;
   case CF_QUERY_TIMER_CONNECT: {
     struct curltime *when = pres2;
-    if(ctx->got_first_byte)
-      *when = ctx->first_byte_at;
+    if(ctx->q.got_first_byte)
+      *when = ctx->q.first_byte_at;
     return CURLE_OK;
   }
   case CF_QUERY_TIMER_APPCONNECT: {
@@ -1617,9 +1476,32 @@
                                     struct Curl_easy *data,
                                     bool *input_pending)
 {
+  struct cf_quiche_ctx *ctx = cf->ctx;
   bool alive = TRUE;
 
   *input_pending = FALSE;
+  if(!ctx->qconn)
+    return FALSE;
+
+  /* Both sides of the QUIC connection announce they max idle times in
+   * the transport parameters. Look at the minimum of both and if
+   * we exceed this, regard the connection as dead. The other side
+   * may have completely purged it and will no longer respond
+   * to any packets from us. */
+  {
+    quiche_transport_params qpeerparams;
+    timediff_t idletime;
+    uint64_t idle_ms = ctx->max_idle_ms;
+
+    if(quiche_conn_peer_transport_params(ctx->qconn, &qpeerparams) &&
+       qpeerparams.peer_max_idle_timeout &&
+       qpeerparams.peer_max_idle_timeout < idle_ms)
+      idle_ms = qpeerparams.peer_max_idle_timeout;
+    idletime = Curl_timediff(Curl_now(), cf->conn->lastused);
+    if(idletime > 0 && (uint64_t)idletime > idle_ms)
+      return FALSE;
+  }
+
   if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
     return FALSE;
 
@@ -1646,7 +1528,7 @@
   cf_quiche_connect,
   cf_quiche_close,
   Curl_cf_def_get_host,
-  cf_quiche_get_select_socks,
+  cf_quiche_adjust_pollset,
   cf_quiche_data_pending,
   cf_quiche_send,
   cf_quiche_recv,
@@ -1667,7 +1549,7 @@
 
   (void)data;
   (void)conn;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c
new file mode 100644
index 0000000..cc7794e
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -0,0 +1,609 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(ENABLE_QUIC) && \
+  (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#include "vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+#include <nettle/sha2.h>
+#include "vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#include "vtls/wolfssl.h"
+#endif
+
+#include "urldata.h"
+#include "curl_trc.h"
+#include "cfilters.h"
+#include "multiif.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "vquic-tls.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#ifdef USE_OPENSSL
+#define QUIC_CIPHERS                                                          \
+  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
+  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+#elif defined(USE_GNUTLS)
+#define QUIC_PRIORITY \
+  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+  "%DISABLE_TLS13_COMPAT_MODE"
+#elif defined(USE_WOLFSSL)
+#define QUIC_CIPHERS                                                          \
+  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
+  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+#endif
+
+
+#ifdef USE_OPENSSL
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+  (void)ssl;
+  Curl_tls_keylog_write_line(line);
+}
+
+static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx,
+                                   struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   Curl_vquic_tls_ctx_setup *ctx_setup)
+{
+  struct ssl_primary_config *conn_config;
+  CURLcode result = CURLE_FAILED_INIT;
+
+  DEBUGASSERT(!ctx->ssl_ctx);
+#ifdef USE_OPENSSL_QUIC
+  ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
+#else
+  ctx->ssl_ctx = SSL_CTX_new(TLS_method());
+#endif
+  if(!ctx->ssl_ctx) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config) {
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  if(ctx_setup) {
+    result = ctx_setup(ctx, cf, data);
+    if(result)
+      goto out;
+  }
+
+  SSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+
+  {
+    const char *curves = conn_config->curves ?
+      conn_config->curves : QUIC_GROUPS;
+    if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) {
+      failf(data, "failed setting curves list for QUIC: '%s'", curves);
+      return CURLE_SSL_CIPHER;
+    }
+  }
+
+#ifndef OPENSSL_IS_BORINGSSL
+  {
+    const char *ciphers13 = conn_config->cipher_list13 ?
+      conn_config->cipher_list13 : QUIC_CIPHERS;
+    if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) {
+      failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
+      return CURLE_SSL_CIPHER;
+    }
+    infof(data, "QUIC cipher selection: %s", ciphers13);
+  }
+#endif
+
+  /* Open the file if a TLS or QUIC backend has not done this before. */
+  Curl_tls_keylog_open();
+  if(Curl_tls_keylog_enabled()) {
+    SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
+  }
+
+  /* OpenSSL always tries to verify the peer, this only says whether it should
+   * fail to connect if the verification fails, or if it should continue
+   * anyway. In the latter case the result of the verification is checked with
+   * SSL_get_verify_result() below. */
+  SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ?
+                     SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+  /* give application a chance to interfere with SSL set up. */
+  if(data->set.ssl.fsslctx) {
+    /* When a user callback is installed to modify the SSL_CTX,
+     * we need to do the full initialization before calling it.
+     * See: #11800 */
+    if(!ctx->x509_store_setup) {
+      result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
+      if(result)
+        goto out;
+      ctx->x509_store_setup = TRUE;
+    }
+    Curl_set_in_callback(data, true);
+    result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
+                                      data->set.ssl.fsslctxp);
+    Curl_set_in_callback(data, false);
+    if(result) {
+      failf(data, "error signaled by ssl ctx callback");
+      goto out;
+    }
+  }
+  result = CURLE_OK;
+
+out:
+  if(result && ctx->ssl_ctx) {
+    SSL_CTX_free(ctx->ssl_ctx);
+    ctx->ssl_ctx = NULL;
+  }
+  return result;
+}
+
+static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx,
+                                     struct Curl_cfilter *cf,
+                                     struct Curl_easy *data)
+{
+  SSL_CTX *ssl_ctx = ctx->ssl_ctx;
+  const struct ssl_config_data *ssl_config;
+
+  ssl_config = Curl_ssl_cf_get_config(cf, data);
+  DEBUGASSERT(ssl_config);
+
+  if(ssl_config->primary.clientcert ||
+     ssl_config->primary.cert_blob ||
+     ssl_config->cert_type) {
+    return Curl_ossl_set_client_cert(
+        data, ssl_ctx, ssl_config->primary.clientcert,
+        ssl_config->primary.cert_blob, ssl_config->cert_type,
+        ssl_config->key, ssl_config->key_blob,
+        ssl_config->key_type, ssl_config->key_passwd);
+  }
+
+  return CURLE_OK;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx,
+                                   struct Curl_easy *data,
+                                   struct ssl_peer *peer,
+                                   const char *alpn, size_t alpn_len,
+                                   void *user_data)
+{
+  DEBUGASSERT(!ctx->ssl);
+  ctx->ssl = SSL_new(ctx->ssl_ctx);
+
+  SSL_set_app_data(ctx->ssl, user_data);
+  SSL_set_connect_state(ctx->ssl);
+#ifndef USE_OPENSSL_QUIC
+  SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+#endif
+
+  if(alpn)
+    SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len);
+
+  if(peer->sni) {
+    if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) {
+      failf(data, "Failed set SNI");
+      SSL_free(ctx->ssl);
+      ctx->ssl = NULL;
+      return CURLE_QUIC_CONNECT_ERROR;
+    }
+  }
+  return CURLE_OK;
+}
+
+#elif defined(USE_GNUTLS)
+static int keylog_callback(gnutls_session_t session, const char *label,
+                    const gnutls_datum_t *secret)
+{
+  gnutls_datum_t crandom;
+  gnutls_datum_t srandom;
+
+  gnutls_session_get_random(session, &crandom, &srandom);
+  if(crandom.size != 32) {
+    return -1;
+  }
+
+  Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+  return 0;
+}
+
+static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx,
+                                   struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct ssl_peer *peer,
+                                   const char *alpn, size_t alpn_len,
+                                   Curl_vquic_tls_ctx_setup *ctx_setup,
+                                   void *user_data)
+{
+  struct ssl_primary_config *conn_config;
+  CURLcode result;
+  gnutls_datum_t alpns[5];
+  /* this will need some attention when HTTPS proxy over QUIC get fixed */
+  long * const pverifyresult = &data->set.ssl.certverifyresult;
+  int rc;
+
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config)
+    return CURLE_FAILED_INIT;
+
+  DEBUGASSERT(ctx->gtls == NULL);
+  ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
+  if(!ctx->gtls)
+    return CURLE_OUT_OF_MEMORY;
+
+  result = gtls_client_init(data, conn_config, &data->set.ssl,
+                            peer, ctx->gtls, pverifyresult);
+  if(result)
+    return result;
+
+  gnutls_session_set_ptr(ctx->gtls->session, user_data);
+
+  if(ctx_setup) {
+    result = ctx_setup(ctx, cf, data);
+    if(result)
+      return result;
+  }
+
+  rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
+  if(rc < 0) {
+    CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+                gnutls_strerror(rc));
+    return CURLE_QUIC_CONNECT_ERROR;
+  }
+
+  /* Open the file if a TLS or QUIC backend has not done this before. */
+  Curl_tls_keylog_open();
+  if(Curl_tls_keylog_enabled()) {
+    gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
+  }
+
+  /* convert the ALPN string from our arguments to a list of strings
+   * that gnutls wants and will convert internally back to this very
+   * string for sending to the server. nice. */
+  if(alpn) {
+    size_t i, alen = alpn_len;
+    unsigned char *s = (unsigned char *)alpn;
+    unsigned char slen;
+    for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
+      slen = s[0];
+      if(slen >= alen)
+        return CURLE_FAILED_INIT;
+      alpns[i].data = s + 1;
+      alpns[i].size = slen;
+      s += slen + 1;
+      alen -= (size_t)slen + 1;
+    }
+    if(alen) /* not all alpn chars used, wrong format or too many */
+        return CURLE_FAILED_INIT;
+    if(i) {
+      gnutls_alpn_set_protocols(ctx->gtls->session,
+                                alpns, (unsigned int)i,
+                                GNUTLS_ALPN_MANDATORY);
+    }
+  }
+
+  return CURLE_OK;
+}
+#elif defined(USE_WOLFSSL)
+
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
+{
+  (void)ssl;
+  Curl_tls_keylog_write_line(line);
+}
+#endif
+
+static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
+                                   struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   Curl_vquic_tls_ctx_setup *ctx_setup)
+{
+  struct ssl_primary_config *conn_config;
+  CURLcode result = CURLE_FAILED_INIT;
+
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config) {
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+  if(!ctx->ssl_ctx) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  if(ctx_setup) {
+    result = ctx_setup(ctx, cf, data);
+    if(result)
+      goto out;
+  }
+
+  wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+
+  if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ?
+                                 conn_config->cipher_list13 :
+                                 QUIC_CIPHERS) != 1) {
+    char error_buffer[256];
+    ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+    failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
+    goto out;
+  }
+
+  if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ?
+                                  conn_config->curves :
+                                  (char *)QUIC_GROUPS) != 1) {
+    failf(data, "wolfSSL failed to set curves");
+    goto out;
+  }
+
+  /* Open the file if a TLS or QUIC backend has not done this before. */
+  Curl_tls_keylog_open();
+  if(Curl_tls_keylog_enabled()) {
+#if defined(HAVE_SECRET_CALLBACK)
+    wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
+#else
+    failf(data, "wolfSSL was built without keylog callback");
+    goto out;
+#endif
+  }
+
+  if(conn_config->verifypeer) {
+    const char * const ssl_cafile = conn_config->CAfile;
+    const char * const ssl_capath = conn_config->CApath;
+
+    wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL);
+    if(ssl_cafile || ssl_capath) {
+      /* tell wolfSSL where to find CA certificates that are used to verify
+         the server's certificate. */
+      int rc =
+        wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile,
+                                             ssl_capath,
+                                             WOLFSSL_LOAD_FLAG_IGNORE_ERR);
+      if(SSL_SUCCESS != rc) {
+        /* Fail if we insist on successfully verifying the server. */
+        failf(data, "error setting certificate verify locations:"
+              "  CAfile: %s CApath: %s",
+              ssl_cafile ? ssl_cafile : "none",
+              ssl_capath ? ssl_capath : "none");
+        goto out;
+      }
+      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+    }
+#ifdef CURL_CA_FALLBACK
+    else {
+      /* verifying the peer without any CA certificates won't work so
+         use wolfssl's built-in default as fallback */
+      wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
+    }
+#endif
+  }
+  else {
+    wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL);
+  }
+
+  /* give application a chance to interfere with SSL set up. */
+  if(data->set.ssl.fsslctx) {
+    Curl_set_in_callback(data, true);
+    result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
+                                      data->set.ssl.fsslctxp);
+    Curl_set_in_callback(data, false);
+    if(result) {
+      failf(data, "error signaled by ssl ctx callback");
+      goto out;
+    }
+  }
+  result = CURLE_OK;
+
+out:
+  if(result && ctx->ssl_ctx) {
+    SSL_CTX_free(ctx->ssl_ctx);
+    ctx->ssl_ctx = NULL;
+  }
+  return result;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
+                                   struct Curl_easy *data,
+                                   struct ssl_peer *peer,
+                                   const char *alpn, size_t alpn_len,
+                                   void *user_data)
+{
+  (void)data;
+  DEBUGASSERT(!ctx->ssl);
+  DEBUGASSERT(ctx->ssl_ctx);
+  ctx->ssl = wolfSSL_new(ctx->ssl_ctx);
+
+  wolfSSL_set_app_data(ctx->ssl, user_data);
+  wolfSSL_set_connect_state(ctx->ssl);
+  wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+  if(alpn)
+    wolfSSL_set_alpn_protos(ctx->ssl, (const unsigned char *)alpn,
+                            (int)alpn_len);
+
+  if(peer->sni) {
+    wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
+                   peer->sni, (unsigned short)strlen(peer->sni));
+  }
+
+  return CURLE_OK;
+}
+#endif /* defined(USE_WOLFSSL) */
+
+CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+                             struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct ssl_peer *peer,
+                             const char *alpn, size_t alpn_len,
+                             Curl_vquic_tls_ctx_setup *ctx_setup,
+                             void *user_data)
+{
+  CURLcode result;
+
+#ifdef USE_OPENSSL
+  result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup);
+  if(result)
+    return result;
+
+  result = curl_ossl_set_client_cert(ctx, cf, data);
+  if(result)
+    return result;
+
+  return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+#elif defined(USE_GNUTLS)
+  (void)result;
+  return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len,
+                            ctx_setup, user_data);
+#elif defined(USE_WOLFSSL)
+  result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup);
+  if(result)
+    return result;
+
+  return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+#else
+#error "no TLS lib in used, should not happen"
+  return CURLE_FAILED_INIT;
+#endif
+}
+
+void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
+{
+#ifdef USE_OPENSSL
+  if(ctx->ssl)
+    SSL_free(ctx->ssl);
+  if(ctx->ssl_ctx)
+    SSL_CTX_free(ctx->ssl_ctx);
+#elif defined(USE_GNUTLS)
+  if(ctx->gtls) {
+    if(ctx->gtls->cred)
+      gnutls_certificate_free_credentials(ctx->gtls->cred);
+    if(ctx->gtls->session)
+      gnutls_deinit(ctx->gtls->session);
+    free(ctx->gtls);
+  }
+#elif defined(USE_WOLFSSL)
+  if(ctx->ssl)
+    wolfSSL_free(ctx->ssl);
+  if(ctx->ssl_ctx)
+    wolfSSL_CTX_free(ctx->ssl_ctx);
+#endif
+  memset(ctx, 0, sizeof(*ctx));
+}
+
+CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL
+  if(!ctx->x509_store_setup) {
+    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
+    if(result)
+      return result;
+    ctx->x509_store_setup = TRUE;
+  }
+#else
+  (void)ctx; (void)cf; (void)data;
+#endif
+  return CURLE_OK;
+}
+
+CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct ssl_peer *peer)
+{
+  struct ssl_primary_config *conn_config;
+  CURLcode result = CURLE_OK;
+
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config)
+    return CURLE_FAILED_INIT;
+
+  if(conn_config->verifyhost) {
+#ifdef USE_OPENSSL
+    X509 *server_cert;
+    server_cert = SSL_get1_peer_certificate(ctx->ssl);
+    if(!server_cert) {
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert);
+    X509_free(server_cert);
+    if(result)
+      return result;
+#elif defined(USE_GNUTLS)
+    result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+                                    conn_config, &data->set.ssl, peer,
+                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+    if(result)
+      return result;
+#elif defined(USE_WOLFSSL)
+    if(!peer->sni ||
+       wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE)
+      return CURLE_PEER_FAILED_VERIFICATION;
+#endif
+    infof(data, "Verified certificate just fine");
+  }
+  else
+    infof(data, "Skipped certificate verification");
+#ifdef USE_OPENSSL
+  if(data->set.ssl.certinfo)
+    /* asked to gather certificate info */
+    (void)Curl_ossl_certchain(data, ctx->ssl);
+#endif
+  return result;
+}
+
+
+#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h
new file mode 100644
index 0000000..9c0dfd8
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h
@@ -0,0 +1,98 @@
+#ifndef HEADER_CURL_VQUIC_TLS_H
+#define HEADER_CURL_VQUIC_TLS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "bufq.h"
+
+#if defined(ENABLE_QUIC) && \
+  (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+
+struct quic_tls_ctx {
+#ifdef USE_OPENSSL
+  SSL_CTX *ssl_ctx;
+  SSL *ssl;
+#elif defined(USE_GNUTLS)
+  struct gtls_instance *gtls;
+#elif defined(USE_WOLFSSL)
+  WOLFSSL_CTX *ssl_ctx;
+  WOLFSSL *ssl;
+#endif
+  BIT(x509_store_setup);             /* if x509 store has been set up */
+};
+
+/**
+ * Callback passed to `Curl_vquic_tls_init()` that can
+ * do early initializations on the not otherwise configured TLS
+ * instances created. This varies by TLS backend:
+ * - openssl/wolfssl: SSL_CTX* has just been created
+ * - gnutls: gtls_client_init() has run
+ */
+typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx,
+                                          struct Curl_cfilter *cf,
+                                          struct Curl_easy *data);
+
+/**
+ * Initialize the QUIC TLS instances based of the SSL configurations
+ * for the connection filter, transfer and peer.
+ * @param ctx         the TLS context to initialize
+ * @param cf          the connection filter involved
+ * @param data        the transfer involved
+ * @param peer        the peer that will be connected to
+ * @param alpn        the ALPN string in protocol format ((len+bytes+)+),
+ *                    may be NULL
+ * @param alpn_len    the overall number of bytes in `alpn`
+ * @param ctx_setup   optional callback for very early TLS config
+ * @param user_data   optional pointer to set in TLS application context
+ */
+CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+                             struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct ssl_peer *peer,
+                             const char *alpn, size_t alpn_len,
+                             Curl_vquic_tls_ctx_setup *ctx_setup,
+                             void *user_data);
+
+/**
+ * Cleanup all data that has been initialized.
+ */
+void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx);
+
+CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data);
+
+/**
+ * After the QUIC basic handshake has been, verify that the peer
+ * (and its certificate) fulfill our requirements.
+ */
+CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct ssl_peer *peer);
+
+#endif /* !ENABLE_QUIC && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
+
+#endif /* HEADER_CURL_VQUIC_TLS_H */
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index 9a1a1bb..612d25b 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -46,6 +46,7 @@
 #include "curl_trc.h"
 #include "curl_msh3.h"
 #include "curl_ngtcp2.h"
+#include "curl_osslq.h"
 #include "curl_quiche.h"
 #include "rand.h"
 #include "vquic.h"
@@ -74,6 +75,8 @@
 {
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
   Curl_ngtcp2_ver(p, len);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+  Curl_osslq_ver(p, len);
 #elif defined(USE_QUICHE)
   Curl_quiche_ver(p, len);
 #elif defined(USE_MSH3)
@@ -100,6 +103,7 @@
     }
   }
 #endif
+  vquic_ctx_update_time(qctx);
 
   return CURLE_OK;
 }
@@ -109,6 +113,11 @@
   Curl_bufq_free(&qctx->sendbuf);
 }
 
+void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
+{
+  qctx->last_op = Curl_now();
+}
+
 static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
                                    struct cf_quic_ctx *qctx,
@@ -173,7 +182,7 @@
         qctx->no_gso = TRUE;
         return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
       }
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     default:
       failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
       return CURLE_SEND_ERROR;
@@ -242,6 +251,7 @@
                                    const uint8_t *pkt, size_t pktlen,
                                    size_t gsolen, size_t *psent)
 {
+  CURLcode result;
 #ifdef DEBUGBUILD
   /* simulate network blocking/partial writes */
   if(qctx->wblock_percent > 0) {
@@ -254,10 +264,14 @@
   }
 #endif
   if(qctx->no_gso && pktlen > gsolen) {
-    return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
+    result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
   }
-
-  return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
+  else {
+    result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
+  }
+  if(!result)
+    qctx->last_io = qctx->last_op;
+  return result;
 }
 
 CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -524,13 +538,22 @@
                             size_t max_pkts,
                             vquic_recv_pkt_cb *recv_cb, void *userp)
 {
+  CURLcode result;
 #if defined(HAVE_SENDMMSG)
-  return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
+  result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
 #elif defined(HAVE_SENDMSG)
-  return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
+  result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
 #else
-  return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
+  result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
 #endif
+  if(!result) {
+    if(!qctx->got_first_byte) {
+      qctx->got_first_byte = TRUE;
+      qctx->first_byte_at = qctx->last_op;
+    }
+    qctx->last_io = qctx->last_op;
+  }
+  return result;
 }
 
 /*
@@ -588,6 +611,8 @@
   DEBUGASSERT(transport == TRNSPRT_QUIC);
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
   return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+  return Curl_cf_osslq_create(pcf, data, conn, ai);
 #elif defined(USE_QUICHE)
   return Curl_cf_quiche_create(pcf, data, conn, ai);
 #elif defined(USE_MSH3)
@@ -607,6 +632,8 @@
 {
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
   return Curl_conn_is_ngtcp2(data, conn, sockindex);
+#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
+  return Curl_conn_is_osslq(data, conn, sockindex);
 #elif defined(USE_QUICHE)
   return Curl_conn_is_quiche(data, conn, sockindex);
 #elif defined(USE_MSH3)
diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h
index dbcd009..c218a94 100644
--- a/Utilities/cmcurl/lib/vquic/vquic_int.h
+++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -31,6 +31,8 @@
 
 #define MAX_PKT_BURST 10
 #define MAX_UDP_PAYLOAD_SIZE  1452
+/* Default QUIC connection timeout we announce from our side */
+#define CURL_QUIC_MAX_IDLE_MS   (120 * 1000)
 
 struct cf_quic_ctx {
   curl_socket_t sockfd; /* connected UDP socket */
@@ -38,18 +40,24 @@
   socklen_t local_addrlen; /* length of local address */
 
   struct bufq sendbuf; /* buffer for sending one or more packets */
+  struct curltime first_byte_at;     /* when first byte was recvd */
+  struct curltime last_op; /* last (attempted) send/recv operation */
+  struct curltime last_io; /* last successful socket IO */
   size_t gsolen; /* length of individual packets in send buf */
   size_t split_len; /* if != 0, buffer length after which GSO differs */
   size_t split_gsolen; /* length of individual packets after split_len */
 #ifdef DEBUGBUILD
   int wblock_percent; /* percent of writes doing EAGAIN */
 #endif
-  bool no_gso; /* do not use gso on sending */
+  BIT(got_first_byte); /* if first byte was received */
+  BIT(no_gso); /* do not use gso on sending */
 };
 
 CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
 void vquic_ctx_free(struct cf_quic_ctx *qctx);
 
+void vquic_ctx_update_time(struct cf_quic_ctx *qctx);
+
 void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
                             struct cf_quic_ctx *qctx,
                             const uint8_t *pkt, size_t pktlen, size_t gsolen);
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index b0f49d6..c6dc63a 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -31,6 +31,8 @@
 
 #include <limits.h>
 
+/* in 0.10.0 or later, ignore deprecated warnings */
+#define SSH_SUPPRESS_DEPRECATED
 #include <libssh/libssh.h>
 #include <libssh/sftp.h>
 
@@ -89,13 +91,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* in 0.10.0 or later, ignore deprecated warnings */
-#if defined(__GNUC__) &&                        \
-  (LIBSSH_VERSION_MINOR >= 10) ||               \
-  (LIBSSH_VERSION_MAJOR > 0)
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#endif
-
 /* A recent macro provided by libssh. Or make our own. */
 #ifndef SSH_STRING_FREE_CHAR
 #define SSH_STRING_FREE_CHAR(x)                 \
@@ -166,7 +161,7 @@
   ZERO_NULL,                    /* domore_getsock */
   myssh_getsock,                /* perform_getsock */
   scp_disconnect,               /* disconnect */
-  ZERO_NULL,                    /* readwrite */
+  ZERO_NULL,                    /* write_resp */
   ZERO_NULL,                    /* connection_check */
   ZERO_NULL,                    /* attach connection */
   PORT_SSH,                     /* defport */
@@ -193,7 +188,7 @@
   ZERO_NULL,                            /* domore_getsock */
   myssh_getsock,                        /* perform_getsock */
   sftp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
@@ -444,11 +439,8 @@
       keymatch = CURLKHMATCH_OK;
       break;
     case SSH_KNOWN_HOSTS_OTHER:
-      /* fallthrough */
     case SSH_KNOWN_HOSTS_NOT_FOUND:
-      /* fallthrough */
     case SSH_KNOWN_HOSTS_UNKNOWN:
-      /* fallthrough */
     case SSH_KNOWN_HOSTS_ERROR:
       keymatch = CURLKHMATCH_MISSING;
       break;
@@ -464,7 +456,6 @@
       keymatch = CURLKHMATCH_OK;
       break;
     case SSH_SERVER_FILE_NOT_FOUND:
-      /* fallthrough */
     case SSH_SERVER_NOT_KNOWN:
       keymatch = CURLKHMATCH_MISSING;
       break;
@@ -628,7 +619,7 @@
       if(rc < 0)
         return SSH_ERROR;
 
-    /* FALLTHROUGH */
+      FALLTHROUGH();
     case 1:
       sshc->kbd_state = 1;
 
@@ -703,7 +694,7 @@
       ssh_set_blocking(sshc->ssh_session, 0);
 
       state(data, SSH_S_STARTUP);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case SSH_S_STARTUP:
       rc = ssh_connect(sshc->ssh_session);
@@ -718,7 +709,7 @@
 
       state(data, SSH_HOSTKEY);
 
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_HOSTKEY:
 
       rc = myssh_is_known(data);
@@ -728,7 +719,7 @@
       }
 
       state(data, SSH_AUTHLIST);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_AUTHLIST:{
         sshc->authed = FALSE;
 
@@ -909,7 +900,7 @@
         break;
       }
       state(data, SSH_AUTH_PASS);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case SSH_AUTH_PASS:
       rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
@@ -972,7 +963,7 @@
         break;
       }
       state(data, SSH_SFTP_REALPATH);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_SFTP_REALPATH:
       /*
        * Get the "home" directory
@@ -1159,13 +1150,23 @@
         break;
       }
       else if(statvfs) {
+        #ifdef _MSC_VER
+        #define CURL_LIBSSH_VFS_SIZE_MASK "I64u"
+        #else
+        #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64
+        #endif
         char *tmp = aprintf("statvfs:\n"
-                            "f_bsize: %llu\n" "f_frsize: %llu\n"
-                            "f_blocks: %llu\n" "f_bfree: %llu\n"
-                            "f_bavail: %llu\n" "f_files: %llu\n"
-                            "f_ffree: %llu\n" "f_favail: %llu\n"
-                            "f_fsid: %llu\n" "f_flag: %llu\n"
-                            "f_namemax: %llu\n",
+                            "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
+                            "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n",
                             statvfs->f_bsize, statvfs->f_frsize,
                             statvfs->f_blocks, statvfs->f_bfree,
                             statvfs->f_bavail, statvfs->f_files,
@@ -1307,13 +1308,14 @@
           }
           /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
           do {
+            char scratch[4*1024];
             size_t readthisamountnow =
-              (data->state.resume_from - passed > data->set.buffer_size) ?
-              (size_t)data->set.buffer_size :
-              curlx_sotouz(data->state.resume_from - passed);
+              (data->state.resume_from - passed >
+                (curl_off_t)sizeof(scratch)) ?
+              sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
 
             size_t actuallyread =
-              data->state.fread_func(data->state.buffer, 1,
+              data->state.fread_func(scratch, 1,
                                      readthisamountnow, data->state.in);
 
             passed += actuallyread;
@@ -1359,7 +1361,7 @@
       /* we want to use the _sending_ function even when the socket turns
          out readable as the underlying libssh sftp send function will deal
          with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_OUT;
+      data->state.select_bits = CURL_CSELECT_OUT;
 
       /* since we don't really wait for anything at this point, we want the
          state machine to move on as soon as possible so we set a very short
@@ -1466,13 +1468,7 @@
             state(data, SSH_STOP);
             break;
           }
-          /* since this counts what we send to the client, we include the
-             newline in this counter */
-          data->req.bytecount += sshc->readdir_len + 1;
 
-          /* output debug output if that is requested */
-          Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename,
-                     sshc->readdir_len);
         }
         else {
           if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
@@ -1555,7 +1551,7 @@
       sshc->readdir_longentry = NULL;
 
       state(data, SSH_SFTP_READDIR_BOTTOM);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_SFTP_READDIR_BOTTOM:
       if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
         result = CURLE_OUT_OF_MEMORY;
@@ -1564,12 +1560,6 @@
                                    Curl_dyn_ptr(&sshc->readdir_buf),
                                    Curl_dyn_len(&sshc->readdir_buf));
 
-      if(!result) {
-        /* output debug output if that is requested */
-        Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf),
-                   Curl_dyn_len(&sshc->readdir_buf));
-        data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf);
-      }
       ssh_string_free_char(sshc->readdir_tmp);
       sshc->readdir_tmp = NULL;
 
@@ -1741,7 +1731,7 @@
     /* we want to use the _receiving_ function even when the socket turns
        out writableable as the underlying libssh recv function will deal
        with both accordingly */
-    conn->cselect_bits = CURL_CSELECT_IN;
+    data->state.select_bits = CURL_CSELECT_IN;
 
     if(result) {
       /* this should never occur; the close state should be entered
@@ -1869,7 +1859,7 @@
       /* we want to use the _sending_ function even when the socket turns
          out readable as the underlying libssh scp send function will deal
          with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_OUT;
+      data->state.select_bits = CURL_CSELECT_OUT;
 
       state(data, SSH_STOP);
 
@@ -1885,7 +1875,7 @@
         break;
       }
       state(data, SSH_SCP_DOWNLOAD);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case SSH_SCP_DOWNLOAD:{
         curl_off_t bytecount;
@@ -1909,7 +1899,7 @@
         /* we want to use the _receiving_ function even when the socket turns
            out writableable as the underlying libssh recv function will deal
            with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_IN;
+        data->state.select_bits = CURL_CSELECT_IN;
 
         state(data, SSH_STOP);
         break;
@@ -1949,7 +1939,7 @@
       ssh_set_blocking(sshc->ssh_session, 0);
 
       state(data, SSH_SESSION_DISCONNECT);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case SSH_SESSION_DISCONNECT:
       /* during weird times when we've been prematurely aborted, the channel
@@ -1963,17 +1953,16 @@
       ssh_disconnect(sshc->ssh_session);
       if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
         /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
-           explicitly mark it as closed with the memdebug macro. This libssh
+           tell the connection to forget about it. This libssh
            bug is fixed in 0.10.0. */
-        fake_sclose(conn->sock[FIRSTSOCKET]);
-        conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
+        Curl_conn_forget_socket(data, FIRSTSOCKET);
       }
 
       SSH_STRING_FREE_CHAR(sshc->homedir);
       data->state.most_recent_ftp_entrypath = NULL;
 
       state(data, SSH_SESSION_FREE);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_SESSION_FREE:
       if(sshc->ssh_session) {
         ssh_free(sshc->ssh_session);
@@ -2024,7 +2013,6 @@
       break;
 
     case SSH_QUIT:
-      /* fallthrough, just stop! */
     default:
       /* internal error */
       sshc->nextstate = SSH_NO_STATE;
@@ -2615,7 +2603,7 @@
         return -1;
       }
 
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case 1:
       conn->proto.sshc.sftp_recv_state = 1;
 
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index f539b39..e9dfef9 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -138,7 +138,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ssh_getsock,                          /* perform_getsock */
   scp_disconnect,                       /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ssh_attach,                           /* attach */
   PORT_SSH,                             /* defport */
@@ -167,7 +167,7 @@
   ZERO_NULL,                            /* domore_getsock */
   ssh_getsock,                          /* perform_getsock */
   sftp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ssh_attach,                           /* attach */
   PORT_SSH,                             /* defport */
@@ -589,10 +589,9 @@
 
     switch(rc) {
     default: /* unknown return codes will equal reject */
-      /* FALLTHROUGH */
     case CURLKHSTAT_REJECT:
       state(data, SSH_SESSION_FREE);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case CURLKHSTAT_DEFER:
       /* DEFER means bail out but keep the SSH_HOSTKEY state */
       result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
@@ -601,9 +600,8 @@
       /* remove old host+key that doesn't match */
       if(host)
         libssh2_knownhost_del(sshc->kh, host);
-        /* FALLTHROUGH */
+      FALLTHROUGH();
     case CURLKHSTAT_FINE:
-        /* FALLTHROUGH */
     case CURLKHSTAT_FINE_ADD_TO_FILE:
       /* proceed */
       if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
@@ -997,7 +995,7 @@
       }
 
       state(data, SSH_S_STARTUP);
-      /* FALLTHROUGH */
+      FALLTHROUGH();
 
     case SSH_S_STARTUP:
       rc = session_startup(sshc->ssh_session, sock);
@@ -1016,7 +1014,7 @@
 
       state(data, SSH_HOSTKEY);
 
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSH_HOSTKEY:
       /*
        * Before we authenticate we should check the hostkey's fingerprint
@@ -1537,139 +1535,137 @@
           state(data, SSH_SFTP_NEXT_QUOTE);
         break;
       }
-      {
-        /*
-         * the arguments following the command must be separated from the
-         * command with a space so we can check for it unconditionally
-         */
-        cp = strchr(cmd, ' ');
-        if(!cp) {
-          failf(data, "Syntax error command '%s', missing parameter",
-                cmd);
-          state(data, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
-        }
 
-        /*
-         * also, every command takes at least one argument so we get that
-         * first argument right now
-         */
-        result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
-        if(result) {
-          if(result == CURLE_OUT_OF_MEMORY)
-            failf(data, "Out of memory");
-          else
-            failf(data, "Syntax error: Bad first parameter to '%s'", cmd);
-          state(data, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = result;
-          break;
-        }
-
-        /*
-         * SFTP is a binary protocol, so we don't send text commands
-         * to the server. Instead, we scan for commands used by
-         * OpenSSH's sftp program and call the appropriate libssh2
-         * functions.
-         */
-        if(strncasecompare(cmd, "chgrp ", 6) ||
-           strncasecompare(cmd, "chmod ", 6) ||
-           strncasecompare(cmd, "chown ", 6) ||
-           strncasecompare(cmd, "atime ", 6) ||
-           strncasecompare(cmd, "mtime ", 6)) {
-          /* attribute change */
-
-          /* sshc->quote_path1 contains the mode to set */
-          /* get the destination */
-          result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data, "Syntax error in %s: Bad second parameter", cmd);
-            Curl_safefree(sshc->quote_path1);
-            state(data, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-          state(data, SSH_SFTP_QUOTE_STAT);
-          break;
-        }
-        if(strncasecompare(cmd, "ln ", 3) ||
-           strncasecompare(cmd, "symlink ", 8)) {
-          /* symbolic linking */
-          /* sshc->quote_path1 is the source */
-          /* get the destination */
-          result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data,
-                    "Syntax error in ln/symlink: Bad second parameter");
-            Curl_safefree(sshc->quote_path1);
-            state(data, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          state(data, SSH_SFTP_QUOTE_SYMLINK);
-          break;
-        }
-        else if(strncasecompare(cmd, "mkdir ", 6)) {
-          /* create dir */
-          state(data, SSH_SFTP_QUOTE_MKDIR);
-          break;
-        }
-        else if(strncasecompare(cmd, "rename ", 7)) {
-          /* rename file */
-          /* first param is the source path */
-          /* second param is the dest. path */
-          result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
-          if(result) {
-            if(result == CURLE_OUT_OF_MEMORY)
-              failf(data, "Out of memory");
-            else
-              failf(data, "Syntax error in rename: Bad second parameter");
-            Curl_safefree(sshc->quote_path1);
-            state(data, SSH_SFTP_CLOSE);
-            sshc->nextstate = SSH_NO_STATE;
-            sshc->actualcode = result;
-            break;
-          }
-          state(data, SSH_SFTP_QUOTE_RENAME);
-          break;
-        }
-        else if(strncasecompare(cmd, "rmdir ", 6)) {
-          /* delete dir */
-          state(data, SSH_SFTP_QUOTE_RMDIR);
-          break;
-        }
-        else if(strncasecompare(cmd, "rm ", 3)) {
-          state(data, SSH_SFTP_QUOTE_UNLINK);
-          break;
-        }
-#ifdef HAS_STATVFS_SUPPORT
-        else if(strncasecompare(cmd, "statvfs ", 8)) {
-          state(data, SSH_SFTP_QUOTE_STATVFS);
-          break;
-        }
-#endif
-
-        failf(data, "Unknown SFTP command");
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
+      /*
+       * the arguments following the command must be separated from the
+       * command with a space so we can check for it unconditionally
+       */
+      cp = strchr(cmd, ' ');
+      if(!cp) {
+        failf(data, "Syntax error command '%s', missing parameter",
+              cmd);
         state(data, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
+
+      /*
+       * also, every command takes at least one argument so we get that
+       * first argument right now
+       */
+      result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
+      if(result) {
+        if(result == CURLE_OUT_OF_MEMORY)
+          failf(data, "Out of memory");
+        else
+          failf(data, "Syntax error: Bad first parameter to '%s'", cmd);
+        state(data, SSH_SFTP_CLOSE);
+        sshc->nextstate = SSH_NO_STATE;
+        sshc->actualcode = result;
+        break;
+      }
+
+      /*
+       * SFTP is a binary protocol, so we don't send text commands
+       * to the server. Instead, we scan for commands used by
+       * OpenSSH's sftp program and call the appropriate libssh2
+       * functions.
+       */
+      if(strncasecompare(cmd, "chgrp ", 6) ||
+         strncasecompare(cmd, "chmod ", 6) ||
+         strncasecompare(cmd, "chown ", 6) ||
+         strncasecompare(cmd, "atime ", 6) ||
+         strncasecompare(cmd, "mtime ", 6)) {
+        /* attribute change */
+
+        /* sshc->quote_path1 contains the mode to set */
+        /* get the destination */
+        result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+        if(result) {
+          if(result == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data, "Syntax error in %s: Bad second parameter", cmd);
+          Curl_safefree(sshc->quote_path1);
+          state(data, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = result;
+          break;
+        }
+        memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
+        state(data, SSH_SFTP_QUOTE_STAT);
+        break;
+      }
+      if(strncasecompare(cmd, "ln ", 3) ||
+         strncasecompare(cmd, "symlink ", 8)) {
+        /* symbolic linking */
+        /* sshc->quote_path1 is the source */
+        /* get the destination */
+        result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+        if(result) {
+          if(result == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data,
+                  "Syntax error in ln/symlink: Bad second parameter");
+          Curl_safefree(sshc->quote_path1);
+          state(data, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = result;
+          break;
+        }
+        state(data, SSH_SFTP_QUOTE_SYMLINK);
+        break;
+      }
+      else if(strncasecompare(cmd, "mkdir ", 6)) {
+        /* create dir */
+        state(data, SSH_SFTP_QUOTE_MKDIR);
+        break;
+      }
+      else if(strncasecompare(cmd, "rename ", 7)) {
+        /* rename file */
+        /* first param is the source path */
+        /* second param is the dest. path */
+        result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
+        if(result) {
+          if(result == CURLE_OUT_OF_MEMORY)
+            failf(data, "Out of memory");
+          else
+            failf(data, "Syntax error in rename: Bad second parameter");
+          Curl_safefree(sshc->quote_path1);
+          state(data, SSH_SFTP_CLOSE);
+          sshc->nextstate = SSH_NO_STATE;
+          sshc->actualcode = result;
+          break;
+        }
+        state(data, SSH_SFTP_QUOTE_RENAME);
+        break;
+      }
+      else if(strncasecompare(cmd, "rmdir ", 6)) {
+        /* delete dir */
+        state(data, SSH_SFTP_QUOTE_RMDIR);
+        break;
+      }
+      else if(strncasecompare(cmd, "rm ", 3)) {
+        state(data, SSH_SFTP_QUOTE_UNLINK);
+        break;
+      }
+#ifdef HAS_STATVFS_SUPPORT
+      else if(strncasecompare(cmd, "statvfs ", 8)) {
+        state(data, SSH_SFTP_QUOTE_STATVFS);
+        break;
+      }
+#endif
+
+      failf(data, "Unknown SFTP command");
+      Curl_safefree(sshc->quote_path1);
+      Curl_safefree(sshc->quote_path2);
+      state(data, SSH_SFTP_CLOSE);
+      sshc->nextstate = SSH_NO_STATE;
+      sshc->actualcode = CURLE_QUOTE_ERROR;
+      break;
     }
-    break;
 
     case SSH_SFTP_NEXT_QUOTE:
       Curl_safefree(sshc->quote_path1);
@@ -1962,13 +1958,23 @@
         break;
       }
       else if(rc == 0) {
+        #ifdef _MSC_VER
+        #define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
+        #else
+        #define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
+        #endif
         char *tmp = aprintf("statvfs:\n"
-                            "f_bsize: %llu\n" "f_frsize: %llu\n"
-                            "f_blocks: %llu\n" "f_bfree: %llu\n"
-                            "f_bavail: %llu\n" "f_files: %llu\n"
-                            "f_ffree: %llu\n" "f_favail: %llu\n"
-                            "f_fsid: %llu\n" "f_flag: %llu\n"
-                            "f_namemax: %llu\n",
+                            "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                            "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
                             statvfs.f_bsize, statvfs.f_frsize,
                             statvfs.f_blocks, statvfs.f_bfree,
                             statvfs.f_bavail, statvfs.f_files,
@@ -2152,14 +2158,15 @@
           }
           /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
           do {
+            char scratch[4*1024];
             size_t readthisamountnow =
-              (data->state.resume_from - passed > data->set.buffer_size) ?
-              (size_t)data->set.buffer_size :
-              curlx_sotouz(data->state.resume_from - passed);
+              (data->state.resume_from - passed >
+                (curl_off_t)sizeof(scratch)) ?
+              sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
 
             size_t actuallyread;
             Curl_set_in_callback(data, true);
-            actuallyread = data->state.fread_func(data->state.buffer, 1,
+            actuallyread = data->state.fread_func(scratch, 1,
                                                   readthisamountnow,
                                                   data->state.in);
             Curl_set_in_callback(data, false);
@@ -2205,7 +2212,7 @@
         /* we want to use the _sending_ function even when the socket turns
            out readable as the underlying libssh2 sftp send function will deal
            with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_OUT;
+        data->state.select_bits = CURL_CSELECT_OUT;
 
         /* since we don't really wait for anything at this point, we want the
            state machine to move on as soon as possible so we set a very short
@@ -2341,14 +2348,7 @@
             state(data, SSH_STOP);
             break;
           }
-          /* since this counts what we send to the client, we include the
-             newline in this counter */
-          data->req.bytecount += readdir_len + 1;
 
-          /* output debug output if that is requested */
-          Curl_debug(data, CURLINFO_DATA_IN, sshp->readdir_filename,
-                     readdir_len);
-          Curl_debug(data, CURLINFO_DATA_IN, (char *)"\n", 1);
         }
         else {
           result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
@@ -2427,13 +2427,6 @@
                                    Curl_dyn_ptr(&sshp->readdir),
                                    Curl_dyn_len(&sshp->readdir));
 
-      if(!result) {
-        /* output debug output if that is requested */
-        Curl_debug(data, CURLINFO_DATA_IN,
-                   Curl_dyn_ptr(&sshp->readdir),
-                   Curl_dyn_len(&sshp->readdir));
-        data->req.bytecount += Curl_dyn_len(&sshp->readdir);
-      }
       if(result) {
         Curl_dyn_free(&sshp->readdir);
         state(data, SSH_STOP);
@@ -2608,7 +2601,7 @@
     /* we want to use the _receiving_ function even when the socket turns
        out writableable as the underlying libssh2 recv function will deal
        with both accordingly */
-    conn->cselect_bits = CURL_CSELECT_IN;
+    data->state.select_bits = CURL_CSELECT_IN;
 
     if(result) {
       /* this should never occur; the close state should be entered
@@ -2763,7 +2756,7 @@
         /* we want to use the _sending_ function even when the socket turns
            out readable as the underlying libssh2 scp send function will deal
            with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_OUT;
+        data->state.select_bits = CURL_CSELECT_OUT;
 
         state(data, SSH_STOP);
       }
@@ -2825,7 +2818,7 @@
       /* we want to use the _receiving_ function even when the socket turns
          out writableable as the underlying libssh2 recv function will deal
          with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_IN;
+      data->state.select_bits = CURL_CSELECT_IN;
 
       if(result) {
         state(data, SSH_SCP_CHANNEL_FREE);
@@ -3030,7 +3023,6 @@
       break;
 
     case SSH_QUIT:
-      /* fallthrough, just stop! */
     default:
       /* internal error */
       sshc->nextstate = SSH_NO_STATE;
@@ -3299,6 +3291,27 @@
 #ifndef CURL_DISABLE_PROXY
   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
     /*
+      Setup libssh2 callbacks to make it read/write TLS from the socket.
+
+      ssize_t
+      recvcb(libssh2_socket_t sock, void *buffer, size_t length,
+      int flags, void **abstract);
+
+      ssize_t
+      sendcb(libssh2_socket_t sock, const void *buffer, size_t length,
+      int flags, void **abstract);
+
+    */
+#if LIBSSH2_VERSION_NUM >= 0x010b01
+    infof(data, "Uses HTTPS proxy");
+    libssh2_session_callback_set2(sshc->ssh_session,
+                                  LIBSSH2_CALLBACK_RECV,
+                                  (libssh2_cb_generic *)ssh_tls_recv);
+    libssh2_session_callback_set2(sshc->ssh_session,
+                                  LIBSSH2_CALLBACK_SEND,
+                                  (libssh2_cb_generic *)ssh_tls_send);
+#else
+    /*
      * This crazy union dance is here to avoid assigning a void pointer a
      * function pointer as it is invalid C. The problem is of course that
      * libssh2 has such an API...
@@ -3318,22 +3331,11 @@
     sshsend.sendptr = ssh_tls_send;
 
     infof(data, "Uses HTTPS proxy");
-    /*
-      Setup libssh2 callbacks to make it read/write TLS from the socket.
-
-      ssize_t
-      recvcb(libssh2_socket_t sock, void *buffer, size_t length,
-      int flags, void **abstract);
-
-      ssize_t
-      sendcb(libssh2_socket_t sock, const void *buffer, size_t length,
-      int flags, void **abstract);
-
-    */
     libssh2_session_callback_set(sshc->ssh_session,
                                  LIBSSH2_CALLBACK_RECV, sshrecv.recvp);
     libssh2_session_callback_set(sshc->ssh_session,
                                  LIBSSH2_CALLBACK_SEND, sshsend.sendp);
+#endif
 
     /* Store the underlying TLS recv/send function pointers to be used when
        reading from the proxy */
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h
index 1e1b137..ca0533a 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -267,6 +267,7 @@
 /* for non-SSH builds */
 #define Curl_ssh_cleanup()
 #define Curl_ssh_attach(x,y)
+#define Curl_ssh_init() 0
 #endif
 
 #endif /* HEADER_CURL_SSH_H */
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c
index 39cee50..7396791c 100644
--- a/Utilities/cmcurl/lib/vssh/wolfssh.c
+++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -42,6 +42,7 @@
 #include "select.h"
 #include "multiif.h"
 #include "warnless.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -92,7 +93,7 @@
   ZERO_NULL,                            /* domore_getsock */
   wssh_getsock,                         /* perform_getsock */
   wscp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
@@ -121,7 +122,7 @@
   ZERO_NULL,                            /* domore_getsock */
   wssh_getsock,                         /* perform_getsock */
   wsftp_disconnect,                     /* disconnect */
-  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   PORT_SSH,                             /* defport */
@@ -343,9 +344,6 @@
   return CURLE_OK;
 }
 
-static Curl_recv wscp_recv, wsftp_recv;
-static Curl_send wscp_send, wsftp_send;
-
 static int userauth(byte authtype,
                     WS_UserAuthData* authdata,
                     void *ctx)
@@ -515,15 +513,9 @@
         return CURLE_OK;
       }
       else if(name && (rc == WS_SUCCESS)) {
-        sshc->homedir = malloc(name->fSz + 1);
-        if(!sshc->homedir) {
+        sshc->homedir = Curl_memdup0(name->fName, name->fSz);
+        if(!sshc->homedir)
           sshc->actualcode = CURLE_OUT_OF_MEMORY;
-        }
-        else {
-          memcpy(sshc->homedir, name->fName, name->fSz);
-          sshc->homedir[name->fSz] = 0;
-          infof(data, "wolfssh SFTP realpath succeeded");
-        }
         wolfSSH_SFTPNAME_list_free(name);
         state(data, SSH_STOP);
         return CURLE_OK;
@@ -649,14 +641,15 @@
           }
           /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
           do {
+            char scratch[4*1024];
             size_t readthisamountnow =
-              (data->state.resume_from - passed > data->set.buffer_size) ?
-              (size_t)data->set.buffer_size :
-              curlx_sotouz(data->state.resume_from - passed);
+              (data->state.resume_from - passed >
+                (curl_off_t)sizeof(scratch)) ?
+              sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
 
             size_t actuallyread;
             Curl_set_in_callback(data, true);
-            actuallyread = data->state.fread_func(data->state.buffer, 1,
+            actuallyread = data->state.fread_func(scratch, 1,
                                                   readthisamountnow,
                                                   data->state.in);
             Curl_set_in_callback(data, false);
@@ -702,7 +695,7 @@
         /* we want to use the _sending_ function even when the socket turns
            out readable as the underlying libssh2 sftp send function will deal
            with both accordingly */
-        conn->cselect_bits = CURL_CSELECT_OUT;
+        data->state.select_bits = CURL_CSELECT_OUT;
 
         /* since we don't really wait for anything at this point, we want the
            state machine to move on as soon as possible so we set a very short
@@ -798,7 +791,7 @@
       /* we want to use the _receiving_ function even when the socket turns
          out writableable as the underlying libssh2 recv function will deal
          with both accordingly */
-      conn->cselect_bits = CURL_CSELECT_IN;
+      data->state.select_bits = CURL_CSELECT_IN;
 
       if(result) {
         /* this should never occur; the close state should be entered
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c
index 934149c..58394ba 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.c
+++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -509,7 +509,6 @@
 {
   uint16_t selected_ciphers[NUM_OF_CIPHERS];
   size_t selected_count = 0;
-  char cipher_name[CIPHER_NAME_BUF_LEN];
   const char *cipher_start = ciphers;
   const char *cipher_end;
   size_t i, j;
@@ -518,41 +517,48 @@
     return CURLE_SSL_CIPHER;
 
   while(true) {
+    const char *cipher;
+    size_t clen;
+
     /* Extract the next cipher name from the ciphers string */
     while(is_separator(*cipher_start))
       ++cipher_start;
-    if(*cipher_start == '\0')
+    if(!*cipher_start)
       break;
     cipher_end = cipher_start;
-    while(*cipher_end != '\0' && !is_separator(*cipher_end))
+    while(*cipher_end && !is_separator(*cipher_end))
       ++cipher_end;
-    j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ?
-        cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1;
-    strncpy(cipher_name, cipher_start, j);
-    cipher_name[j] = '\0';
+
+    clen = cipher_end - cipher_start;
+    cipher = cipher_start;
+
     cipher_start = cipher_end;
 
     /* Lookup the cipher name in the table of available ciphers. If the cipher
        name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try
        to match cipher name by an (OpenSSL) alias. */
-    if(strncasecompare(cipher_name, "TLS_", 4)) {
+    if(strncasecompare(cipher, "TLS_", 4)) {
       for(i = 0; i < NUM_OF_CIPHERS &&
-                 !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+            (strlen(ciphertable[i].name) == clen) &&
+            !strncasecompare(cipher, ciphertable[i].name, clen); ++i);
     }
     else {
       for(i = 0; i < NUM_OF_CIPHERS &&
-                 !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+            (strlen(ciphertable[i].alias_name) == clen) &&
+            !strncasecompare(cipher, ciphertable[i].alias_name, clen); ++i);
     }
     if(i == NUM_OF_CIPHERS) {
-      infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+      infof(data, "BearSSL: unknown cipher in list: %.*s",
+            (int)clen, cipher);
       continue;
     }
 
     /* No duplicates allowed */
     for(j = 0; j < selected_count &&
-               selected_ciphers[j] != ciphertable[i].num; j++);
+          selected_ciphers[j] != ciphertable[i].num; j++);
     if(j < selected_count) {
-      infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+      infof(data, "BearSSL: duplicate cipher in list: %.*s",
+            (int)clen, cipher);
       continue;
     }
 
@@ -582,17 +588,12 @@
   const char * const ssl_cafile =
     /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
     (ca_info_blob ? NULL : conn_config->CAfile);
-  const char *hostname = connssl->hostname;
+  const char *hostname = connssl->peer.hostname;
   const bool verifypeer = conn_config->verifypeer;
   const bool verifyhost = conn_config->verifyhost;
   CURLcode ret;
   unsigned version_min, version_max;
   int session_set = 0;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
 
   DEBUGASSERT(backend);
   CURL_TRC_CF(data, cf, "connect_step1");
@@ -706,11 +707,7 @@
     infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
   }
 
-  if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
-#ifdef ENABLE_IPV6
-      || (1 == Curl_inet_pton(AF_INET6, hostname, &addr))
-#endif
-     ) {
+  if(connssl->peer.is_ip_address) {
     if(verifyhost) {
       failf(data, "BearSSL: "
             "host verification of IP address is not supported");
@@ -719,12 +716,11 @@
     hostname = NULL;
   }
   else {
-    char *snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost) {
+    if(!connssl->peer.sni) {
       failf(data, "Failed to set SNI");
       return CURLE_SSL_CONNECT_ERROR;
     }
-    hostname = snihost;
+    hostname = connssl->peer.sni;
     CURL_TRC_CF(data, cf, "connect_step1, SNI set");
   }
 
@@ -749,26 +745,26 @@
   return CURLE_OK;
 }
 
-static int bearssl_get_select_socks(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    curl_socket_t *socks)
+static void bearssl_adjust_pollset(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct easy_pollset *ps)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+  if(!cf->connected) {
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+    if(sock != CURL_SOCKET_BAD) {
+      struct ssl_connect_data *connssl = cf->ctx;
+      struct bearssl_ssl_backend_data *backend =
+        (struct bearssl_ssl_backend_data *)connssl->backend;
+      unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
 
-  if(sock == CURL_SOCKET_BAD)
-    return GETSOCK_BLANK;
-  else {
-    struct bearssl_ssl_backend_data *backend =
-      (struct bearssl_ssl_backend_data *)connssl->backend;
-    unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
-    if(state & BR_SSL_SENDREC) {
-      socks[0] = sock;
-      return GETSOCK_WRITESOCK(0);
+      if(state & BR_SSL_SENDREC) {
+        Curl_pollset_set_out_only(data, ps, sock);
+      }
+      else {
+        Curl_pollset_set_in_only(data, ps, sock);
+      }
     }
   }
-  socks[0] = sock;
-  return GETSOCK_READSOCK(0);
 }
 
 static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
@@ -1210,7 +1206,7 @@
   Curl_none_cert_status_request,   /* cert_status_request */
   bearssl_connect,                 /* connect */
   bearssl_connect_nonblocking,     /* connect_nonblocking */
-  bearssl_get_select_socks,        /* getsock */
+  bearssl_adjust_pollset,          /* adjust_pollset */
   bearssl_get_internals,           /* get_internals */
   bearssl_close,                   /* close_one */
   Curl_none_close_all,             /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index c538a96..b95c5be 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -402,18 +402,13 @@
 CURLcode gtls_client_init(struct Curl_easy *data,
                           struct ssl_primary_config *config,
                           struct ssl_config_data *ssl_config,
-                          const char *hostname,
+                          struct ssl_peer *peer,
                           struct gtls_instance *gtls,
                           long *pverifyresult)
 {
   unsigned int init_flags;
   int rc;
   bool sni = TRUE; /* default is SNI enabled */
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
   const char *prioritylist;
   const char *err = NULL;
   const char *tls13support;
@@ -460,50 +455,60 @@
   }
 #endif
 
-  if(config->CAfile) {
-    /* set the trusted CA cert bundle file */
-    gnutls_certificate_set_verify_flags(gtls->cred,
-                                        GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+  if(config->verifypeer) {
+    bool imported_native_ca = false;
 
-    rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
-                                                config->CAfile,
-                                                GNUTLS_X509_FMT_PEM);
-    if(rc < 0) {
-      infof(data, "error reading ca cert file %s (%s)",
-            config->CAfile, gnutls_strerror(rc));
-      if(config->verifypeer) {
-        *pverifyresult = rc;
-        return CURLE_SSL_CACERT_BADFILE;
+    if(ssl_config->native_ca_store) {
+      rc = gnutls_certificate_set_x509_system_trust(gtls->cred);
+      if(rc < 0)
+        infof(data, "error reading native ca store (%s), continuing anyway",
+              gnutls_strerror(rc));
+      else {
+        infof(data, "found %d certificates in native ca store", rc);
+        if(rc > 0)
+          imported_native_ca = true;
       }
     }
-    else
-      infof(data, "found %d certificates in %s", rc, config->CAfile);
-  }
 
-  if(config->CApath) {
-    /* set the trusted CA cert directory */
-    rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
-                                               config->CApath,
-                                               GNUTLS_X509_FMT_PEM);
-    if(rc < 0) {
-      infof(data, "error reading ca cert file %s (%s)",
-            config->CApath, gnutls_strerror(rc));
-      if(config->verifypeer) {
-        *pverifyresult = rc;
-        return CURLE_SSL_CACERT_BADFILE;
+    if(config->CAfile) {
+      /* set the trusted CA cert bundle file */
+      gnutls_certificate_set_verify_flags(gtls->cred,
+                                          GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
+
+      rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
+                                                  config->CAfile,
+                                                  GNUTLS_X509_FMT_PEM);
+      if(rc < 0) {
+        infof(data, "error reading ca cert file %s (%s)%s",
+              config->CAfile, gnutls_strerror(rc),
+              (imported_native_ca ? ", continuing anyway" : ""));
+        if(!imported_native_ca) {
+          *pverifyresult = rc;
+          return CURLE_SSL_CACERT_BADFILE;
+        }
       }
+      else
+        infof(data, "found %d certificates in %s", rc, config->CAfile);
     }
-    else
-      infof(data, "found %d certificates in %s", rc, config->CApath);
-  }
 
-#ifdef CURL_CA_FALLBACK
-  /* use system ca certificate store as fallback */
-  if(config->verifypeer && !(config->CAfile || config->CApath)) {
-    /* this ignores errors on purpose */
-    gnutls_certificate_set_x509_system_trust(gtls->cred);
+    if(config->CApath) {
+      /* set the trusted CA cert directory */
+      rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
+                                                 config->CApath,
+                                                 GNUTLS_X509_FMT_PEM);
+      if(rc < 0) {
+        infof(data, "error reading ca cert file %s (%s)%s",
+              config->CApath, gnutls_strerror(rc),
+              (imported_native_ca ? ", continuing anyway" : ""));
+        if(!imported_native_ca) {
+          *pverifyresult = rc;
+          return CURLE_SSL_CACERT_BADFILE;
+        }
+      }
+      else
+        infof(data, "found %d certificates in %s", rc, config->CApath);
+    }
   }
-#endif
 
   if(config->CRLfile) {
     /* set the CRL list file */
@@ -537,15 +542,9 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
-#ifdef ENABLE_IPV6
-     (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
-#endif
-     sni) {
-    size_t snilen;
-    char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
-    if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
-                                          snihost, snilen) < 0) {
+  if(sni && peer->sni) {
+    if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
+                              peer->sni, strlen(peer->sni)) < 0) {
       failf(data, "Failed to set SNI");
       return CURLE_SSL_CONNECT_ERROR;
     }
@@ -585,13 +584,9 @@
   /* Only add SRP to the cipher list if SRP is requested. Otherwise
    * GnuTLS will disable TLS 1.3 support. */
   if(config->username) {
-    size_t len = strlen(prioritylist);
-
-    char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
+    char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
     if(!prioritysrp)
       return CURLE_OUT_OF_MEMORY;
-    strcpy(prioritysrp, prioritylist);
-    strcpy(prioritysrp + len, ":" GNUTLS_SRP);
     rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
     free(prioritysrp);
 
@@ -699,7 +694,7 @@
     return CURLE_OK;
 
   result = gtls_client_init(data, conn_config, ssl_config,
-                            connssl->hostname,
+                            &connssl->peer,
                             &backend->gtls, pverifyresult);
   if(result)
     return result;
@@ -811,8 +806,7 @@
                        gnutls_session_t session,
                        struct ssl_primary_config *config,
                        struct ssl_config_data *ssl_config,
-                       const char *hostname,
-                       const char *dispname,
+                       struct ssl_peer *peer,
                        const char *pinned_key)
 {
   unsigned int cert_list_size;
@@ -824,16 +818,17 @@
   char certname[65] = ""; /* limited to 64 chars by ASN.1 */
   size_t size;
   time_t certclock;
-  const char *ptr;
   int rc;
   CURLcode result = CURLE_OK;
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
+  const char *ptr;
   unsigned int algo;
   unsigned int bits;
   gnutls_protocol_t version = gnutls_protocol_get_version(session);
 #endif
   long * const certverifyresult = &ssl_config->certverifyresult;
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
   /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
   ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session),
                                      gnutls_cipher_get(session),
@@ -841,6 +836,7 @@
 
   infof(data, "SSL connection using %s / %s",
         gnutls_protocol_get_name(version), ptr);
+#endif
 
   /* This function will return the peer's raw certificate (chain) as sent by
      the peer. These certificates are in raw format (DER encoded for
@@ -1068,7 +1064,7 @@
      in RFC2818 (HTTPS), which takes into account wildcards, and the subject
      alternative name PKIX extension. Returns non zero on success, and zero on
      failure. */
-  rc = gnutls_x509_crt_check_hostname(x509_cert, hostname);
+  rc = gnutls_x509_crt_check_hostname(x509_cert, peer->hostname);
 #if GNUTLS_VERSION_NUMBER < 0x030306
   /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP
      addresses. */
@@ -1081,10 +1077,10 @@
     unsigned char addrbuf[sizeof(struct use_addr)];
     size_t addrlen = 0;
 
-    if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0)
+    if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
       addrlen = 4;
 #ifdef ENABLE_IPV6
-    else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0)
+    else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
       addrlen = 16;
 #endif
 
@@ -1114,13 +1110,13 @@
   if(!rc) {
     if(config->verifyhost) {
       failf(data, "SSL: certificate subject name (%s) does not match "
-            "target host name '%s'", certname, dispname);
+            "target host name '%s'", certname, peer->dispname);
       gnutls_x509_crt_deinit(x509_cert);
       return CURLE_PEER_FAILED_VERIFICATION;
     }
     else
       infof(data, "  common name: %s (does not match '%s')",
-            certname, dispname);
+            certname, peer->dispname);
   }
   else
     infof(data, "  common name: %s (matched)", certname);
@@ -1253,8 +1249,7 @@
   CURLcode result;
 
   result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config,
-                                  connssl->hostname, connssl->dispname,
-                                  pinned_key);
+                                  &connssl->peer, pinned_key);
   if(result)
     goto out;
 
@@ -1662,7 +1657,7 @@
   gtls_cert_status_request,      /* cert_status_request */
   gtls_connect,                  /* connect */
   gtls_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,              /* getsock */
+  Curl_ssl_adjust_pollset,       /* adjust_pollset */
   gtls_get_internals,            /* get_internals */
   gtls_close,                    /* close_one */
   Curl_none_close_all,           /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h
index ac141e1..1a81c01 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.h
+++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -43,6 +43,7 @@
 struct Curl_cfilter;
 struct ssl_primary_config;
 struct ssl_config_data;
+struct ssl_peer;
 
 struct gtls_instance {
   gnutls_session_t session;
@@ -56,7 +57,7 @@
 gtls_client_init(struct Curl_easy *data,
                  struct ssl_primary_config *config,
                  struct ssl_config_data *ssl_config,
-                 const char *hostname,
+                 struct ssl_peer *peer,
                  struct gtls_instance *gtls,
                  long *pverifyresult);
 
@@ -65,8 +66,7 @@
                        gnutls_session_t session,
                        struct ssl_primary_config *config,
                        struct ssl_config_data *ssl_config,
-                       const char *hostname,
-                       const char *dispname,
+                       struct ssl_peer *peer,
                        const char *pinned_key);
 
 extern const struct Curl_ssl Curl_ssl_gnutls;
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c
index d37bb18..fbcb25c 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.c
+++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -23,6 +23,11 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
+#if defined(USE_OPENSSL) || \
+  defined(USE_WOLFSSL) || \
+  (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
+  defined(USE_QUICHE)
+
 #include "keylog.h"
 #include <curl/curl.h>
 
@@ -55,7 +60,7 @@
     if(keylog_file_name) {
       keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
       if(keylog_file_fp) {
-#ifdef WIN32
+#ifdef _WIN32
         if(setvbuf(keylog_file_fp, NULL, _IONBF, 0))
 #else
         if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096))
@@ -157,3 +162,5 @@
   fputs(line, keylog_file_fp);
   return true;
 }
+
+#endif  /* TLS or QUIC backend */
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 2f994d7..7d70de5 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -36,6 +36,13 @@
 /* Define this to enable lots of debugging for mbedTLS */
 /* #define MBEDTLS_DEBUG */
 
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+/* mbedTLS (as of v3.5.1) has a duplicate function declaration
+   in its public headers. Disable the warning that detects it. */
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
 #include <mbedtls/version.h>
 #if MBEDTLS_VERSION_NUMBER >= 0x02040000
 #include <mbedtls/net_sockets.h>
@@ -56,6 +63,10 @@
 #  endif
 #endif
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 #include "urldata.h"
 #include "sendf.h"
 #include "inet_pton.h"
@@ -67,6 +78,7 @@
 #include "select.h"
 #include "multiif.h"
 #include "mbedtls_threadlock.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -153,7 +165,6 @@
   infof(data, "%s", line);
   (void) level;
 }
-#else
 #endif
 
 static int mbedtls_bio_cf_write(void *bio,
@@ -165,6 +176,9 @@
   CURLcode result;
 
   DEBUGASSERT(data);
+  if(!data)
+    return 0;
+
   nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
   CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
               blen, nwritten, result);
@@ -182,6 +196,8 @@
   CURLcode result;
 
   DEBUGASSERT(data);
+  if(!data)
+    return 0;
   /* OpenSSL catches this case, so should we. */
   if(!buf)
     return 0;
@@ -322,7 +338,7 @@
   char * const ssl_cert = ssl_config->primary.clientcert;
   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
   const char * const ssl_crlfile = ssl_config->primary.CRLfile;
-  const char *hostname = connssl->hostname;
+  const char *hostname = connssl->peer.hostname;
   int ret = -1;
   char errorbuf[128];
 
@@ -367,11 +383,10 @@
     /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
        terminated even when provided the exact length, forcing us to waste
        extra memory here. */
-    unsigned char *newblob = malloc(ca_info_blob->len + 1);
+    unsigned char *newblob = Curl_memdup0(ca_info_blob->data,
+                                          ca_info_blob->len);
     if(!newblob)
       return CURLE_OUT_OF_MEMORY;
-    memcpy(newblob, ca_info_blob->data, ca_info_blob->len);
-    newblob[ca_info_blob->len] = 0; /* null terminate */
     ret = mbedtls_x509_crt_parse(&backend->cacert, newblob,
                                  ca_info_blob->len + 1);
     free(newblob);
@@ -441,11 +456,10 @@
     /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
        terminated even when provided the exact length, forcing us to waste
        extra memory here. */
-    unsigned char *newblob = malloc(ssl_cert_blob->len + 1);
+    unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data,
+                                          ssl_cert_blob->len);
     if(!newblob)
       return CURLE_OUT_OF_MEMORY;
-    memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len);
-    newblob[ssl_cert_blob->len] = 0; /* null terminate */
     ret = mbedtls_x509_crt_parse(&backend->clicert, newblob,
                                  ssl_cert_blob->len + 1);
     free(newblob);
@@ -639,9 +653,9 @@
     mbedtls_ssl_conf_own_cert(&backend->config,
                               &backend->clicert, &backend->pk);
   }
-  {
-    char *snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) {
+
+  if(connssl->peer.sni) {
+    if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni)) {
       /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and
          the name to set in the SNI extension. So even if curl connects to a
          host specified as an IP address, this function must be used. */
@@ -1207,6 +1221,9 @@
 
 static void mbedtls_cleanup(void)
 {
+#ifdef THREADING_SUPPORT
+  mbedtls_entropy_free(&ts_entropy);
+#endif /* THREADING_SUPPORT */
   (void)Curl_mbedtlsthreadlock_thread_cleanup();
 }
 
@@ -1274,7 +1291,7 @@
   Curl_none_cert_status_request,    /* cert_status_request */
   mbedtls_connect,                  /* connect */
   mbedtls_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,                 /* getsock */
+  Curl_ssl_adjust_pollset,          /* adjust_pollset */
   mbedtls_get_internals,            /* get_internals */
   mbedtls_close,                    /* close_one */
   mbedtls_close_all,                /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
index bcb7106..22b1b22 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
@@ -51,7 +51,7 @@
 {
   int i;
 
-  mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1);
+  mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T));
   if(!mutex_buf)
     return 0;     /* error, no number of threads defined */
 
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index 15d84ed..ef5883d 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -79,6 +79,8 @@
 #include <openssl/bio.h>
 #include <openssl/buffer.h>
 #include <openssl/pkcs12.h>
+#include <openssl/tls1.h>
+#include <openssl/evp.h>
 
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
 #include <openssl/ocsp.h>
@@ -96,6 +98,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
 
 /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
    renegotiations when built with BoringSSL. Renegotiating is non-compliant
@@ -173,8 +178,6 @@
 
 #if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
 #define HAVE_EVP_PKEY_GET_PARAMS 1
-#else
-#define SSL_get1_peer_certificate SSL_get_peer_certificate
 #endif
 
 #ifdef HAVE_EVP_PKEY_GET_PARAMS
@@ -237,7 +240,11 @@
 #elif defined(OPENSSL_IS_AWSLC)
 #define OSSL_PACKAGE "AWS-LC"
 #else
-#define OSSL_PACKAGE "OpenSSL"
+# if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
+#   define OSSL_PACKAGE "quictls"
+# else
+#   define OSSL_PACKAGE "OpenSSL"
+#endif
 #endif
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
@@ -286,9 +293,9 @@
 #define HAVE_SSL_X509_STORE_SHARE
 #endif
 
-/* FIXME: On a specific machine using LCC 1.23, OpenSSL 2.0.0
- * is found but does not seem to have X509_STORE_up_ref. */
-#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123)
+/* FIXME: On LCC <= 1.23, OpenSSL 2.0.0 may be
+ * found but does not seem to have X509_STORE_up_ref. */
+#if defined(__LCC__) && defined(__EDG__) && (__LCC__ <= 123)
 #undef HAVE_SSL_X509_STORE_SHARE
 #endif
 
@@ -546,9 +553,9 @@
 #else
           RSA_get0_key(rsa, &n, &e, NULL);
 #endif /* HAVE_EVP_PKEY_GET_PARAMS */
-          BIO_printf(mem, "%d", BN_num_bits(n));
+          BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0);
 #else
-          BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+          BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0);
 #endif /* HAVE_OPAQUE_RSA_DSA_DH */
           push_certinfo("RSA Public Key", i);
           print_pubkey_BN(rsa, n, i);
@@ -955,8 +962,9 @@
 #endif
 
   if(!*buf) {
-    strncpy(buf, (error ? "Unknown error" : "No error"), size);
-    buf[size - 1] = '\0';
+    const char *msg = error ? "Unknown error" : "No error";
+    if(strlen(msg) < size)
+      strcpy(buf, msg);
   }
 
   return buf;
@@ -1088,6 +1096,7 @@
       UI_set_result(ui, uis, password);
       return 1;
     }
+    FALLTHROUGH();
   default:
     break;
   }
@@ -1106,6 +1115,7 @@
        (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
       return 1;
     }
+    FALLTHROUGH();
   default:
     break;
   }
@@ -1523,7 +1533,7 @@
     case SSL_FILETYPE_PEM:
       if(cert_done)
         break;
-      /* FALLTHROUGH */
+      FALLTHROUGH();
     case SSL_FILETYPE_ASN1:
       cert_use_result = key_blob ?
         SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
@@ -1753,7 +1763,7 @@
 static void ossl_cleanup(void)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
-  !defined(LIBRESSL_VERSION_NUMBER)
+  (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
   /* OpenSSL 1.1 deprecates all these cleanup functions and
      turns them into no-ops in OpenSSL 1.0 compatibility mode */
 #else
@@ -2106,22 +2116,6 @@
   return FALSE;
 }
 
-static CURLcode
-ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
-                X509 *server_cert, const char *hostname,
-                const char *dispname);
-
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
-                              X509 *server_cert)
-{
-  const char *hostname, *dispname;
-  int port;
-
-  (void)conn;
-  Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
-  return ossl_verifyhost(data, conn, server_cert, hostname, dispname);
-}
-
 /* Quote from RFC2818 section 3.1 "Server Identity"
 
    If a subjectAltName extension of type dNSName is present, that MUST
@@ -2144,10 +2138,8 @@
 
    This function is now used from ngtcp2 (QUIC) as well.
 */
-static CURLcode
-ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
-                X509 *server_cert, const char *hostname,
-                const char *dispname)
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+                              struct ssl_peer *peer, X509 *server_cert)
 {
   bool matched = FALSE;
   int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
@@ -2164,25 +2156,21 @@
   size_t hostlen;
 
   (void)conn;
-  hostlen = strlen(hostname);
-
-#ifndef ENABLE_IPV6
-  /* Silence compiler warnings for unused params */
-  (void) conn;
-#endif
-
+  hostlen = strlen(peer->hostname);
+  if(peer->is_ip_address) {
 #ifdef ENABLE_IPV6
-  if(conn->bits.ipv6_ip &&
-     Curl_inet_pton(AF_INET6, hostname, &addr)) {
-    target = GEN_IPADD;
-    addrlen = sizeof(struct in6_addr);
-  }
-  else
-#endif
-    if(Curl_inet_pton(AF_INET, hostname, &addr)) {
+    if(conn->bits.ipv6_ip &&
+       Curl_inet_pton(AF_INET6, peer->hostname, &addr)) {
       target = GEN_IPADD;
-      addrlen = sizeof(struct in_addr);
+      addrlen = sizeof(struct in6_addr);
     }
+    else
+#endif
+      if(Curl_inet_pton(AF_INET, peer->hostname, &addr)) {
+        target = GEN_IPADD;
+        addrlen = sizeof(struct in_addr);
+      }
+  }
 
   /* get a "list" of alternative names */
   altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
@@ -2232,9 +2220,9 @@
           if((altlen == strlen(altptr)) &&
              /* if this isn't true, there was an embedded zero in the name
                 string and we cannot match it. */
-             subj_alt_hostcheck(data,
-                                altptr,
-                                altlen, hostname, hostlen, dispname)) {
+             subj_alt_hostcheck(data, altptr, altlen,
+                                peer->hostname, hostlen,
+                                peer->dispname)) {
             dnsmatched = TRUE;
           }
           break;
@@ -2246,7 +2234,7 @@
             ipmatched = TRUE;
             infof(data,
                   " subjectAltName: host \"%s\" matched cert's IP address!",
-                  dispname);
+                  peer->dispname);
           }
           break;
         }
@@ -2262,9 +2250,9 @@
     /* an alternative name matched */
     ;
   else if(dNSName || iPAddress) {
-    infof(data, " subjectAltName does not match %s", dispname);
+    infof(data, " subjectAltName does not match %s", peer->dispname);
     failf(data, "SSL: no alternative certificate subject name matches "
-          "target host name '%s'", dispname);
+          "target host name '%s'", peer->dispname);
     result = CURLE_PEER_FAILED_VERIFICATION;
   }
   else {
@@ -2328,9 +2316,9 @@
       result = CURLE_PEER_FAILED_VERIFICATION;
     }
     else if(!Curl_cert_hostcheck((const char *)peer_CN,
-                                 peerlen, hostname, hostlen)) {
+                                 peerlen, peer->hostname, hostlen)) {
       failf(data, "SSL: certificate subject name '%s' does not match "
-            "target host name '%s'", peer_CN, dispname);
+            "target host name '%s'", peer_CN, peer->dispname);
       result = CURLE_PEER_FAILED_VERIFICATION;
     }
     else {
@@ -2739,12 +2727,6 @@
 #ifdef USE_OPENSSL
 /* ====================================================== */
 
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-#  define use_sni(x)  sni = (x)
-#else
-#  define use_sni(x)  Curl_nop_stmt
-#endif
-
 /* Check for OpenSSL 1.0.2 which has ALPN support. */
 #undef HAS_ALPN
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L       \
@@ -2885,7 +2867,7 @@
   failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
   return CURLE_NOT_BUILT_IN;
 #endif
-  /* FALLTHROUGH */
+  FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_2:
 #if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_1;
@@ -2893,7 +2875,7 @@
     failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
     return CURLE_NOT_BUILT_IN;
 #endif
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_1:
 #if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1;
@@ -2901,7 +2883,7 @@
     failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
     return CURLE_NOT_BUILT_IN;
 #endif
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_0:
   case CURL_SSLVERSION_TLSv1:
     break;
@@ -2912,12 +2894,12 @@
 #if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_1;
 #endif
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CURL_SSLVERSION_MAX_TLSv1_1:
 #if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_2;
 #endif
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case CURL_SSLVERSION_MAX_TLSv1_2:
 #ifdef TLS1_3_VERSION
     *ctx_options |= SSL_OP_NO_TLSv1_3;
@@ -3048,6 +3030,151 @@
   return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
 }
 
+#if defined(USE_WIN32_CRYPTO)
+static CURLcode import_windows_cert_store(struct Curl_easy *data,
+                                          const char *name,
+                                          X509_STORE *store,
+                                          bool *imported)
+{
+  CURLcode result = CURLE_OK;
+  HCERTSTORE hStore;
+
+  *imported = false;
+
+  hStore = CertOpenSystemStoreA(0, name);
+  if(hStore) {
+    PCCERT_CONTEXT pContext = NULL;
+    /* The array of enhanced key usage OIDs will vary per certificate and
+       is declared outside of the loop so that rather than malloc/free each
+       iteration we can grow it with realloc, when necessary. */
+    CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+    DWORD enhkey_usage_size = 0;
+
+    /* This loop makes a best effort to import all valid certificates from
+       the MS root store. If a certificate cannot be imported it is
+       skipped. 'result' is used to store only hard-fail conditions (such
+       as out of memory) that cause an early break. */
+    result = CURLE_OK;
+    for(;;) {
+      X509 *x509;
+      FILETIME now;
+      BYTE key_usage[2];
+      DWORD req_size;
+      const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+      char cert_name[256];
+#endif
+
+      pContext = CertEnumCertificatesInStore(hStore, pContext);
+      if(!pContext)
+        break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+      if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+                             NULL, cert_name, sizeof(cert_name))) {
+        strcpy(cert_name, "Unknown");
+      }
+      infof(data, "SSL: Checking cert \"%s\"", cert_name);
+#endif
+      encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+      if(!encoded_cert)
+        continue;
+
+      GetSystemTimeAsFileTime(&now);
+      if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+         CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+        continue;
+
+      /* If key usage exists check for signing attribute */
+      if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+                                 pContext->pCertInfo,
+                                 key_usage, sizeof(key_usage))) {
+        if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+          continue;
+      }
+      else if(GetLastError())
+        continue;
+
+      /* If enhanced key usage exists check for server auth attribute.
+       *
+       * Note "In a Microsoft environment, a certificate might also have
+       * EKU extended properties that specify valid uses for the
+       * certificate."  The call below checks both, and behavior varies
+       * depending on what is found. For more details see
+       * CertGetEnhancedKeyUsage doc.
+       */
+      if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+        if(req_size && req_size > enhkey_usage_size) {
+          void *tmp = realloc(enhkey_usage, req_size);
+
+          if(!tmp) {
+            failf(data, "SSL: Out of memory allocating for OID list");
+            result = CURLE_OUT_OF_MEMORY;
+            break;
+          }
+
+          enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+          enhkey_usage_size = req_size;
+        }
+
+        if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+          if(!enhkey_usage->cUsageIdentifier) {
+            /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
+               is good for all uses. If it returns zero, the certificate
+               has no valid uses." */
+            if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+              continue;
+          }
+          else {
+            DWORD i;
+            bool found = false;
+
+            for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+              if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+                         enhkey_usage->rgpszUsageIdentifier[i])) {
+                found = true;
+                break;
+              }
+            }
+
+            if(!found)
+              continue;
+          }
+        }
+        else
+          continue;
+      }
+      else
+        continue;
+
+      x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+      if(!x509)
+        continue;
+
+      /* Try to import the certificate. This may fail for legitimate
+         reasons such as duplicate certificate, which is allowed by MS but
+         not OpenSSL. */
+      if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+        infof(data, "SSL: Imported cert \"%s\"", cert_name);
+#endif
+        *imported = true;
+      }
+      X509_free(x509);
+    }
+
+    free(enhkey_usage);
+    CertFreeCertificateContext(pContext);
+    CertCloseStore(hStore, 0);
+
+    if(result)
+      return result;
+  }
+
+  return result;
+}
+#endif
+
 static CURLcode populate_x509_store(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     X509_STORE *store)
@@ -3066,6 +3193,8 @@
   bool imported_native_ca = false;
   bool imported_ca_info_blob = false;
 
+  CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+              ssl_cafile? ssl_cafile : "none", !!ca_info_blob);
   if(!store)
     return CURLE_OUT_OF_MEMORY;
 
@@ -3077,140 +3206,25 @@
        https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
        https://datatracker.ietf.org/doc/html/rfc5280 */
     if(ssl_config->native_ca_store) {
-      HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
-
-      if(hStore) {
-        PCCERT_CONTEXT pContext = NULL;
-        /* The array of enhanced key usage OIDs will vary per certificate and
-           is declared outside of the loop so that rather than malloc/free each
-           iteration we can grow it with realloc, when necessary. */
-        CERT_ENHKEY_USAGE *enhkey_usage = NULL;
-        DWORD enhkey_usage_size = 0;
-
-        /* This loop makes a best effort to import all valid certificates from
-           the MS root store. If a certificate cannot be imported it is
-           skipped. 'result' is used to store only hard-fail conditions (such
-           as out of memory) that cause an early break. */
-        result = CURLE_OK;
-        for(;;) {
-          X509 *x509;
-          FILETIME now;
-          BYTE key_usage[2];
-          DWORD req_size;
-          const unsigned char *encoded_cert;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-          char cert_name[256];
-#endif
-
-          pContext = CertEnumCertificatesInStore(hStore, pContext);
-          if(!pContext)
-            break;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-          if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
-                                 NULL, cert_name, sizeof(cert_name))) {
-            strcpy(cert_name, "Unknown");
-          }
-          infof(data, "SSL: Checking cert \"%s\"", cert_name);
-#endif
-          encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
-          if(!encoded_cert)
-            continue;
-
-          GetSystemTimeAsFileTime(&now);
-          if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
-             CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
-            continue;
-
-          /* If key usage exists check for signing attribute */
-          if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
-                                     pContext->pCertInfo,
-                                     key_usage, sizeof(key_usage))) {
-            if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
-              continue;
-          }
-          else if(GetLastError())
-            continue;
-
-          /* If enhanced key usage exists check for server auth attribute.
-           *
-           * Note "In a Microsoft environment, a certificate might also have
-           * EKU extended properties that specify valid uses for the
-           * certificate."  The call below checks both, and behavior varies
-           * depending on what is found. For more details see
-           * CertGetEnhancedKeyUsage doc.
-           */
-          if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
-            if(req_size && req_size > enhkey_usage_size) {
-              void *tmp = realloc(enhkey_usage, req_size);
-
-              if(!tmp) {
-                failf(data, "SSL: Out of memory allocating for OID list");
-                result = CURLE_OUT_OF_MEMORY;
-                break;
-              }
-
-              enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
-              enhkey_usage_size = req_size;
-            }
-
-            if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
-              if(!enhkey_usage->cUsageIdentifier) {
-                /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
-                   is good for all uses. If it returns zero, the certificate
-                   has no valid uses." */
-                if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
-                  continue;
-              }
-              else {
-                DWORD i;
-                bool found = false;
-
-                for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
-                  if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
-                             enhkey_usage->rgpszUsageIdentifier[i])) {
-                    found = true;
-                    break;
-                  }
-                }
-
-                if(!found)
-                  continue;
-              }
-            }
-            else
-              continue;
-          }
-          else
-            continue;
-
-          x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
-          if(!x509)
-            continue;
-
-          /* Try to import the certificate. This may fail for legitimate
-             reasons such as duplicate certificate, which is allowed by MS but
-             not OpenSSL. */
-          if(X509_STORE_add_cert(store, x509) == 1) {
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-            infof(data, "SSL: Imported cert \"%s\"", cert_name);
-#endif
-            imported_native_ca = true;
-          }
-          X509_free(x509);
-        }
-
-        free(enhkey_usage);
-        CertFreeCertificateContext(pContext);
-        CertCloseStore(hStore, 0);
-
+      const char *storeNames[] = {
+        "ROOT",   /* Trusted Root Certification Authorities */
+        "CA"      /* Intermediate Certification Authorities */
+      };
+      size_t i;
+      for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+        bool imported = false;
+        result = import_windows_cert_store(data, storeNames[i], store,
+                                           &imported);
         if(result)
           return result;
+        if(imported) {
+          infof(data, "successfully imported Windows %s store", storeNames[i]);
+          imported_native_ca = true;
+        }
+        else
+          infof(data, "error importing Windows %s store, continuing anyway",
+                storeNames[i]);
       }
-      if(imported_native_ca)
-        infof(data, "successfully imported Windows CA store");
-      else
-        infof(data, "error importing Windows CA store, continuing anyway");
     }
 #endif
     if(ca_info_blob) {
@@ -3226,7 +3240,7 @@
     }
 
     if(ssl_cafile || ssl_capath) {
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
       /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
       if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) {
         if(!imported_native_ca && !imported_ca_info_blob) {
@@ -3355,6 +3369,7 @@
   struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
   X509_STORE *store = NULL;
 
+  DEBUGASSERT(multi);
   if(multi &&
      multi->ssl_backend_data &&
      multi->ssl_backend_data->store &&
@@ -3374,6 +3389,7 @@
   struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
   struct multi_ssl_backend_data *mbackend;
 
+  DEBUGASSERT(multi);
   if(!multi)
     return;
 
@@ -3465,17 +3481,6 @@
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   BIO *bio;
-
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  bool sni;
-  const char *hostname = connssl->hostname;
-
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-#endif
   const long int ssl_version = conn_config->version;
   char * const ssl_cert = ssl_config->primary.clientcert;
   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
@@ -3510,7 +3515,6 @@
 #else
     req_method = SSLv23_client_method();
 #endif
-    use_sni(TRUE);
     break;
   case CURL_SSLVERSION_SSLv2:
     failf(data, "No SSLv2 support");
@@ -3803,13 +3807,8 @@
 
   backend->server_cert = 0x0;
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
-#ifdef ENABLE_IPV6
-     (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
-#endif
-     sni) {
-    char *snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) {
+  if(connssl->peer.sni) {
+    if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) {
       failf(data, "Failed set SNI");
       return CURLE_SSL_CONNECT_ERROR;
     }
@@ -3818,6 +3817,7 @@
 
   SSL_set_app_data(backend->handle, cf);
 
+  connssl->reused_session = FALSE;
   if(ssl_config->primary.sessionid) {
     Curl_ssl_sessionid_lock(data);
     if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
@@ -3831,6 +3831,7 @@
       }
       /* Informational message */
       infof(data, "SSL reusing session ID");
+      connssl->reused_session = TRUE;
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -3991,7 +3992,7 @@
           Curl_strerror(sockerr, extramsg, sizeof(extramsg));
         failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
               extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
-              connssl->hostname, connssl->port);
+              connssl->peer.hostname, connssl->port);
         return result;
       }
 
@@ -4002,13 +4003,28 @@
     }
   }
   else {
+    int psigtype_nid = NID_undef;
+    const char *negotiated_group_name = NULL;
+
     /* we connected fine, we're not waiting for anything else. */
     connssl->connecting_state = ssl_connect_3;
 
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+    SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid);
+#if (OPENSSL_VERSION_NUMBER >= 0x30200000L)
+    negotiated_group_name = SSL_get0_group_name(backend->handle);
+#else
+    negotiated_group_name =
+      OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF);
+#endif
+#endif
+
     /* Informational message */
-    infof(data, "SSL connection using %s / %s",
+    infof(data, "SSL connection using %s / %s / %s / %s",
           SSL_get_version(backend->handle),
-          SSL_get_cipher(backend->handle));
+          SSL_get_cipher(backend->handle),
+          negotiated_group_name? negotiated_group_name : "[blank]",
+          OBJ_nid2sn(psigtype_nid));
 
 #ifdef HAS_ALPN
     /* Sets data and len to negotiated protocol, len is 0 if no protocol was
@@ -4085,6 +4101,75 @@
   return result;
 }
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
+  !(defined(LIBRESSL_VERSION_NUMBER) && \
+    LIBRESSL_VERSION_NUMBER < 0x3060000fL) && \
+  !defined(OPENSSL_IS_BORINGSSL) && \
+  !defined(OPENSSL_IS_AWSLC) && \
+  !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
+{
+  STACK_OF(X509) *certstack;
+  long verify_result;
+  int num_cert_levels;
+  int cert_level;
+
+  verify_result = SSL_get_verify_result(ssl);
+  if(verify_result != X509_V_OK)
+    certstack = SSL_get_peer_cert_chain(ssl);
+  else
+    certstack = SSL_get0_verified_chain(ssl);
+  num_cert_levels = sk_X509_num(certstack);
+
+  for(cert_level = 0; cert_level < num_cert_levels; cert_level++) {
+    char cert_algorithm[80] = "";
+    char group_name_final[80] = "";
+    const X509_ALGOR *palg_cert = NULL;
+    const ASN1_OBJECT *paobj_cert = NULL;
+    X509 *current_cert;
+    EVP_PKEY *current_pkey;
+    int key_bits;
+    int key_sec_bits;
+    int get_group_name;
+    const char *type_name;
+
+    current_cert = sk_X509_value(certstack, cert_level);
+
+    X509_get0_signature(NULL, &palg_cert, current_cert);
+    X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert);
+    OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0);
+
+    current_pkey = X509_get0_pubkey(current_cert);
+    key_bits = EVP_PKEY_bits(current_pkey);
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
+#define EVP_PKEY_get_security_bits EVP_PKEY_security_bits
+#endif
+    key_sec_bits = EVP_PKEY_get_security_bits(current_pkey);
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+    {
+      char group_name[80] = "";
+      get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name,
+                                               sizeof(group_name), NULL);
+      msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name);
+    }
+    type_name = EVP_PKEY_get0_type_name(current_pkey);
+#else
+    get_group_name = 0;
+    type_name = NULL;
+#endif
+
+    infof(data,
+          "  Certificate level %d: "
+          "Public key type %s%s (%d/%d Bits/secBits), signed using %s",
+          cert_level, type_name ? type_name : "?",
+          get_group_name == 0 ? "" : group_name_final,
+          key_bits, key_sec_bits, cert_algorithm);
+  }
+}
+#else
+#define infof_certstack(data, ssl)
+#endif
+
 /*
  * Get the server cert, verify it and show it, etc., only call failf() if the
  * 'strict' argument is TRUE as otherwise all this is for informational
@@ -4163,8 +4248,8 @@
   BIO_free(mem);
 
   if(conn_config->verifyhost) {
-    result = ossl_verifyhost(data, conn, backend->server_cert,
-                             connssl->hostname, connssl->dispname);
+    result = Curl_ossl_verifyhost(data, conn, &connssl->peer,
+                                  backend->server_cert);
     if(result) {
       X509_free(backend->server_cert);
       backend->server_cert = NULL;
@@ -4274,11 +4359,28 @@
       infof(data, " SSL certificate verify ok.");
   }
 
+  infof_certstack(data, backend->handle);
+
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
   !defined(OPENSSL_NO_OCSP)
-  if(conn_config->verifystatus) {
+  if(conn_config->verifystatus && !connssl->reused_session) {
+    /* don't do this after Session ID reuse */
     result = verifystatus(cf, data);
     if(result) {
+      /* when verifystatus failed, remove the session id from the cache again
+         if present */
+      if(!Curl_ssl_cf_is_proxy(cf)) {
+        void *old_ssl_sessionid = NULL;
+        bool incache;
+        Curl_ssl_sessionid_lock(data);
+        incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+        if(incache) {
+          infof(data, "Remove session ID again from cache");
+          Curl_ssl_delsessionid(data, old_ssl_sessionid);
+        }
+        Curl_ssl_sessionid_unlock(data);
+      }
+
       X509_free(backend->server_cert);
       backend->server_cert = NULL;
       return result;
@@ -4525,10 +4627,10 @@
         ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
       else if(sockerr)
         Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
-      else {
-        strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
-        error_buffer[sizeof(error_buffer) - 1] = '\0';
-      }
+      else
+        msnprintf(error_buffer, sizeof(error_buffer), "%s",
+                  SSL_ERROR_to_str(err));
+
       failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d",
             error_buffer, sockerr);
       *curlcode = CURLE_SEND_ERROR;
@@ -4538,22 +4640,9 @@
     case SSL_ERROR_SSL: {
       /*  A failure in the SSL library occurred, usually a protocol error.
           The OpenSSL error queue contains more information on the error. */
-      struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-      struct ssl_connect_data *connssl_next = cf_ssl_next?
-        cf_ssl_next->ctx : NULL;
       sslerror = ERR_get_error();
-      if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL &&
-         ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET &&
-         connssl->state == ssl_connection_complete &&
-         (connssl_next && connssl_next->state == ssl_connection_complete)
-        ) {
-        char ver[120];
-        (void)ossl_version(ver, sizeof(ver));
-        failf(data, "Error: %s does not support double SSL tunneling.", ver);
-      }
-      else
-        failf(data, "SSL_write() error: %s",
-              ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+      failf(data, "SSL_write() error: %s",
+            ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
       *curlcode = CURLE_SEND_ERROR;
       rc = -1;
       goto out;
@@ -4634,10 +4723,9 @@
           ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
         else if(sockerr && err == SSL_ERROR_SYSCALL)
           Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
-        else {
-          strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
-          error_buffer[sizeof(error_buffer) - 1] = '\0';
-        }
+        else
+          msnprintf(error_buffer, sizeof(error_buffer), "%s",
+                    SSL_ERROR_to_str(err));
         failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d",
               error_buffer, sockerr);
         *curlcode = CURLE_RECV_ERROR;
@@ -4858,7 +4946,7 @@
   ossl_cert_status_request, /* cert_status_request */
   ossl_connect,             /* connect */
   ossl_connect_nonblocking, /* connect_nonblocking */
-  Curl_ssl_get_select_socks,/* getsock */
+  Curl_ssl_adjust_pollset,  /* adjust_pollset */
   ossl_get_internals,       /* get_internals */
   ossl_close,               /* close_one */
   ossl_close_all,           /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h
index 950faab..e802363 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.h
+++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -31,24 +31,21 @@
  * This header should only be needed to get included by vtls.c, openssl.c
  * and ngtcp2.c
  */
+#include <openssl/ossl_typ.h>
 #include <openssl/ssl.h>
 
 #include "urldata.h"
 
-/*
- * In an effort to avoid using 'X509 *' here, we instead use the struct
- * x509_st version of the type so that we can forward-declare it here without
- * having to include <openssl/x509v3.h>. Including that header causes name
- * conflicts when libcurl is built with both Schannel and OpenSSL support.
- */
-struct x509_st;
+#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
+#define SSL_get1_peer_certificate SSL_get_peer_certificate
+#endif
+
 CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
-                              struct x509_st *server_cert);
+                              struct ssl_peer *peer, X509 *server_cert);
 extern const struct Curl_ssl Curl_ssl_openssl;
 
-struct ssl_ctx_st;
 CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
-                                   struct ssl_ctx_st *ctx, char *cert_file,
+                                   SSL_CTX *ctx, char *cert_file,
                                    const struct curl_blob *cert_blob,
                                    const char *cert_type, char *key_file,
                                    const struct curl_blob *key_blob,
@@ -65,5 +62,9 @@
                                    struct Curl_easy *data,
                                    SSL_CTX *ssl_ctx);
 
+CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 SSL_CTX *ssl_ctx);
+
 #endif /* USE_OPENSSL */
 #endif /* HEADER_CURL_SSLUSE_H */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index a3e9d96..d589709 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -39,6 +39,7 @@
 #include "select.h"
 #include "strerror.h"
 #include "multiif.h"
+#include "connect.h" /* for the connect timeout */
 
 struct rustls_ssl_backend_data
 {
@@ -75,14 +76,6 @@
   return backend->data_pending;
 }
 
-static CURLcode
-cr_connect(struct Curl_cfilter *cf UNUSED_PARAM,
-           struct Curl_easy *data UNUSED_PARAM)
-{
-  infof(data, "rustls_connect: unimplemented");
-  return CURLE_SSL_CONNECT_ERROR;
-}
-
 struct io_ctx {
   struct Curl_cfilter *cf;
   struct Curl_easy *data;
@@ -163,7 +156,7 @@
     size_t errorlen;
     rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
     failf(data, "rustls_connection_process_new_packets: %.*s",
-      errorlen, errorbuf);
+      (int)errorlen, errorbuf);
     *err = map_error(rresult);
     return -1;
   }
@@ -232,7 +225,7 @@
       char errorbuf[255];
       size_t errorlen;
       rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
-      failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf);
+      failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
       *err = CURLE_READ_ERROR;
       nread = -1;
       goto out;
@@ -308,7 +301,7 @@
                                       &plainwritten);
     if(rresult != RUSTLS_RESULT_OK) {
       rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
-      failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf);
+      failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
@@ -386,7 +379,7 @@
     /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
     (ca_info_blob ? NULL : conn_config->CAfile);
   const bool verifypeer = conn_config->verifypeer;
-  const char *hostname = connssl->hostname;
+  const char *hostname = connssl->peer.hostname;
   char errorbuf[256];
   size_t errorlen;
   int result;
@@ -458,16 +451,15 @@
   backend->config = rustls_client_config_builder_build(config_builder);
   DEBUGASSERT(rconn == NULL);
   {
-    char *snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost) {
-      failf(data, "rustls: failed to get SNI");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    result = rustls_client_connection_new(backend->config, snihost, &rconn);
+    /* rustls claims to manage ip address hostnames as well here. So,
+     * if we have an SNI, we use it, otherwise we pass the hostname */
+    char *server = connssl->peer.sni?
+                   connssl->peer.sni : connssl->peer.hostname;
+    result = rustls_client_connection_new(backend->config, server, &rconn);
   }
   if(result != RUSTLS_RESULT_OK) {
     rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
-    failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
+    failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
     return CURLE_COULDNT_CONNECT;
   }
   rustls_connection_set_userdata(rconn, backend);
@@ -486,9 +478,20 @@
   Curl_alpn_set_negotiated(cf, data, protocol, len);
 }
 
+/* Given an established network connection, do a TLS handshake.
+ *
+ * If `blocking` is true, this function will block until the handshake is
+ * complete. Otherwise it will return as soon as I/O would block.
+ *
+ * For the non-blocking I/O case, this function will set `*done` to true
+ * once the handshake is complete. This function never reads the value of
+ * `*done*`.
+ */
 static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
-                       struct Curl_easy *data, bool *done)
+cr_connect_common(struct Curl_cfilter *cf,
+                  struct Curl_easy *data,
+                  bool blocking,
+                  bool *done)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
@@ -502,6 +505,8 @@
   bool wants_write;
   curl_socket_t writefd;
   curl_socket_t readfd;
+  timediff_t timeout_ms;
+  timediff_t socket_check_timeout;
 
   DEBUGASSERT(backend);
 
@@ -539,12 +544,29 @@
     writefd = wants_write?sockfd:CURL_SOCKET_BAD;
     readfd = wants_read?sockfd:CURL_SOCKET_BAD;
 
-    what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0);
+    /* check allowed time left */
+    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+    if(timeout_ms < 0) {
+      /* no need to continue if time already is up */
+      failf(data, "rustls: operation timed out before socket check");
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    socket_check_timeout = blocking?timeout_ms:0;
+
+    what = Curl_socket_check(
+      readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout);
     if(what < 0) {
       /* fatal error */
       failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
       return CURLE_SSL_CONNECT_ERROR;
     }
+    if(blocking && 0 == what) {
+      failf(data, "rustls connection timeout after %"
+        CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
+      return CURLE_OPERATION_TIMEDOUT;
+    }
     if(0 == what) {
       infof(data, "Curl_socket_check: %s would block",
             wants_read&&wants_write ? "writing and reading" :
@@ -589,32 +611,43 @@
   DEBUGASSERT(false);
 }
 
-/* returns a bitmap of flags for this connection's first socket indicating
-   whether we want to read or write */
-static int
-cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
-                    curl_socket_t *socks)
+static CURLcode
+cr_connect_nonblocking(struct Curl_cfilter *cf,
+                       struct Curl_easy *data, bool *done)
 {
-  struct ssl_connect_data *const connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct rustls_ssl_backend_data *const backend =
-    (struct rustls_ssl_backend_data *)connssl->backend;
-  struct rustls_connection *rconn = NULL;
+  return cr_connect_common(cf, data, false, done);
+}
 
-  (void)data;
-  DEBUGASSERT(backend);
-  rconn = backend->conn;
+static CURLcode
+cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM,
+           struct Curl_easy *data UNUSED_PARAM)
+{
+  bool done; /* unused */
+  return cr_connect_common(cf, data, true, &done);
+}
 
-  if(rustls_connection_wants_write(rconn)) {
-    socks[0] = sockfd;
-    return GETSOCK_WRITESOCK(0);
+static void cr_adjust_pollset(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              struct easy_pollset *ps)
+{
+  if(!cf->connected) {
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+    struct ssl_connect_data *const connssl = cf->ctx;
+    struct rustls_ssl_backend_data *const backend =
+      (struct rustls_ssl_backend_data *)connssl->backend;
+    struct rustls_connection *rconn = NULL;
+
+    (void)data;
+    DEBUGASSERT(backend);
+    rconn = backend->conn;
+
+    if(rustls_connection_wants_write(rconn)) {
+      Curl_pollset_add_out(data, ps, sock);
+    }
+    if(rustls_connection_wants_read(rconn)) {
+      Curl_pollset_add_in(data, ps, sock);
+    }
   }
-  if(rustls_connection_wants_read(rconn)) {
-    socks[0] = sockfd;
-    return GETSOCK_READSOCK(0);
-  }
-
-  return GETSOCK_BLANK;
 }
 
 static void *
@@ -675,9 +708,9 @@
   cr_data_pending,                 /* data_pending */
   Curl_none_random,                /* random */
   Curl_none_cert_status_request,   /* cert_status_request */
-  cr_connect,                      /* connect */
+  cr_connect_blocking,             /* connect */
   cr_connect_nonblocking,          /* connect_nonblocking */
-  cr_get_select_socks,             /* get_select_socks */
+  cr_adjust_pollset,               /* adjust_pollset */
   cr_get_internals,                /* get_internals */
   cr_close,                        /* close_one */
   Curl_none_close_all,             /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 410a5c4..45c3373 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -439,6 +439,12 @@
   return CURLE_OK;
 }
 #endif
+
+static bool algo(const char *check, char *namep, size_t nlen)
+{
+  return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
+}
+
 static CURLcode
 schannel_acquire_credential_handle(struct Curl_cfilter *cf,
                                    struct Curl_easy *data)
@@ -660,7 +666,7 @@
                 cert_showfilename_error);
         else
           failf(data, "schannel: Failed to import cert file %s, "
-                "last error is 0x%x",
+                "last error is 0x%lx",
                 cert_showfilename_error, errorcode);
         return CURLE_SSL_CERTPROBLEM;
       }
@@ -671,7 +677,7 @@
 
       if(!client_certs[0]) {
         failf(data, "schannel: Failed to get certificate from file %s"
-              ", last error is 0x%x",
+              ", last error is 0x%lx",
               cert_showfilename_error, GetLastError());
         CertCloseStore(cert_store, 0);
         return CURLE_SSL_CERTPROBLEM;
@@ -684,10 +690,15 @@
                       CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
                       cert_store_path);
       if(!cert_store) {
-        failf(data, "schannel: Failed to open cert store %x %s, "
-              "last error is 0x%x",
-              cert_store_name, cert_store_path, GetLastError());
+        char *path_utf8 =
+          curlx_convert_tchar_to_UTF8(cert_store_path);
+        failf(data, "schannel: Failed to open cert store %lx %s, "
+              "last error is 0x%lx",
+              cert_store_name,
+              (path_utf8 ? path_utf8 : "(unknown)"),
+              GetLastError());
         free(cert_store_path);
+        curlx_unicodefree(path_utf8);
         curlx_unicodefree(cert_path);
         return CURLE_SSL_CERTPROBLEM;
       }
@@ -790,9 +801,7 @@
 
       char *startCur = ciphers13;
       int algCount = 0;
-      char tmp[LONGEST_ALG_ID] = { 0 };
       char *nameEnd;
-      size_t n;
 
       disable_aes_gcm_sha384 = TRUE;
       disable_aes_gcm_sha256 = TRUE;
@@ -801,40 +810,34 @@
       disable_aes_ccm_sha256 = TRUE;
 
       while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+        size_t n;
+        char *namep;
         nameEnd = strchr(startCur, ':');
         n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+        namep = startCur;
 
-        /* reject too-long cipher names */
-        if(n > (LONGEST_ALG_ID - 1)) {
-          failf(data, "schannel: Cipher name too long, not checked");
-          return CURLE_SSL_CIPHER;
-        }
-
-        strncpy(tmp, startCur, n);
-        tmp[n] = 0;
-
-        if(disable_aes_gcm_sha384
-           && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) {
+        if(disable_aes_gcm_sha384 &&
+           algo("TLS_AES_256_GCM_SHA384", namep, n)) {
           disable_aes_gcm_sha384 = FALSE;
         }
         else if(disable_aes_gcm_sha256
-                && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+                && algo("TLS_AES_128_GCM_SHA256", namep, n)) {
           disable_aes_gcm_sha256 = FALSE;
         }
         else if(disable_chacha_poly
-                && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+                && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
           disable_chacha_poly = FALSE;
         }
         else if(disable_aes_ccm_8_sha256
-                && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+                && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
           disable_aes_ccm_8_sha256 = FALSE;
         }
         else if(disable_aes_ccm_sha256
-                && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+                && algo("TLS_AES_128_CCM_SHA256", namep, n)) {
           disable_aes_ccm_sha256 = FALSE;
         }
         else {
-          failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp);
+          failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
           return CURLE_SSL_CIPHER;
         }
 
@@ -1063,17 +1066,12 @@
 #endif
   SECURITY_STATUS sspi_status = SEC_E_OK;
   struct Curl_schannel_cred *old_cred = NULL;
-  struct in_addr addr;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr6;
-#endif
   CURLcode result;
-  const char *hostname = connssl->hostname;
 
   DEBUGASSERT(backend);
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 1/3)",
-               hostname, connssl->port));
+               connssl->peer.hostname, connssl->port));
 
   if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
                                   VERSION_LESS_THAN_EQUAL)) {
@@ -1154,22 +1152,14 @@
 
     /* A hostname associated with the credential is needed by
        InitializeSecurityContext for SNI and other reasons. */
-    snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost) {
-      failf(data, "Failed to set SNI");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
+    snihost = connssl->peer.sni? connssl->peer.sni : connssl->peer.hostname;
     backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost);
     if(!backend->cred->sni_hostname)
       return CURLE_OUT_OF_MEMORY;
   }
 
   /* Warn if SNI is disabled due to use of an IP address */
-  if(Curl_inet_pton(AF_INET, hostname, &addr)
-#ifdef ENABLE_IPV6
-     || Curl_inet_pton(AF_INET6, hostname, &addr6)
-#endif
-    ) {
+  if(connssl->peer.is_ip_address) {
     infof(data, "schannel: using IP address, SNI is not supported by OS.");
   }
 
@@ -1208,9 +1198,8 @@
     cur += proto.len;
 
     *list_len = curlx_uitous(cur - list_start_index);
-    *extension_len = *list_len +
-      (unsigned short)sizeof(unsigned int) +
-      (unsigned short)sizeof(unsigned short);
+    *extension_len = (unsigned int)(*list_len +
+      sizeof(unsigned int) + sizeof(unsigned short));
 
     InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
     InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
@@ -1346,7 +1335,7 @@
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 2/3)",
-               connssl->hostname, connssl->port));
+               connssl->peer.hostname, connssl->port));
 
   if(!backend->cred || !backend->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
@@ -1700,7 +1689,7 @@
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 3/3)",
-               connssl->hostname, connssl->port));
+               connssl->peer.hostname, connssl->port));
 
   if(!backend->cred)
     return CURLE_SSL_CONNECT_ERROR;
@@ -2345,10 +2334,10 @@
     else {
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       char buffer[STRERROR_LEN];
-#endif
-      *err = CURLE_RECV_ERROR;
       infof(data, "schannel: failed to read data from server: %s",
             Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+#endif
+      *err = CURLE_RECV_ERROR;
       goto cleanup;
     }
   }
@@ -2498,7 +2487,7 @@
 
   if(backend->ctxt) {
     infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
-          connssl->hostname, connssl->port);
+          connssl->peer.hostname, connssl->port);
   }
 
   if(backend->cred && backend->ctxt) {
@@ -2754,6 +2743,151 @@
   return &backend->ctxt->ctxt_handle;
 }
 
+HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
+                                               const struct Curl_easy *data)
+{
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+  struct schannel_multi_ssl_backend_data *mbackend;
+  const struct ssl_general_config *cfg = &data->set.general_ssl;
+  timediff_t timeout_ms;
+  timediff_t elapsed_ms;
+  struct curltime now;
+  unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH];
+
+  DEBUGASSERT(multi);
+
+  if(!multi || !multi->ssl_backend_data) {
+    return NULL;
+  }
+
+  mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
+  if(!mbackend->cert_store) {
+    return NULL;
+  }
+
+  /* zero ca_cache_timeout completely disables caching */
+  if(!cfg->ca_cache_timeout) {
+    return NULL;
+  }
+
+  /* check for cache timeout by using the cached_x509_store_expired timediff
+     calculation pattern from openssl.c.
+     negative timeout means retain forever. */
+  timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
+  if(timeout_ms >= 0) {
+    now = Curl_now();
+    elapsed_ms = Curl_timediff(now, mbackend->time);
+    if(elapsed_ms >= timeout_ms) {
+      return NULL;
+    }
+  }
+
+  if(ca_info_blob) {
+    if(!mbackend->CAinfo_blob_digest) {
+      return NULL;
+    }
+    if(mbackend->CAinfo_blob_size != ca_info_blob->len) {
+      return NULL;
+    }
+    schannel_sha256sum((const unsigned char *)ca_info_blob->data,
+                       ca_info_blob->len,
+                       info_blob_digest,
+                       CURL_SHA256_DIGEST_LENGTH);
+    if(memcmp(mbackend->CAinfo_blob_digest,
+              info_blob_digest,
+              CURL_SHA256_DIGEST_LENGTH)) {
+        return NULL;
+    }
+  }
+  else {
+    if(!conn_config->CAfile || !mbackend->CAfile ||
+       strcmp(mbackend->CAfile, conn_config->CAfile)) {
+      return NULL;
+    }
+  }
+
+  return mbackend->cert_store;
+}
+
+bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
+                                         const struct Curl_easy *data,
+                                         HCERTSTORE cert_store)
+{
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi;
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+  struct schannel_multi_ssl_backend_data *mbackend;
+  unsigned char *CAinfo_blob_digest = NULL;
+  size_t CAinfo_blob_size = 0;
+  char *CAfile = NULL;
+
+  DEBUGASSERT(multi);
+
+  if(!multi) {
+    return false;
+  }
+
+  if(!multi->ssl_backend_data) {
+    multi->ssl_backend_data =
+      calloc(1, sizeof(struct schannel_multi_ssl_backend_data));
+    if(!multi->ssl_backend_data) {
+      return false;
+    }
+  }
+
+  mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
+
+
+  if(ca_info_blob) {
+    CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH);
+    if(!CAinfo_blob_digest) {
+      return false;
+    }
+    schannel_sha256sum((const unsigned char *)ca_info_blob->data,
+                       ca_info_blob->len,
+                       CAinfo_blob_digest,
+                       CURL_SHA256_DIGEST_LENGTH);
+    CAinfo_blob_size = ca_info_blob->len;
+  }
+  else {
+    if(conn_config->CAfile) {
+      CAfile = strdup(conn_config->CAfile);
+      if(!CAfile) {
+        return false;
+      }
+    }
+  }
+
+  /* free old cache data */
+  if(mbackend->cert_store) {
+    CertCloseStore(mbackend->cert_store, 0);
+  }
+  free(mbackend->CAinfo_blob_digest);
+  free(mbackend->CAfile);
+
+  mbackend->time = Curl_now();
+  mbackend->cert_store = cert_store;
+  mbackend->CAinfo_blob_digest = CAinfo_blob_digest;
+  mbackend->CAinfo_blob_size = CAinfo_blob_size;
+  mbackend->CAfile = CAfile;
+  return true;
+}
+
+static void schannel_free_multi_ssl_backend_data(
+  struct multi_ssl_backend_data *msbd)
+{
+  struct schannel_multi_ssl_backend_data *mbackend =
+    (struct schannel_multi_ssl_backend_data*)msbd;
+  if(mbackend->cert_store) {
+    CertCloseStore(mbackend->cert_store, 0);
+  }
+  free(mbackend->CAinfo_blob_digest);
+  free(mbackend->CAfile);
+  free(mbackend);
+}
+
 const struct Curl_ssl Curl_ssl_schannel = {
   { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
 
@@ -2777,7 +2911,7 @@
   Curl_none_cert_status_request,     /* cert_status_request */
   schannel_connect,                  /* connect */
   schannel_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,         /* getsock */
+  Curl_ssl_adjust_pollset,           /* adjust_pollset */
   schannel_get_internals,            /* get_internals */
   schannel_close,                    /* close_one */
   Curl_none_close_all,               /* close_all */
@@ -2789,7 +2923,7 @@
   schannel_sha256sum,                /* sha256sum */
   NULL,                              /* associate_connection */
   NULL,                              /* disassociate_connection */
-  NULL,                              /* free_multi_ssl_backend_data */
+  schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
   schannel_recv,                     /* recv decrypted data */
   schannel_send,                     /* send data to encrypt */
 };
diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h
index a128e04..fe7450d 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_int.h
+++ b/Utilities/cmcurl/lib/vtls/schannel_int.h
@@ -149,5 +149,22 @@
 #endif
 };
 
+struct schannel_multi_ssl_backend_data {
+  unsigned char *CAinfo_blob_digest; /* CA info blob digest */
+  size_t CAinfo_blob_size;           /* CA info blob size */
+  char *CAfile;                      /* CAfile path used to generate
+                                        certificate store */
+  HCERTSTORE cert_store;             /* cached certificate store or
+                                        NULL if none */
+  struct curltime time;              /* when the cached store was created */
+};
+
+HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
+                                               const struct Curl_easy *data);
+
+bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
+                                         const struct Curl_easy *data,
+                                         HCERTSTORE cert_store);
+
 #endif /* USE_SCHANNEL */
 #endif /* HEADER_CURL_SCHANNEL_INT_H */
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index a5d5c98..24146d0 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -172,7 +172,7 @@
           /* Sanity check that the cert_context object is the right type */
           if(CERT_QUERY_CONTENT_CERT != actual_content_type) {
             failf(data,
-                  "schannel: unexpected content type '%d' when extracting "
+                  "schannel: unexpected content type '%lu' when extracting "
                   "certificate from CA file '%s'",
                   actual_content_type, ca_file_text);
             result = CURLE_SSL_CACERT_BADFILE;
@@ -470,7 +470,7 @@
   CERT_CONTEXT *pCertContextServer = NULL;
   TCHAR *cert_hostname_buff = NULL;
   size_t cert_hostname_buff_index = 0;
-  const char *conn_hostname = connssl->hostname;
+  const char *conn_hostname = connssl->peer.hostname;
   size_t hostlen = strlen(conn_hostname);
   DWORD len = 0;
   DWORD actual_len = 0;
@@ -600,6 +600,7 @@
   const CERT_CHAIN_CONTEXT *pChainContext = NULL;
   HCERTCHAINENGINE cert_chain_engine = NULL;
   HCERTSTORE trust_store = NULL;
+  HCERTSTORE own_trust_store = NULL;
 
   DEBUGASSERT(BACKEND);
 
@@ -630,31 +631,46 @@
       result = CURLE_SSL_CACERT_BADFILE;
     }
     else {
-      /* Open the certificate store */
-      trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
-                                  0,
-                                  (HCRYPTPROV)NULL,
-                                  CERT_STORE_CREATE_NEW_FLAG,
-                                  NULL);
-      if(!trust_store) {
-        char buffer[STRERROR_LEN];
-        failf(data, "schannel: failed to create certificate store: %s",
-              Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
-        result = CURLE_SSL_CACERT_BADFILE;
+      /* try cache */
+      trust_store = Curl_schannel_get_cached_cert_store(cf, data);
+
+      if(trust_store) {
+        infof(data, "schannel: reusing certificate store from cache");
       }
       else {
-        const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
-        if(ca_info_blob) {
-          result = add_certs_data_to_store(trust_store,
-                                           (const char *)ca_info_blob->data,
-                                           ca_info_blob->len,
-                                           "(memory blob)",
-                                           data);
+        /* Open the certificate store */
+        trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
+                                    0,
+                                    (HCRYPTPROV)NULL,
+                                    CERT_STORE_CREATE_NEW_FLAG,
+                                    NULL);
+        if(!trust_store) {
+          char buffer[STRERROR_LEN];
+          failf(data, "schannel: failed to create certificate store: %s",
+                Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+          result = CURLE_SSL_CACERT_BADFILE;
         }
         else {
-          result = add_certs_file_to_store(trust_store,
-                                           conn_config->CAfile,
-                                           data);
+          const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+          own_trust_store = trust_store;
+
+          if(ca_info_blob) {
+            result = add_certs_data_to_store(trust_store,
+                                              (const char *)ca_info_blob->data,
+                                              ca_info_blob->len,
+                                              "(memory blob)",
+                                              data);
+          }
+          else {
+            result = add_certs_file_to_store(trust_store,
+                                              conn_config->CAfile,
+                                              data);
+          }
+          if(result == CURLE_OK) {
+            if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) {
+              own_trust_store = NULL;
+            }
+          }
         }
       }
     }
@@ -737,7 +753,7 @@
           failf(data, "schannel: CertGetCertificateChain trust error"
                 " CERT_TRUST_REVOCATION_STATUS_UNKNOWN");
         else
-          failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+          failf(data, "schannel: CertGetCertificateChain error mask: 0x%08lx",
                 dwTrustErrorMask);
         result = CURLE_PEER_FAILED_VERIFICATION;
       }
@@ -754,8 +770,8 @@
     CertFreeCertificateChainEngine(cert_chain_engine);
   }
 
-  if(trust_store) {
-    CertCloseStore(trust_store, 0);
+  if(own_trust_store) {
+    CertCloseStore(own_trust_store, 0);
   }
 
   if(pChainContext)
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index 3378f76..1f37305 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -46,8 +46,10 @@
 #endif /* __clang__ */
 
 #ifdef __GNUC__
+#pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Waddress"
 #pragma GCC diagnostic ignored "-Wundef"
+#pragma GCC diagnostic ignored "-Wunreachable-code"
 #endif
 
 #include <limits.h>
@@ -904,7 +906,6 @@
   return rtn;
 }
 
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
 CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
 {
   /* The first ciphers in the ciphertable are continuous. Here we do small
@@ -923,7 +924,6 @@
   }
   return ciphertable[SSL_NULL_WITH_NULL_NULL].name;
 }
-#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 
 #if CURL_BUILD_MAC
 CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
@@ -1013,7 +1013,7 @@
   }
   else {
     size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1;
-    cbuf = calloc(cbuf_size, 1);
+    cbuf = calloc(1, cbuf_size);
     if(cbuf) {
       if(!CFStringGetCString(c, cbuf, cbuf_size,
                              kCFStringEncodingUTF8)) {
@@ -1651,11 +1651,6 @@
   const bool verifypeer = conn_config->verifypeer;
   char * const ssl_cert = ssl_config->primary.clientcert;
   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif /* ENABLE_IPV6 */
   char *ciphers;
   OSStatus err = noErr;
 #if CURL_BUILD_MAC
@@ -2003,13 +1998,9 @@
    * Both hostname check and SNI require SSLSetPeerDomainName().
    * Also: the verifyhost setting influences SNI usage */
   if(conn_config->verifyhost) {
-    size_t snilen;
-    char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
-    if(!snihost) {
-      failf(data, "Failed to set SNI");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen);
+    char *server = connssl->peer.sni?
+                   connssl->peer.sni : connssl->peer.hostname;
+    err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server));
 
     if(err != noErr) {
       failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d",
@@ -2017,11 +2008,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
 
-    if((Curl_inet_pton(AF_INET, connssl->hostname, &addr))
-  #ifdef ENABLE_IPV6
-    || (Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
-  #endif
-       ) {
+    if(connssl->peer.is_ip_address) {
       infof(data, "WARNING: using IP address, SNI is being disabled by "
             "the OS.");
     }
@@ -2079,7 +2066,7 @@
       ssl_sessionid =
         aprintf("%s:%d:%d:%s:%d",
                 ssl_cafile ? ssl_cafile : "(blob memory)",
-                verifypeer, conn_config->verifyhost, connssl->hostname,
+                verifypeer, conn_config->verifyhost, connssl->peer.hostname,
                 connssl->port);
       ssl_sessionid_len = strlen(ssl_sessionid);
 
@@ -2380,19 +2367,15 @@
                             const struct curl_blob *ca_info_blob,
                             SSLContextRef ctx)
 {
-  int result;
+  CURLcode result;
   unsigned char *certbuf;
   size_t buflen;
+  bool free_certbuf = FALSE;
 
   if(ca_info_blob) {
     CURL_TRC_CF(data, cf, "verify_peer, CA from config blob");
-    certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
-    if(!certbuf) {
-      return CURLE_OUT_OF_MEMORY;
-    }
+    certbuf = ca_info_blob->data;
     buflen = ca_info_blob->len;
-    memcpy(certbuf, ca_info_blob->data, ca_info_blob->len);
-    certbuf[ca_info_blob->len]='\0';
   }
   else if(cafile) {
     CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile);
@@ -2400,12 +2383,14 @@
       failf(data, "SSL: failed to read or invalid CA certificate");
       return CURLE_SSL_CACERT_BADFILE;
     }
+    free_certbuf = TRUE;
   }
   else
     return CURLE_SSL_CACERT_BADFILE;
 
   result = verify_cert_buf(cf, data, certbuf, buflen, ctx);
-  free(certbuf);
+  if(free_certbuf)
+    free(certbuf);
   return result;
 }
 
@@ -2665,7 +2650,7 @@
          host name: */
       case errSSLHostNameMismatch:
         failf(data, "SSL certificate peer verification failed, the "
-              "certificate did not match \"%s\"\n", connssl->dispname);
+              "certificate did not match \"%s\"\n", connssl->peer.dispname);
         return CURLE_PEER_FAILED_VERIFICATION;
 
       /* Problem with SSL / TLS negotiation */
@@ -2757,7 +2742,7 @@
       default:
         /* May also return codes listed in Security Framework Result Codes */
         failf(data, "Unknown SSL protocol error in connection to %s:%d",
-              connssl->hostname, err);
+              connssl->peer.hostname, err);
         break;
     }
     return CURLE_SSL_CONNECT_ERROR;
@@ -3415,7 +3400,6 @@
         }
         *curlcode = CURLE_AGAIN;
         return -1L;
-        break;
 
       /* errSSLClosedGraceful - server gracefully shut down the SSL session
          errSSLClosedNoNotify - server hung up on us instead of sending a
@@ -3425,7 +3409,6 @@
       case errSSLClosedNoNotify:
         *curlcode = CURLE_OK;
         return 0;
-        break;
 
         /* The below is errSSLPeerAuthCompleted; it's not defined in
            Leopard's headers */
@@ -3445,7 +3428,6 @@
         failf(data, "SSLRead() return error %d", err);
         *curlcode = CURLE_RECV_ERROR;
         return -1L;
-        break;
     }
   }
   return (ssize_t)processed;
@@ -3483,7 +3465,7 @@
   Curl_none_cert_status_request,      /* cert_status_request */
   sectransp_connect,                  /* connect */
   sectransp_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,          /* getsock */
+  Curl_ssl_adjust_pollset,            /* adjust_pollset */
   sectransp_get_internals,            /* get_internals */
   sectransp_close,                    /* close_one */
   Curl_none_close_all,                /* close_all */
@@ -3500,6 +3482,10 @@
   sectransp_send,                     /* send data to encrypt */
 };
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 494b660..e928ba5 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -67,6 +67,7 @@
 #include "warnless.h"
 #include "curl_base64.h"
 #include "curl_printf.h"
+#include "inet_pton.h"
 #include "strdup.h"
 
 /* The last #include files should be: */
@@ -131,9 +132,6 @@
 }
 
 #ifdef USE_SSL
-static const struct alpn_spec ALPN_SPEC_H10 = {
-  { ALPN_HTTP_1_0 }, 1
-};
 static const struct alpn_spec ALPN_SPEC_H11 = {
   { ALPN_HTTP_1_1 }, 1
 };
@@ -147,51 +145,83 @@
 {
   if(!use_alpn)
     return NULL;
-  if(httpwant == CURL_HTTP_VERSION_1_0)
-    return &ALPN_SPEC_H10;
 #ifdef USE_HTTP2
   if(httpwant >= CURL_HTTP_VERSION_2)
     return &ALPN_SPEC_H2_H11;
+#else
+  (void)httpwant;
 #endif
+  /* Use the ALPN protocol "http/1.1" for HTTP/1.x.
+     Avoid "http/1.0" because some servers don't support it. */
   return &ALPN_SPEC_H11;
 }
 #endif /* USE_SSL */
 
 
-bool
-Curl_ssl_config_matches(struct ssl_primary_config *data,
-                        struct ssl_primary_config *needle)
+void Curl_ssl_easy_config_init(struct Curl_easy *data)
 {
-  if((data->version == needle->version) &&
-     (data->version_max == needle->version_max) &&
-     (data->ssl_options == needle->ssl_options) &&
-     (data->verifypeer == needle->verifypeer) &&
-     (data->verifyhost == needle->verifyhost) &&
-     (data->verifystatus == needle->verifystatus) &&
-     blobcmp(data->cert_blob, needle->cert_blob) &&
-     blobcmp(data->ca_info_blob, needle->ca_info_blob) &&
-     blobcmp(data->issuercert_blob, needle->issuercert_blob) &&
-     Curl_safecmp(data->CApath, needle->CApath) &&
-     Curl_safecmp(data->CAfile, needle->CAfile) &&
-     Curl_safecmp(data->issuercert, needle->issuercert) &&
-     Curl_safecmp(data->clientcert, needle->clientcert) &&
-#ifdef USE_TLS_SRP
-     !Curl_timestrcmp(data->username, needle->username) &&
-     !Curl_timestrcmp(data->password, needle->password) &&
+  /*
+   * libcurl 7.10 introduced SSL verification *by default*! This needs to be
+   * switched off unless wanted.
+   */
+  data->set.ssl.primary.verifypeer = TRUE;
+  data->set.ssl.primary.verifyhost = TRUE;
+  data->set.ssl.primary.sessionid = TRUE; /* session ID caching by default */
+#ifndef CURL_DISABLE_PROXY
+  data->set.proxy_ssl = data->set.ssl;
 #endif
-     strcasecompare(data->cipher_list, needle->cipher_list) &&
-     strcasecompare(data->cipher_list13, needle->cipher_list13) &&
-     strcasecompare(data->curves, needle->curves) &&
-     strcasecompare(data->CRLfile, needle->CRLfile) &&
-     strcasecompare(data->pinned_key, needle->pinned_key))
+}
+
+static bool
+match_ssl_primary_config(struct Curl_easy *data,
+                         struct ssl_primary_config *c1,
+                         struct ssl_primary_config *c2)
+{
+  (void)data;
+  if((c1->version == c2->version) &&
+     (c1->version_max == c2->version_max) &&
+     (c1->ssl_options == c2->ssl_options) &&
+     (c1->verifypeer == c2->verifypeer) &&
+     (c1->verifyhost == c2->verifyhost) &&
+     (c1->verifystatus == c2->verifystatus) &&
+     blobcmp(c1->cert_blob, c2->cert_blob) &&
+     blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
+     blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
+     Curl_safecmp(c1->CApath, c2->CApath) &&
+     Curl_safecmp(c1->CAfile, c2->CAfile) &&
+     Curl_safecmp(c1->issuercert, c2->issuercert) &&
+     Curl_safecmp(c1->clientcert, c2->clientcert) &&
+#ifdef USE_TLS_SRP
+     !Curl_timestrcmp(c1->username, c2->username) &&
+     !Curl_timestrcmp(c1->password, c2->password) &&
+#endif
+     strcasecompare(c1->cipher_list, c2->cipher_list) &&
+     strcasecompare(c1->cipher_list13, c2->cipher_list13) &&
+     strcasecompare(c1->curves, c2->curves) &&
+     strcasecompare(c1->CRLfile, c2->CRLfile) &&
+     strcasecompare(c1->pinned_key, c2->pinned_key))
     return TRUE;
 
   return FALSE;
 }
 
-bool
-Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
-                              struct ssl_primary_config *dest)
+bool Curl_ssl_conn_config_match(struct Curl_easy *data,
+                                struct connectdata *candidate,
+                                bool proxy)
+{
+#ifndef CURL_DISABLE_PROXY
+  if(proxy)
+    return match_ssl_primary_config(data, &data->set.proxy_ssl.primary,
+                                    &candidate->proxy_ssl_config);
+#else
+  (void)proxy;
+#endif
+  return match_ssl_primary_config(data, &data->set.ssl.primary,
+                                  &candidate->ssl_config);
+}
+
+static bool clone_ssl_primary_config(struct ssl_primary_config *source,
+                                     struct ssl_primary_config *dest)
 {
   dest->version = source->version;
   dest->version_max = source->version_max;
@@ -221,7 +251,7 @@
   return TRUE;
 }
 
-void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
+static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
 {
   Curl_safefree(sslc->CApath);
   Curl_safefree(sslc->CAfile);
@@ -241,6 +271,111 @@
 #endif
 }
 
+CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
+{
+  data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
+  data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
+  data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE];
+  data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+  data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
+  data->set.ssl.primary.cipher_list =
+    data->set.str[STRING_SSL_CIPHER_LIST];
+  data->set.ssl.primary.cipher_list13 =
+    data->set.str[STRING_SSL_CIPHER13_LIST];
+  data->set.ssl.primary.pinned_key =
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+  data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
+  data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO];
+  data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES];
+#ifdef USE_TLS_SRP
+  data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
+  data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
+#endif
+  data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
+  data->set.ssl.key = data->set.str[STRING_KEY];
+  data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
+  data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD];
+  data->set.ssl.primary.clientcert = data->set.str[STRING_CERT];
+  data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
+
+#ifndef CURL_DISABLE_PROXY
+  data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
+  data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
+  data->set.proxy_ssl.primary.cipher_list =
+    data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
+  data->set.proxy_ssl.primary.cipher_list13 =
+    data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
+  data->set.proxy_ssl.primary.pinned_key =
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
+  data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
+  data->set.proxy_ssl.primary.ca_info_blob =
+    data->set.blobs[BLOB_CAINFO_PROXY];
+  data->set.proxy_ssl.primary.issuercert =
+    data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+  data->set.proxy_ssl.primary.issuercert_blob =
+    data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
+  data->set.proxy_ssl.primary.CRLfile =
+    data->set.str[STRING_SSL_CRLFILE_PROXY];
+  data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
+  data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
+  data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
+  data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
+  data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY];
+  data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
+#ifdef USE_TLS_SRP
+  data->set.proxy_ssl.primary.username =
+    data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
+  data->set.proxy_ssl.primary.password =
+    data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
+#endif
+#endif /* CURL_DISABLE_PROXY */
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
+                                   struct connectdata *conn)
+{
+  /* Clone "primary" SSL configurations from the esay handle to
+   * the connection. They are used for connection cache matching and
+   * probably outlive the easy handle */
+  if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config))
+    return CURLE_OUT_OF_MEMORY;
+#ifndef CURL_DISABLE_PROXY
+  if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary,
+                               &conn->proxy_ssl_config))
+    return CURLE_OUT_OF_MEMORY;
+#endif
+  return CURLE_OK;
+}
+
+void Curl_ssl_conn_config_cleanup(struct connectdata *conn)
+{
+  Curl_free_primary_ssl_config(&conn->ssl_config);
+#ifndef CURL_DISABLE_PROXY
+  Curl_free_primary_ssl_config(&conn->proxy_ssl_config);
+#endif
+}
+
+void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy)
+{
+  /* May be called on an easy that has no connection yet */
+  if(data->conn) {
+    struct ssl_primary_config *src, *dest;
+#ifndef CURL_DISABLE_PROXY
+    src = for_proxy? &data->set.proxy_ssl.primary : &data->set.ssl.primary;
+    dest = for_proxy? &data->conn->proxy_ssl_config : &data->conn->ssl_config;
+#else
+    (void)for_proxy;
+    src = &data->set.ssl.primary;
+    dest = &data->conn->ssl_config;
+#endif
+    dest->verifyhost = src->verifyhost;
+    dest->verifypeer = src->verifypeer;
+    dest->verifystatus = src->verifystatus;
+  }
+}
+
 #ifdef USE_SSL
 static int multissl_setup(const struct Curl_ssl *backend);
 #endif
@@ -432,7 +567,7 @@
     if(!check->sessionid)
       /* not session ID means blank entry */
       continue;
-    if(strcasecompare(connssl->hostname, check->name) &&
+    if(strcasecompare(connssl->peer.hostname, check->name) &&
        ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
         (cf->conn->bits.conn_to_host && check->conn_to_host &&
          strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
@@ -441,7 +576,7 @@
          cf->conn->conn_to_port == check->conn_to_port)) &&
        (connssl->port == check->remote_port) &&
        strcasecompare(cf->conn->handler->scheme, check->scheme) &&
-       Curl_ssl_config_matches(conn_config, &check->ssl_config)) {
+       match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
       /* yes, we have a session ID! */
       (*general_age)++;          /* increase general age */
       check->age = *general_age; /* set this as used in this age */
@@ -456,7 +591,8 @@
   DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
                no_match? "Didn't find": "Found",
                Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
-               cf->conn->handler->scheme, connssl->hostname, connssl->port));
+               cf->conn->handler->scheme, connssl->peer.hostname,
+               connssl->port));
   return no_match;
 }
 
@@ -532,7 +668,7 @@
   (void)ssl_config;
   DEBUGASSERT(ssl_config->primary.sessionid);
 
-  clone_host = strdup(connssl->hostname);
+  clone_host = strdup(connssl->peer.hostname);
   if(!clone_host)
     return CURLE_OUT_OF_MEMORY; /* bail out */
 
@@ -590,7 +726,7 @@
   store->remote_port = connssl->port;
   store->scheme = cf->conn->handler->scheme;
 
-  if(!Curl_clone_primary_ssl_config(conn_config, &store->ssl_config)) {
+  if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
     Curl_free_primary_ssl_config(&store->ssl_config);
     store->sessionid = NULL; /* let caller free sessionid */
     free(clone_host);
@@ -629,22 +765,21 @@
   Curl_ssl->close_all(data);
 }
 
-int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              curl_socket_t *socks)
+void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
+                              struct easy_pollset *ps)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
-
-  if(sock == CURL_SOCKET_BAD)
-    return GETSOCK_BLANK;
-
-  if(connssl->connecting_state == ssl_connect_2_writing) {
-    /* we are only interested in writing */
-    socks[0] = sock;
-    return GETSOCK_WRITESOCK(0);
+  if(!cf->connected) {
+    struct ssl_connect_data *connssl = cf->ctx;
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+    if(sock != CURL_SOCKET_BAD) {
+      if(connssl->connecting_state == ssl_connect_2_writing) {
+        Curl_pollset_set_out_only(data, ps, sock);
+      }
+      else {
+        Curl_pollset_set_in_only(data, ps, sock);
+      }
+    }
   }
-  socks[0] = sock;
-  return GETSOCK_READSOCK(0);
 }
 
 /* Selects an SSL crypto engine
@@ -748,28 +883,21 @@
                                     size_t valuelen)
 {
   struct curl_certinfo *ci = &data->info.certs;
-  char *output;
   struct curl_slist *nl;
   CURLcode result = CURLE_OK;
-  size_t labellen = strlen(label);
-  size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
+  struct dynbuf build;
 
-  output = malloc(outlen);
-  if(!output)
+  Curl_dyn_init(&build, 10000);
+
+  if(Curl_dyn_add(&build, label) ||
+     Curl_dyn_addn(&build, ":", 1) ||
+     Curl_dyn_addn(&build, value, valuelen))
     return CURLE_OUT_OF_MEMORY;
 
-  /* sprintf the label and colon */
-  msnprintf(output, outlen, "%s:", label);
-
-  /* memcpy the value (it might not be null-terminated) */
-  memcpy(&output[labellen + 1], value, valuelen);
-
-  /* null-terminate the output */
-  output[labellen + 1 + valuelen] = 0;
-
-  nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
+  nl = Curl_slist_append_nodup(ci->certinfo[certnum],
+                               Curl_dyn_ptr(&build));
   if(!nl) {
-    free(output);
+    Curl_dyn_free(&build);
     curl_slist_free_all(ci->certinfo[certnum]);
     result = CURLE_OUT_OF_MEMORY;
   }
@@ -786,32 +914,6 @@
 }
 
 /*
- * Curl_ssl_snihost() converts the input host name to a suitable SNI name put
- * in data->state.buffer. Returns a pointer to the name (or NULL if a problem)
- * and stores the new length in 'olen'.
- *
- * SNI fields must not have any trailing dot and while RFC 6066 section 3 says
- * the SNI field is case insensitive, browsers always send the data lowercase
- * and subsequently there are numerous servers out there that don't work
- * unless the name is lowercased.
- */
-
-char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen)
-{
-  size_t len = strlen(host);
-  if(len && (host[len-1] == '.'))
-    len--;
-  if(len >= data->set.buffer_size)
-    return NULL;
-
-  Curl_strntolower(data->state.buffer, host, len);
-  data->state.buffer[len] = 0;
-  if(olen)
-    *olen = len;
-  return data->state.buffer;
-}
-
-/*
  * Public key pem to der conversion
  */
 
@@ -893,7 +995,7 @@
   /* only do this if pinnedpubkey starts with "sha256//", length 8 */
   if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
     CURLcode encode;
-    size_t encodedlen = 0, pinkeylen;
+    size_t encodedlen = 0;
     char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos;
     unsigned char *sha256sumdigest;
 
@@ -921,13 +1023,11 @@
     infof(data, " public key hash: sha256//%s", encoded);
 
     /* it starts with sha256//, copy so we can modify it */
-    pinkeylen = strlen(pinnedpubkey) + 1;
-    pinkeycopy = malloc(pinkeylen);
+    pinkeycopy = strdup(pinnedpubkey);
     if(!pinkeycopy) {
       Curl_safefree(encoded);
       return CURLE_OUT_OF_MEMORY;
     }
-    memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
     /* point begin_pos to the copy, and start extracting keys */
     begin_pos = pinkeycopy;
     do {
@@ -1156,13 +1256,13 @@
   return Curl_ssl->connect_nonblocking(cf, data, done);
 }
 
-static int multissl_get_select_socks(struct Curl_cfilter *cf,
+static void multissl_adjust_pollset(struct Curl_cfilter *cf,
                                      struct Curl_easy *data,
-                                     curl_socket_t *socks)
+                                     struct easy_pollset *ps)
 {
   if(multissl_setup(NULL))
-    return 0;
-  return Curl_ssl->get_select_socks(cf, data, socks);
+    return;
+  Curl_ssl->adjust_pollset(cf, data, ps);
 }
 
 static void *multissl_get_internals(struct ssl_connect_data *connssl,
@@ -1214,7 +1314,7 @@
   Curl_none_cert_status_request,     /* cert_status_request */
   multissl_connect,                  /* connect */
   multissl_connect_nonblocking,      /* connect_nonblocking */
-  multissl_get_select_socks,         /* getsock */
+  multissl_adjust_pollset,          /* adjust_pollset */
   multissl_get_internals,            /* get_internals */
   multissl_close,                    /* close_one */
   Curl_none_close_all,               /* close_all */
@@ -1313,17 +1413,13 @@
     backends_len = p - backends;
   }
 
-  if(!size)
-    return 0;
-
-  if(size <= backends_len) {
-    strncpy(buffer, backends, size - 1);
-    buffer[size - 1] = '\0';
-    return size - 1;
+  if(size) {
+    if(backends_len < size)
+      strcpy(buffer, backends);
+    else
+      *buffer = 0; /* did not fit */
   }
-
-  strcpy(buffer, backends);
-  return backends_len;
+  return 0;
 }
 
 static int multissl_setup(const struct Curl_ssl *backend)
@@ -1409,12 +1505,14 @@
 
 #ifdef USE_SSL
 
-static void free_hostname(struct ssl_connect_data *connssl)
+void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
 {
-  if(connssl->dispname != connssl->hostname)
-    free(connssl->dispname);
-  free(connssl->hostname);
-  connssl->hostname = connssl->dispname = NULL;
+  if(peer->dispname != peer->hostname)
+    free(peer->dispname);
+  free(peer->sni);
+  free(peer->hostname);
+  peer->hostname = peer->sni = peer->dispname = NULL;
+  peer->is_ip_address = FALSE;
 }
 
 static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -1423,12 +1521,26 @@
   if(connssl) {
     Curl_ssl->close(cf, data);
     connssl->state = ssl_connection_none;
-    free_hostname(connssl);
+    Curl_ssl_peer_cleanup(&connssl->peer);
   }
   cf->connected = FALSE;
 }
 
-static CURLcode reinit_hostname(struct Curl_cfilter *cf)
+static int is_ip_address(const char *hostname)
+{
+#ifdef ENABLE_IPV6
+  struct in6_addr addr;
+#else
+  struct in_addr addr;
+#endif
+  return (hostname && hostname[0] && (Curl_inet_pton(AF_INET, hostname, &addr)
+#ifdef ENABLE_IPV6
+          || Curl_inet_pton(AF_INET6, hostname, &addr)
+#endif
+         ));
+}
+
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   const char *ehostname, *edispname;
@@ -1454,23 +1566,43 @@
   }
 
   /* change if ehostname changed */
-  if(ehostname && (!connssl->hostname
-                   || strcmp(ehostname, connssl->hostname))) {
-    free_hostname(connssl);
-    connssl->hostname = strdup(ehostname);
-    if(!connssl->hostname) {
-      free_hostname(connssl);
+  if(ehostname && (!peer->hostname
+                   || strcmp(ehostname, peer->hostname))) {
+    Curl_ssl_peer_cleanup(peer);
+    peer->hostname = strdup(ehostname);
+    if(!peer->hostname) {
+      Curl_ssl_peer_cleanup(peer);
       return CURLE_OUT_OF_MEMORY;
     }
     if(!edispname || !strcmp(ehostname, edispname))
-      connssl->dispname = connssl->hostname;
+      peer->dispname = peer->hostname;
     else {
-      connssl->dispname = strdup(edispname);
-      if(!connssl->dispname) {
-        free_hostname(connssl);
+      peer->dispname = strdup(edispname);
+      if(!peer->dispname) {
+        Curl_ssl_peer_cleanup(peer);
         return CURLE_OUT_OF_MEMORY;
       }
     }
+
+    peer->sni = NULL;
+    peer->is_ip_address = is_ip_address(peer->hostname)? TRUE : FALSE;
+    if(peer->hostname[0] && !peer->is_ip_address) {
+      /* not an IP address, normalize according to RCC 6066 ch. 3,
+       * max len of SNI is 2^16-1, no trailing dot */
+      size_t len = strlen(peer->hostname);
+      if(len && (peer->hostname[len-1] == '.'))
+        len--;
+      if(len < USHRT_MAX) {
+        peer->sni = calloc(1, len + 1);
+        if(!peer->sni) {
+          Curl_ssl_peer_cleanup(peer);
+          return CURLE_OUT_OF_MEMORY;
+        }
+        Curl_strntolower(peer->sni, peer->hostname, len);
+        peer->sni[len] = 0;
+      }
+    }
+
   }
   connssl->port = eport;
   return CURLE_OK;
@@ -1525,7 +1657,7 @@
     goto out;
 
   *done = FALSE;
-  result = reinit_hostname(cf);
+  result = Curl_ssl_peer_init(&connssl->peer, cf);
   if(result)
     goto out;
 
@@ -1583,38 +1715,49 @@
 {
   struct cf_call_data save;
   ssize_t nread;
+  size_t ntotal = 0;
 
   CF_DATA_SAVE(save, cf, data);
   *err = CURLE_OK;
-  nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
-  if(nread > 0) {
-    DEBUGASSERT((size_t)nread <= len);
-  }
-  else if(nread == 0) {
-    /* eof */
+  /* Do receive until we fill the buffer somehwhat or EGAIN, error or EOF */
+  while(!ntotal || (len - ntotal) > (4*1024)) {
     *err = CURLE_OK;
+    nread = Curl_ssl->recv_plain(cf, data, buf + ntotal, len - ntotal, err);
+    if(nread < 0) {
+      if(*err == CURLE_AGAIN && ntotal > 0) {
+        /* we EAGAINed after having reed data, return the success amount */
+        *err = CURLE_OK;
+        break;
+      }
+      /* we have a an error to report */
+      goto out;
+    }
+    else if(nread == 0) {
+      /* eof */
+      break;
+    }
+    ntotal += (size_t)nread;
+    DEBUGASSERT((size_t)ntotal <= len);
   }
-  CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err);
+  nread = (ssize_t)ntotal;
+out:
+  CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
+              nread, *err);
   CF_DATA_RESTORE(cf, save);
   return nread;
 }
 
-static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
+static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
-                                   curl_socket_t *socks)
+                                   struct easy_pollset *ps)
 {
   struct cf_call_data save;
-  int fds = GETSOCK_BLANK;
 
-  if(!cf->next->connected) {
-    fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  }
-  else if(!cf->connected) {
+  if(!cf->connected) {
     CF_DATA_SAVE(save, cf, data);
-    fds = Curl_ssl->get_select_socks(cf, data, socks);
+    Curl_ssl->adjust_pollset(cf, data, ps);
     CF_DATA_RESTORE(cf, save);
   }
-  return fds;
 }
 
 static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
@@ -1705,7 +1848,7 @@
   ssl_cf_connect,
   ssl_cf_close,
   Curl_cf_def_get_host,
-  ssl_cf_get_select_socks,
+  ssl_cf_adjust_pollset,
   ssl_cf_data_pending,
   ssl_cf_send,
   ssl_cf_recv,
@@ -1715,6 +1858,8 @@
   ssl_cf_query,
 };
 
+#ifndef CURL_DISABLE_PROXY
+
 struct Curl_cftype Curl_cft_ssl_proxy = {
   "SSL-PROXY",
   CF_TYPE_SSL,
@@ -1723,7 +1868,7 @@
   ssl_cf_connect,
   ssl_cf_close,
   Curl_cf_def_get_host,
-  ssl_cf_get_select_socks,
+  ssl_cf_adjust_pollset,
   ssl_cf_data_pending,
   ssl_cf_send,
   ssl_cf_recv,
@@ -1733,6 +1878,8 @@
   Curl_cf_def_query,
 };
 
+#endif /* !CURL_DISABLE_PROXY */
+
 static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
                               struct Curl_easy *data,
                               struct connectdata *conn)
@@ -1837,6 +1984,20 @@
   return (Curl_ssl->supports & option)? TRUE : FALSE;
 }
 
+static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf)
+{
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_ssl)
+      return cf;
+#ifndef CURL_DISABLE_PROXY
+    if(cf->cft == &Curl_cft_ssl_proxy)
+      return cf;
+#endif
+  }
+  return NULL;
+}
+
+
 void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
                              CURLINFO info, int n)
 {
@@ -1844,8 +2005,8 @@
   (void)n;
   if(data->conn) {
     struct Curl_cfilter *cf;
-    /* get first filter in chain, if any is present */
-    cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
+    /* get first SSL filter in chain, if any is present */
+    cf = get_ssl_filter(data->conn->cfilter[sockindex]);
     if(cf) {
       struct cf_call_data save;
       CF_DATA_SAVE(save, cf, data);
@@ -1875,26 +2036,14 @@
   return result;
 }
 
-static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
-                                               int sockindex)
-{
-  struct Curl_cfilter *cf, *lowest_ssl_cf = NULL;
-
-  for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) {
-    if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) {
-      lowest_ssl_cf = cf;
-      if(cf->connected || (cf->next && cf->next->connected)) {
-        /* connected or about to start */
-        return cf;
-      }
-    }
-  }
-  return lowest_ssl_cf;
-}
-
 bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
 {
+#ifndef CURL_DISABLE_PROXY
   return (cf->cft == &Curl_cft_ssl_proxy);
+#else
+  (void)cf;
+  return FALSE;
+#endif
 }
 
 struct ssl_config_data *
@@ -1908,17 +2057,6 @@
 #endif
 }
 
-struct ssl_config_data *
-Curl_ssl_get_config(struct Curl_easy *data, int sockindex)
-{
-  struct Curl_cfilter *cf;
-
-  (void)data;
-  DEBUGASSERT(data->conn);
-  cf = get_ssl_cf_engaged(data->conn, sockindex);
-  return cf? Curl_ssl_cf_get_config(cf, data) : &data->set.ssl;
-}
-
 struct ssl_primary_config *
 Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf)
 {
@@ -1930,15 +2068,6 @@
 #endif
 }
 
-struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf)
-{
-  for(; cf; cf = cf->next) {
-    if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy)
-      return cf;
-  }
-  return NULL;
-}
-
 CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
                                 const struct alpn_spec *spec)
 {
@@ -2005,10 +2134,6 @@
        !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
       *palpn = CURL_HTTP_VERSION_1_1;
     }
-    else if(proto_len == ALPN_HTTP_1_0_LENGTH &&
-            !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) {
-      *palpn = CURL_HTTP_VERSION_1_0;
-    }
 #ifdef USE_HTTP2
     else if(proto_len == ALPN_H2_LENGTH &&
             !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 8ad1cf6..744bbf8 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -65,15 +65,54 @@
 #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
 #endif
 
-char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen);
-bool Curl_ssl_config_matches(struct ssl_primary_config *data,
-                             struct ssl_primary_config *needle);
-bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
-                                   struct ssl_primary_config *dest);
-void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
-
 curl_sslbackend Curl_ssl_backend(void);
 
+/**
+ * Init ssl config for a new easy handle.
+ */
+void Curl_ssl_easy_config_init(struct Curl_easy *data);
+
+/**
+ * Init the `data->set.ssl` and `data->set.proxy_ssl` for
+ * connection matching use.
+ */
+CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data);
+
+/**
+ * Init SSL configs (main + proxy) for a new connection from the easy handle.
+ */
+CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
+                                   struct connectdata *conn);
+
+/**
+ * Free allocated resources in SSL configs (main + proxy) for
+ * the given connection.
+ */
+void Curl_ssl_conn_config_cleanup(struct connectdata *conn);
+
+/**
+ * Return TRUE iff SSL configuration from `conn` is functionally the
+ * same as the one on `candidate`.
+ * @param proxy   match the proxy SSL config or the main one
+ */
+bool Curl_ssl_conn_config_match(struct Curl_easy *data,
+                                struct connectdata *candidate,
+                                bool proxy);
+
+/* Update certain connection SSL config flags after they have
+ * been changed on the easy handle. Will work for `verifypeer`,
+ * `verifyhost` and `verifystatus`. */
+void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
+
+/**
+ * Init SSL peer information for filter. Can be called repeatedly.
+ */
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf);
+/**
+ * Free all allocated data and reset peer information.
+ */
+void Curl_ssl_peer_cleanup(struct ssl_peer *peer);
+
 #ifdef USE_SSL
 int Curl_ssl_init(void);
 void Curl_ssl_cleanup(void);
@@ -160,18 +199,6 @@
 #endif /* !CURL_DISABLE_PROXY */
 
 /**
- * Get the SSL configuration that is used on the connection.
- * This returns NULL if no SSL is configured.
- * Otherwise it returns the config of the first (highest) one that is
- * either connected, in handshake or about to start
- * (e.g. all filters below it are connected). If SSL filters are present,
- * but neither can start operating, return the config of the lowest one
- * that will first come into effect when connecting.
- */
-struct ssl_config_data *Curl_ssl_get_config(struct Curl_easy *data,
-                                            int sockindex);
-
-/**
  * True iff the underlying SSL implementation supports the option.
  * Option is one of the defined SSLSUPP_* values.
  * `data` maybe NULL for the features of the default implementation.
@@ -188,8 +215,22 @@
 void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
                              CURLINFO info, int n);
 
+/**
+ * Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
+ */
+struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf,
+                                               struct Curl_easy *data);
+
+/**
+ * Get the primary config relevant for the filter from its connection.
+ */
+struct ssl_primary_config *
+  Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
+
 extern struct Curl_cftype Curl_cft_ssl;
+#ifndef CURL_DISABLE_PROXY
 extern struct Curl_cftype Curl_cft_ssl_proxy;
+#endif
 
 #else /* if not USE_SSL */
 
@@ -209,8 +250,9 @@
 #define Curl_ssl_get_internals(a,b,c,d) NULL
 #define Curl_ssl_supports(a,b) FALSE
 #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
-#define Curl_ssl_get_config(a,b) NULL
 #define Curl_ssl_cfilter_remove(a,b) CURLE_OK
+#define Curl_ssl_cf_get_config(a,b) NULL
+#define Curl_ssl_cf_get_primary_config(a) NULL
 #endif
 
 #endif /* HEADER_CURL_VTLS_H */
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h
index a6e4544..af7ae55 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_int.h
+++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -32,8 +32,6 @@
 /* see https://www.iana.org/assignments/tls-extensiontype-values/ */
 #define ALPN_HTTP_1_1_LENGTH 8
 #define ALPN_HTTP_1_1 "http/1.1"
-#define ALPN_HTTP_1_0_LENGTH 8
-#define ALPN_HTTP_1_0 "http/1.0"
 #define ALPN_H2_LENGTH 2
 #define ALPN_H2 "h2"
 #define ALPN_H3_LENGTH 2
@@ -70,14 +68,14 @@
 struct ssl_connect_data {
   ssl_connection_state state;
   ssl_connect_state connecting_state;
-  char *hostname;                   /* hostname for verification */
-  char *dispname;                   /* display version of hostname */
+  struct ssl_peer peer;
   const struct alpn_spec *alpn;     /* ALPN to use or NULL for none */
   void *backend;                    /* vtls backend specific props */
   struct cf_call_data call_data;    /* data handle used in current call */
   struct curltime handshake_done;   /* time when handshake finished */
   int port;                         /* remote port at origin */
   BIT(use_alpn);                    /* if ALPN shall be used in handshake */
+  BIT(reused_session);              /* session-ID was reused for this */
 };
 
 
@@ -118,14 +116,11 @@
                                   struct Curl_easy *data,
                                   bool *done);
 
-  /* If the SSL backend wants to read or write on this connection during a
-     handshake, set socks[0] to the connection's FIRSTSOCKET, and return
-     a bitmap indicating read or write with GETSOCK_WRITESOCK(0) or
-     GETSOCK_READSOCK(0). Otherwise return GETSOCK_BLANK.
-     Mandatory. */
-  int (*get_select_socks)(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          curl_socket_t *socks);
-
+  /* During handshake, adjust the pollset to include the socket
+   * for POLLOUT or POLLIN as needed.
+   * Mandatory. */
+  void (*adjust_pollset)(struct Curl_cfilter *cf, struct Curl_easy *data,
+                          struct easy_pollset *ps);
   void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
   void (*close)(struct Curl_cfilter *cf, struct Curl_easy *data);
   void (*close_all)(struct Curl_easy *data);
@@ -169,25 +164,8 @@
 CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
 struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
 bool Curl_none_false_start(void);
-int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              curl_socket_t *socks);
-
-/**
- * Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
- */
-struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf,
-                                               struct Curl_easy *data);
-
-/**
- * Get the primary config relevant for the filter from its connection.
- */
-struct ssl_primary_config *
-  Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf);
-
-/**
- * Get the first SSL filter in the chain starting with `cf`, or NULL.
- */
-struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf);
+void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
+                              struct easy_pollset *ps);
 
 /**
  * Get the SSL filter below the given one or NULL if there is none.
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index b1384a6..a3c017c 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -480,6 +480,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
 #endif
+  default:
     break;
   }
 
@@ -513,7 +514,7 @@
     }
   }
 
-#ifndef NO_FILESYSTEM
+#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
   /* load native CA certificates */
   if(ssl_config->native_ca_store) {
     if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) {
@@ -582,12 +583,25 @@
   if(ssl_config->primary.clientcert && ssl_config->key) {
     int file_type = do_file_type(ssl_config->cert_type);
 
-    if(wolfSSL_CTX_use_certificate_file(backend->ctx,
-                                        ssl_config->primary.clientcert,
-                                        file_type) != 1) {
-      failf(data, "unable to use client certificate (no key or wrong pass"
-            " phrase?)");
-      return CURLE_SSL_CONNECT_ERROR;
+    if(file_type == WOLFSSL_FILETYPE_PEM) {
+      if(wolfSSL_CTX_use_certificate_chain_file(backend->ctx,
+                                                ssl_config->primary.clientcert)
+         != 1) {
+        failf(data, "unable to use client certificate");
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+    }
+    else if(file_type == WOLFSSL_FILETYPE_ASN1) {
+      if(wolfSSL_CTX_use_certificate_file(backend->ctx,
+                                          ssl_config->primary.clientcert,
+                                          file_type) != 1) {
+        failf(data, "unable to use client certificate");
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+    }
+    else {
+      failf(data, "unknown cert type");
+      return CURLE_BAD_FUNCTION_ARGUMENT;
     }
 
     file_type = do_file_type(ssl_config->key_type);
@@ -608,24 +622,12 @@
                          SSL_VERIFY_NONE, NULL);
 
 #ifdef HAVE_SNI
-  if(sni) {
-    struct in_addr addr4;
-#ifdef ENABLE_IPV6
-    struct in6_addr addr6;
-#endif
-    size_t hostname_len = strlen(connssl->hostname);
-
-    if((hostname_len < USHRT_MAX) &&
-       !Curl_inet_pton(AF_INET, connssl->hostname, &addr4)
-#ifdef ENABLE_IPV6
-       && !Curl_inet_pton(AF_INET6, connssl->hostname, &addr6)
-#endif
-      ) {
-      size_t snilen;
-      char *snihost = Curl_ssl_snihost(data, connssl->hostname, &snilen);
-      if(!snihost ||
-         wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost,
-                            (unsigned short)snilen) != 1) {
+  if(sni && connssl->peer.sni) {
+    size_t sni_len = strlen(connssl->peer.sni);
+    if((sni_len < USHRT_MAX)) {
+      if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
+                            connssl->peer.sni,
+                            (unsigned short)sni_len) != 1) {
         failf(data, "Failed to set SNI");
         return CURLE_SSL_CONNECT_ERROR;
       }
@@ -763,9 +765,9 @@
 
   /* Enable RFC2818 checks */
   if(conn_config->verifyhost) {
-    char *snihost = Curl_ssl_snihost(data, connssl->hostname, NULL);
-    if(!snihost ||
-       (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE))
+    char *snihost = connssl->peer.sni?
+                    connssl->peer.sni : connssl->peer.hostname;
+    if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)
       return CURLE_SSL_CONNECT_ERROR;
   }
 
@@ -813,7 +815,7 @@
     else if(DOMAIN_NAME_MISMATCH == detail) {
 #if 1
       failf(data, " subject alt name(s) or common name do not match \"%s\"",
-            connssl->dispname);
+            connssl->peer.dispname);
       return CURLE_PEER_FAILED_VERIFICATION;
 #else
       /* When the wolfssl_check_domain_name() is used and you desire to
@@ -1095,9 +1097,7 @@
       *curlcode = CURLE_OK;
       return 0;
     case SSL_ERROR_NONE:
-      /* FALLTHROUGH */
     case SSL_ERROR_WANT_READ:
-      /* FALLTHROUGH */
     case SSL_ERROR_WANT_WRITE:
       /* there's data pending, re-invoke wolfSSL_read() */
       CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
@@ -1398,7 +1398,7 @@
   Curl_none_cert_status_request,   /* cert_status_request */
   wolfssl_connect,                 /* connect */
   wolfssl_connect_nonblocking,     /* connect_nonblocking */
-  Curl_ssl_get_select_socks,                /* getsock */
+  Curl_ssl_adjust_pollset,         /* adjust_pollset */
   wolfssl_get_internals,           /* get_internals */
   wolfssl_close,                   /* close_one */
   Curl_none_close_all,             /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c
index c3fd3a3..da07936 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.c
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -97,6 +97,11 @@
 #define CURL_ASN1_CHARACTER_STRING      29
 #define CURL_ASN1_BMP_STRING            30
 
+/* Max sixes */
+
+#define MAX_X509_STR  10000
+#define MAX_X509_CERT 100000
+
 #ifdef WANT_EXTRACT_CERTINFO
 /* ASN.1 OID table entry. */
 struct Curl_OID {
@@ -255,61 +260,61 @@
 }
 
 /*
- * Convert an ASN.1 Boolean value into its string representation.  Return the
- * dynamically allocated string, or NULL if source is not an ASN.1 Boolean
- * value.
+ * Convert an ASN.1 Boolean value into its string representation.
+ *
+ * Return error code.
  */
 
-static const char *bool2str(const char *beg, const char *end)
+static CURLcode bool2str(struct dynbuf *store,
+                         const char *beg, const char *end)
 {
   if(end - beg != 1)
-    return NULL;
-  return strdup(*beg? "TRUE": "FALSE");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  return Curl_dyn_add(store, *beg? "TRUE": "FALSE");
 }
 
 /*
  * Convert an ASN.1 octet string to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
  */
-static const char *octet2str(const char *beg, const char *end)
+static CURLcode octet2str(struct dynbuf *store,
+                          const char *beg, const char *end)
 {
-  struct dynbuf buf;
-  CURLcode result;
-
-  Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1);
-  result = Curl_dyn_addn(&buf, "", 0);
+  CURLcode result = CURLE_OK;
 
   while(!result && beg < end)
-    result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+    result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
 
-  return Curl_dyn_ptr(&buf);
+  return result;
 }
 
-static const char *bit2str(const char *beg, const char *end)
+static CURLcode bit2str(struct dynbuf *store,
+                        const char *beg, const char *end)
 {
-  /* Convert an ASN.1 bit string to a printable string.
-     Return the dynamically allocated string, or NULL if an error occurs. */
+  /* Convert an ASN.1 bit string to a printable string. */
 
   if(++beg > end)
-    return NULL;
-  return octet2str(beg, end);
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  return octet2str(store, beg, end);
 }
 
 /*
  * Convert an ASN.1 integer value into its string representation.
- * Return the dynamically allocated string, or NULL if source is not an
- * ASN.1 integer value.
+ *
+ * Returns error.
  */
-static const char *int2str(const char *beg, const char *end)
+static CURLcode int2str(struct dynbuf *store,
+                        const char *beg, const char *end)
 {
   unsigned int val = 0;
   size_t n = end - beg;
 
   if(!n)
-    return NULL;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 
   if(n > 4)
-    return octet2str(beg, end);
+    return octet2str(store, beg, end);
 
   /* Represent integers <= 32-bit as a single value. */
   if(*beg & 0x80)
@@ -318,25 +323,24 @@
   do
     val = (val << 8) | *(const unsigned char *) beg++;
   while(beg < end);
-  return curl_maprintf("%s%x", val >= 10? "0x": "", val);
+  return Curl_dyn_addf(store, "%s%x", val >= 10? "0x": "", val);
 }
 
 /*
- * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the
- * destination buffer dynamically. The allocation size will normally be too
- * large: this is to avoid buffer overflows.
- * Terminate the string with a nul byte and return the converted
- * string length.
+ * Convert from an ASN.1 typed string to UTF8.
+ *
+ * The result is stored in a dynbuf that is inited by the user of this
+ * function.
+ *
+ * Returns error.
  */
-static ssize_t
-utf8asn1str(char **to, int type, const char *from, const char *end)
+static CURLcode
+utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
 {
   size_t inlength = end - from;
   int size = 1;
-  size_t outlength;
-  char *buf;
+  CURLcode result = CURLE_OK;
 
-  *to = NULL;
   switch(type) {
   case CURL_ASN1_BMP_STRING:
     size = 2;
@@ -352,133 +356,85 @@
   case CURL_ASN1_UTF8_STRING:
     break;
   default:
-    return -1;  /* Conversion not supported. */
+    return CURLE_BAD_FUNCTION_ARGUMENT;  /* Conversion not supported. */
   }
 
   if(inlength % size)
-    return -1;  /* Length inconsistent with character size. */
-  if(inlength / size > (SIZE_T_MAX - 1) / 4)
-    return -1;  /* Too big. */
-  buf = malloc(4 * (inlength / size) + 1);
-  if(!buf)
-    return -1;  /* Not enough memory. */
+    /* Length inconsistent with character size. */
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 
   if(type == CURL_ASN1_UTF8_STRING) {
     /* Just copy. */
-    outlength = inlength;
-    if(outlength)
-      memcpy(buf, from, outlength);
+    if(inlength)
+      result = Curl_dyn_addn(to, from, inlength);
   }
   else {
-    for(outlength = 0; from < end;) {
-      int charsize;
-      unsigned int wc;
+    while(!result && (from < end)) {
+      char buf[4]; /* decode buffer */
+      int charsize = 1;
+      unsigned int wc = 0;
 
-      wc = 0;
       switch(size) {
       case 4:
         wc = (wc << 8) | *(const unsigned char *) from++;
         wc = (wc << 8) | *(const unsigned char *) from++;
-        /* FALLTHROUGH */
+        FALLTHROUGH();
       case 2:
         wc = (wc << 8) | *(const unsigned char *) from++;
-        /* FALLTHROUGH */
+        FALLTHROUGH();
       default: /* case 1: */
         wc = (wc << 8) | *(const unsigned char *) from++;
       }
-      charsize = 1;
       if(wc >= 0x00000080) {
         if(wc >= 0x00000800) {
           if(wc >= 0x00010000) {
             if(wc >= 0x00200000) {
               free(buf);
-              return -1;        /* Invalid char. size for target encoding. */
+              /* Invalid char. size for target encoding. */
+              return CURLE_WEIRD_SERVER_REPLY;
             }
-            buf[outlength + 3] = (char) (0x80 | (wc & 0x3F));
+            buf[3] = (char) (0x80 | (wc & 0x3F));
             wc = (wc >> 6) | 0x00010000;
             charsize++;
           }
-          buf[outlength + 2] = (char) (0x80 | (wc & 0x3F));
+          buf[2] = (char) (0x80 | (wc & 0x3F));
           wc = (wc >> 6) | 0x00000800;
           charsize++;
         }
-        buf[outlength + 1] = (char) (0x80 | (wc & 0x3F));
+        buf[1] = (char) (0x80 | (wc & 0x3F));
         wc = (wc >> 6) | 0x000000C0;
         charsize++;
       }
-      buf[outlength] = (char) wc;
-      outlength += charsize;
+      buf[0] = (char) wc;
+      result = Curl_dyn_addn(to, buf, charsize);
     }
   }
-  buf[outlength] = '\0';
-  *to = buf;
-  return outlength;
-}
-
-/*
- * Convert an ASN.1 String into its UTF-8 string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
- */
-static const char *string2str(int type, const char *beg, const char *end)
-{
-  char *buf;
-  if(utf8asn1str(&buf, type, beg, end) < 0)
-    return NULL;
-  return buf;
-}
-
-/*
- * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at
- * buf.  Return the total number of encoded digits, even if larger than
- * `buflen'.
- */
-static size_t encodeUint(char *buf, size_t buflen, unsigned int x)
-{
-  size_t i = 0;
-  unsigned int y = x / 10;
-
-  if(y) {
-    i = encodeUint(buf, buflen, y);
-    x -= y * 10;
-  }
-  if(i < buflen)
-    buf[i] = (char) ('0' + x);
-  i++;
-  if(i < buflen)
-    buf[i] = '\0';      /* Store a terminator if possible. */
-  return i;
+  return result;
 }
 
 /*
  * Convert an ASN.1 OID into its dotted string representation.
- * Store the result in th `n'-byte buffer at `buf'.
- * Return the converted string length, or 0 on errors.
+ *
+ * Return error code.
  */
-static size_t encodeOID(char *buf, size_t buflen,
-                        const char *beg, const char *end)
+static CURLcode encodeOID(struct dynbuf *store,
+                          const char *beg, const char *end)
 {
-  size_t i;
   unsigned int x;
   unsigned int y;
+  CURLcode result = CURLE_OK;
 
   /* Process the first two numbers. */
   y = *(const unsigned char *) beg++;
   x = y / 40;
   y -= x * 40;
-  i = encodeUint(buf, buflen, x);
-  if(i < buflen)
-    buf[i] = '.';
-  i++;
-  if(i >= buflen)
-    i += encodeUint(NULL, 0, y);
-  else
-    i += encodeUint(buf + i, buflen - i, y);
+
+  result = Curl_dyn_addf(store, "%u.%u", x, y);
+  if(result)
+    return result;
 
   /* Process the trailing numbers. */
   while(beg < end) {
-    if(i < buflen)
-      buf[i] = '.';
-    i++;
     x = 0;
     do {
       if(x & 0xFF000000)
@@ -486,46 +442,42 @@
       y = *(const unsigned char *) beg++;
       x = (x << 7) | (y & 0x7F);
     } while(y & 0x80);
-    if(i >= buflen)
-      i += encodeUint(NULL, 0, x);
-    else
-      i += encodeUint(buf + i, buflen - i, x);
+    result = Curl_dyn_addf(store, ".%u", x);
   }
-  if(i < buflen)
-    buf[i] = '\0';
-  return i;
+  return result;
 }
 
 /*
  * Convert an ASN.1 OID into its dotted or symbolic string representation.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error code.
  */
 
-static const char *OID2str(const char *beg, const char *end, bool symbolic)
+static CURLcode OID2str(struct dynbuf *store,
+                        const char *beg, const char *end, bool symbolic)
 {
-  char *buf = NULL;
+  CURLcode result = CURLE_OK;
   if(beg < end) {
-    size_t buflen = encodeOID(NULL, 0, beg, end);
-    if(buflen) {
-      buf = malloc(buflen + 1); /* one extra for the zero byte */
-      if(buf) {
-        encodeOID(buf, buflen, beg, end);
-        buf[buflen] = '\0';
+    if(symbolic) {
+      struct dynbuf buf;
+      Curl_dyn_init(&buf, MAX_X509_STR);
+      result = encodeOID(&buf, beg, end);
 
-        if(symbolic) {
-          const struct Curl_OID *op = searchOID(buf);
-          if(op) {
-            free(buf);
-            buf = strdup(op->textoid);
-          }
-        }
+      if(!result) {
+        const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+        if(op)
+          result = Curl_dyn_add(store, op->textoid);
+        Curl_dyn_free(&buf);
       }
     }
+    else
+      result = encodeOID(store, beg, end);
   }
-  return buf;
+  return result;
 }
 
-static const char *GTime2str(const char *beg, const char *end)
+static CURLcode GTime2str(struct dynbuf *store,
+                          const char *beg, const char *end)
 {
   const char *tzp;
   const char *fracp;
@@ -548,12 +500,12 @@
     break;
   case 2:
     sec1 = fracp[-2];
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case 1:
     sec2 = fracp[-1];
     break;
   default:
-    return NULL;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
   /* Scan for timezone, measure fractional seconds. */
@@ -582,7 +534,8 @@
   }
 
   tzl = end - tzp;
-  return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+  return Curl_dyn_addf(store,
+                       "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
                        beg, beg + 4, beg + 6,
                        beg + 8, beg + 10, sec1, sec2,
                        fracl? ".": "", (int)fracl, fracp,
@@ -590,10 +543,12 @@
 }
 
 /*
- *  Convert an ASN.1 UTC time to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Convert an ASN.1 UTC time to a printable string.
+ *
+ * Return error code.
  */
-static const char *UTime2str(const char *beg, const char *end)
+static CURLcode UTime2str(struct dynbuf *store,
+                             const char *beg, const char *end)
 {
   const char *tzp;
   size_t tzl;
@@ -606,15 +561,16 @@
   switch(tzp - sec) {
   case 0:
     sec = "00";
+    FALLTHROUGH();
   case 2:
     break;
   default:
-    return NULL;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
   /* Process timezone. */
   if(tzp >= end)
-    return NULL;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   if(*tzp == 'Z') {
     tzp = "GMT";
     end = tzp + 3;
@@ -623,7 +579,7 @@
     tzp++;
 
   tzl = end - tzp;
-  return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+  return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
                        20 - (*beg >= '5'), beg, beg + 2, beg + 4,
                        beg + 6, beg + 8, sec,
                        (int)tzl, tzp);
@@ -631,34 +587,45 @@
 
 /*
  * Convert an ASN.1 element to a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ *
+ * Return error
  */
-static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
+static CURLcode ASN1tostr(struct dynbuf *store,
+                          struct Curl_asn1Element *elem, int type)
 {
+  CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
   if(elem->constructed)
-    return NULL; /* No conversion of structured elements. */
+    return CURLE_OK; /* No conversion of structured elements. */
 
   if(!type)
     type = elem->tag;   /* Type not forced: use element tag as type. */
 
   switch(type) {
   case CURL_ASN1_BOOLEAN:
-    return bool2str(elem->beg, elem->end);
+    result = bool2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_INTEGER:
   case CURL_ASN1_ENUMERATED:
-    return int2str(elem->beg, elem->end);
+    result = int2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_BIT_STRING:
-    return bit2str(elem->beg, elem->end);
+    result = bit2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_OCTET_STRING:
-    return octet2str(elem->beg, elem->end);
+    result = octet2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_NULL:
-    return strdup("");
+    result = Curl_dyn_addn(store, "", 1);
+    break;
   case CURL_ASN1_OBJECT_IDENTIFIER:
-    return OID2str(elem->beg, elem->end, TRUE);
+    result = OID2str(store, elem->beg, elem->end, TRUE);
+    break;
   case CURL_ASN1_UTC_TIME:
-    return UTime2str(elem->beg, elem->end);
+    result = UTime2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_GENERALIZED_TIME:
-    return GTime2str(elem->beg, elem->end);
+    result = GTime2str(store, elem->beg, elem->end);
+    break;
   case CURL_ASN1_UTF8_STRING:
   case CURL_ASN1_NUMERIC_STRING:
   case CURL_ASN1_PRINTABLE_STRING:
@@ -667,87 +634,96 @@
   case CURL_ASN1_VISIBLE_STRING:
   case CURL_ASN1_UNIVERSAL_STRING:
   case CURL_ASN1_BMP_STRING:
-    return string2str(type, elem->beg, elem->end);
+    result = utf8asn1str(store, type, elem->beg, elem->end);
+    break;
   }
 
-  return NULL;   /* Unsupported. */
+  return result;
 }
 
 /*
- * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
- * `buf'.
+ * ASCII encode distinguished name at `dn' into the store dynbuf.
  *
- * Returns the total string length, even if larger than `buflen' or -1 on
- * error.
+ * Returns error.
  */
-static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
+static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
 {
   struct Curl_asn1Element rdn;
   struct Curl_asn1Element atv;
   struct Curl_asn1Element oid;
   struct Curl_asn1Element value;
-  size_t l = 0;
   const char *p1;
   const char *p2;
   const char *p3;
   const char *str;
+  CURLcode result = CURLE_OK;
+  bool added = FALSE;
+  struct dynbuf temp;
+  Curl_dyn_init(&temp, MAX_X509_STR);
 
   for(p1 = dn->beg; p1 < dn->end;) {
     p1 = getASN1Element(&rdn, p1, dn->end);
-    if(!p1)
-      return -1;
+    if(!p1) {
+      result = CURLE_BAD_FUNCTION_ARGUMENT;
+      goto error;
+    }
     for(p2 = rdn.beg; p2 < rdn.end;) {
       p2 = getASN1Element(&atv, p2, rdn.end);
-      if(!p2)
-        return -1;
+      if(!p2) {
+        result = CURLE_BAD_FUNCTION_ARGUMENT;
+        goto error;
+      }
       p3 = getASN1Element(&oid, atv.beg, atv.end);
-      if(!p3)
-        return -1;
-      if(!getASN1Element(&value, p3, atv.end))
-        return -1;
-      str = ASN1tostr(&oid, 0);
-      if(!str)
-        return -1;
+      if(!p3) {
+        result = CURLE_BAD_FUNCTION_ARGUMENT;
+        goto error;
+      }
+      if(!getASN1Element(&value, p3, atv.end)) {
+        result = CURLE_BAD_FUNCTION_ARGUMENT;
+        goto error;
+      }
+      Curl_dyn_reset(&temp);
+      result = ASN1tostr(&temp, &oid, 0);
+      if(result)
+        goto error;
+
+      str = Curl_dyn_ptr(&temp);
 
       /* Encode delimiter.
          If attribute has a short uppercase name, delimiter is ", ". */
-      if(l) {
-        for(p3 = str; ISUPPER(*p3); p3++)
-          ;
-        for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
-          if(l < buflen)
-            buf[l] = *p3;
-          l++;
-        }
+      for(p3 = str; ISUPPER(*p3); p3++)
+        ;
+      if(added) {
+        if(p3 - str > 2)
+          result = Curl_dyn_addn(store, "/", 1);
+        else
+          result = Curl_dyn_addn(store, ", ", 2);
+        if(result)
+          goto error;
       }
 
       /* Encode attribute name. */
-      for(p3 = str; *p3; p3++) {
-        if(l < buflen)
-          buf[l] = *p3;
-        l++;
-      }
-      free((char *) str);
+      result = Curl_dyn_add(store, str);
+      if(result)
+        goto error;
 
       /* Generate equal sign. */
-      if(l < buflen)
-        buf[l] = '=';
-      l++;
+      result = Curl_dyn_addn(store, "=", 1);
+      if(result)
+        goto error;
 
       /* Generate value. */
-      str = ASN1tostr(&value, 0);
-      if(!str)
-        return -1;
-      for(p3 = str; *p3; p3++) {
-        if(l < buflen)
-          buf[l] = *p3;
-        l++;
-      }
-      free((char *) str);
+      result = ASN1tostr(store, &value, 0);
+      if(result)
+        goto error;
+      Curl_dyn_reset(&temp);
+      added = TRUE; /* use separator for next */
     }
   }
+error:
+  Curl_dyn_free(&temp);
 
-  return l;
+  return result;
 }
 
 #endif /* WANT_EXTRACT_CERTINFO */
@@ -876,25 +852,9 @@
 
 #ifdef WANT_EXTRACT_CERTINFO
 
-/*
- * Copy at most 64-characters, terminate with a newline and returns the
- * effective number of stored characters.
- */
-static size_t copySubstring(char *to, const char *from)
-{
-  size_t i;
-  for(i = 0; i < 64; i++) {
-    to[i] = *from;
-    if(!*from++)
-      break;
-  }
-
-  to[i++] = '\n';
-  return i;
-}
-
-static const char *dumpAlgo(struct Curl_asn1Element *param,
-                            const char *beg, const char *end)
+static CURLcode dumpAlgo(struct dynbuf *store,
+                         struct Curl_asn1Element *param,
+                         const char *beg, const char *end)
 {
   struct Curl_asn1Element oid;
 
@@ -902,14 +862,16 @@
 
   beg = getASN1Element(&oid, beg, end);
   if(!beg)
-    return NULL;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   param->header = NULL;
   param->tag = 0;
   param->beg = param->end = end;
-  if(beg < end)
-    if(!getASN1Element(param, beg, end))
-      return NULL;
-  return OID2str(oid.beg, oid.end, TRUE);
+  if(beg < end) {
+    const char *p = getASN1Element(param, beg, end);
+    if(!p)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  return OID2str(store, oid.beg, oid.end, TRUE);
 }
 
 /*
@@ -926,24 +888,47 @@
   return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
 }
 
-/* return 0 on success, 1 on error */
-static int do_pubkey_field(struct Curl_easy *data, int certnum,
-                           const char *label, struct Curl_asn1Element *elem)
+/*
+ * This is a convenience function for push_certinfo_len that takes a
+ * dynbuf value.
+ *
+ * It also does the verbose output if !certnum.
+ */
+static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
+                                      int certnum,
+                                      const char *label,
+                                      struct dynbuf *ptr)
 {
-  const char *output;
-  CURLcode result = CURLE_OK;
+  size_t valuelen = Curl_dyn_len(ptr);
+  char *value = Curl_dyn_ptr(ptr);
+
+  CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
+                                               value, valuelen);
+
+  if(!certnum && !result)
+    infof(data, "   %s: %s", label, value);
+
+  return result;
+}
+
+static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
+                                const char *label,
+                                struct Curl_asn1Element *elem)
+{
+  CURLcode result;
+  struct dynbuf out;
+
+  Curl_dyn_init(&out, MAX_X509_STR);
 
   /* Generate a certificate information record for the public key. */
 
-  output = ASN1tostr(elem, 0);
-  if(output) {
+  result = ASN1tostr(&out, elem, 0);
+  if(!result) {
     if(data->set.ssl.certinfo)
-      result = ssl_push_certinfo(data, certnum, label, output);
-    if(!certnum && !result)
-      infof(data, "   %s: %s", label, output);
-    free((char *) output);
+      result = ssl_push_certinfo_dyn(data, certnum, label, &out);
+    Curl_dyn_free(&out);
   }
-  return result ? 1 : 0;
+  return result;
 }
 
 /* return 0 on success, 1 on error */
@@ -964,7 +949,7 @@
      */
     const size_t len = ((pubkey->end - pubkey->beg - 2) * 4);
     if(!certnum)
-      infof(data, "   ECC Public Key (%lu bits)", len);
+      infof(data, "   ECC Public Key (%zu bits)", len);
     if(data->set.ssl.certinfo) {
       char q[sizeof(len) * 8 / 3 + 1];
       (void)msnprintf(q, sizeof(q), "%zu", len);
@@ -998,7 +983,7 @@
     if(len > 32)
       elem.beg = q;     /* Strip leading zero bytes. */
     if(!certnum)
-      infof(data, "   RSA Public Key (%lu bits)", len);
+      infof(data, "   RSA Public Key (%zu bits)", len);
     if(data->set.ssl.certinfo) {
       char r[sizeof(len) * 8 / 3 + 1];
       msnprintf(r, sizeof(r), "%zu", len);
@@ -1049,24 +1034,12 @@
 
 /*
  * Convert an ASN.1 distinguished name into a printable string.
- * Return the dynamically allocated string, or NULL if an error occurs.
+ * Return error.
  */
-static const char *DNtostr(struct Curl_asn1Element *dn)
+static CURLcode DNtostr(struct dynbuf *store,
+                        struct Curl_asn1Element *dn)
 {
-  char *buf = NULL;
-  ssize_t buflen = encodeDN(NULL, 0, dn);
-
-  if(buflen >= 0) {
-    buf = malloc(buflen + 1);
-    if(buf) {
-      if(encodeDN(buf, buflen + 1, dn) == -1) {
-        free(buf);
-        return NULL;
-      }
-      buf[buflen] = '\0';
-    }
-  }
-  return buf;
+  return encodeDN(store, dn);
 }
 
 CURLcode Curl_extract_certinfo(struct Curl_easy *data,
@@ -1076,19 +1049,19 @@
 {
   struct Curl_X509certificate cert;
   struct Curl_asn1Element param;
-  const char *ccp;
-  char *cp1;
-  size_t cl1;
-  char *cp2;
+  char *certptr;
+  size_t clen;
+  struct dynbuf out;
   CURLcode result = CURLE_OK;
   unsigned int version;
-  size_t i;
-  size_t j;
+  const char *ptr;
+  int rc;
 
   if(!data->set.ssl.certinfo)
     if(certnum)
       return CURLE_OK;
 
+  Curl_dyn_init(&out, MAX_X509_STR);
   /* Prepare the certificate information for curl_easy_getinfo(). */
 
   /* Extract the certificate ASN.1 elements. */
@@ -1096,135 +1069,126 @@
     return CURLE_PEER_FAILED_VERIFICATION;
 
   /* Subject. */
-  ccp = DNtostr(&cert.subject);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
+  result = DNtostr(&out, &cert.subject);
+  if(result)
+    goto done;
   if(data->set.ssl.certinfo) {
-    result = ssl_push_certinfo(data, certnum, "Subject", ccp);
+    result = ssl_push_certinfo_dyn(data, certnum, "Subject", &out);
     if(result)
-      return result;
+      goto done;
   }
-  if(!certnum)
-    infof(data, "%2d Subject: %s", certnum, ccp);
-  free((char *) ccp);
+  Curl_dyn_reset(&out);
 
   /* Issuer. */
-  ccp = DNtostr(&cert.issuer);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo) {
-    result = ssl_push_certinfo(data, certnum, "Issuer", ccp);
-  }
-  if(!certnum)
-    infof(data, "   Issuer: %s", ccp);
-  free((char *) ccp);
+  result = DNtostr(&out, &cert.issuer);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Issuer", &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Version (always fits in less than 32 bits). */
   version = 0;
-  for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
-    version = (version << 8) | *(const unsigned char *) ccp;
+  for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
+    version = (version << 8) | *(const unsigned char *) ptr;
   if(data->set.ssl.certinfo) {
-    ccp = curl_maprintf("%x", version);
-    if(!ccp)
-      return CURLE_OUT_OF_MEMORY;
-    result = ssl_push_certinfo(data, certnum, "Version", ccp);
-    free((char *) ccp);
+    result = Curl_dyn_addf(&out, "%x", version);
     if(result)
-      return result;
+      goto done;
+    result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
+    if(result)
+      goto done;
+    Curl_dyn_reset(&out);
   }
-  if(!certnum)
-    infof(data, "   Version: %u (0x%x)", version + 1, version);
 
   /* Serial number. */
-  ccp = ASN1tostr(&cert.serialNumber, 0);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Serial Number", ccp);
-  if(!certnum)
-    infof(data, "   Serial Number: %s", ccp);
-  free((char *) ccp);
+  result = ASN1tostr(&out, &cert.serialNumber, 0);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Serial Number", &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Signature algorithm .*/
-  ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
-                 cert.signatureAlgorithm.end);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
-  if(!certnum)
-    infof(data, "   Signature Algorithm: %s", ccp);
-  free((char *) ccp);
+  result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
+                    cert.signatureAlgorithm.end);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm",
+                                   &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Start Date. */
-  ccp = ASN1tostr(&cert.notBefore, 0);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Start Date", ccp);
-  if(!certnum)
-    infof(data, "   Start Date: %s", ccp);
-  free((char *) ccp);
+  result = ASN1tostr(&out, &cert.notBefore, 0);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Start Date", &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Expire Date. */
-  ccp = ASN1tostr(&cert.notAfter, 0);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Expire Date", ccp);
-  if(!certnum)
-    infof(data, "   Expire Date: %s", ccp);
-  free((char *) ccp);
+  result = ASN1tostr(&out, &cert.notAfter, 0);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Expire Date", &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Public Key Algorithm. */
-  ccp = dumpAlgo(&param, cert.subjectPublicKeyAlgorithm.beg,
-                 cert.subjectPublicKeyAlgorithm.end);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Public Key Algorithm",
-                                    ccp);
-  if(!result) {
-    int ret;
-    if(!certnum)
-      infof(data, "   Public Key Algorithm: %s", ccp);
-    ret = do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
-    if(ret)
-      result = CURLE_OUT_OF_MEMORY; /* the most likely error */
-  }
-  free((char *) ccp);
+  result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
+                    cert.subjectPublicKeyAlgorithm.end);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Public Key Algorithm",
+                                   &out);
+    if(result)
+      goto done;
+  }
+
+  rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+                 &param, &cert.subjectPublicKey);
+  if(rc) {
+    result = CURLE_OUT_OF_MEMORY; /* the most likely error */
+    goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Signature. */
-  ccp = ASN1tostr(&cert.signature, 0);
-  if(!ccp)
-    return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Signature", ccp);
-  if(!certnum)
-    infof(data, "   Signature: %s", ccp);
-  free((char *) ccp);
+  result = ASN1tostr(&out, &cert.signature, 0);
   if(result)
-    return result;
+    goto done;
+  if(data->set.ssl.certinfo) {
+    result = ssl_push_certinfo_dyn(data, certnum, "Signature", &out);
+    if(result)
+      goto done;
+  }
+  Curl_dyn_reset(&out);
 
   /* Generate PEM certificate. */
   result = Curl_base64_encode(cert.certificate.beg,
                               cert.certificate.end - cert.certificate.beg,
-                              &cp1, &cl1);
+                              &certptr, &clen);
   if(result)
-    return result;
-  /* Compute the number of characters in final certificate string. Format is:
+    goto done;
+
+  /* Generate the final output certificate string. Format is:
      -----BEGIN CERTIFICATE-----\n
      <max 64 base64 characters>\n
      .
@@ -1232,207 +1196,34 @@
      .
      -----END CERTIFICATE-----\n
    */
-  i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26;
-  cp2 = malloc(i + 1);
-  if(!cp2) {
-    free(cp1);
-    return CURLE_OUT_OF_MEMORY;
-  }
+
+  Curl_dyn_reset(&out);
+
   /* Build the certificate string. */
-  i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----");
-  for(j = 0; j < cl1; j += 64)
-    i += copySubstring(cp2 + i, cp1 + j);
-  i += copySubstring(cp2 + i, "-----END CERTIFICATE-----");
-  cp2[i] = '\0';
-  free(cp1);
-  if(data->set.ssl.certinfo)
-    result = ssl_push_certinfo(data, certnum, "Cert", cp2);
-  if(!certnum)
-    infof(data, "%s", cp2);
-  free(cp2);
+  result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+  if(!result) {
+    size_t j = 0;
+
+    while(!result && (j < clen)) {
+      size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
+      result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+      if(!result)
+        result = Curl_dyn_addn(&out, "\n", 1);
+      j += chunksize;
+    }
+    if(!result)
+      result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
+  }
+  free(certptr);
+  if(!result)
+    if(data->set.ssl.certinfo)
+      result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out);
+
+done:
+  Curl_dyn_free(&out);
   return result;
 }
 
 #endif /* WANT_EXTRACT_CERTINFO */
 
 #endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
-
-#ifdef WANT_VERIFYHOST
-
-static const char *checkOID(const char *beg, const char *end,
-                            const char *oid)
-{
-  struct Curl_asn1Element e;
-  const char *ccp;
-  const char *p;
-  bool matched;
-
-  /* Check if first ASN.1 element at `beg' is the given OID.
-     Return a pointer in the source after the OID if found, else NULL. */
-
-  ccp = getASN1Element(&e, beg, end);
-  if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER)
-    return NULL;
-
-  p = OID2str(e.beg, e.end, FALSE);
-  if(!p)
-    return NULL;
-
-  matched = !strcmp(p, oid);
-  free((char *) p);
-  return matched? ccp: NULL;
-}
-
-CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
-                         struct Curl_easy *data,
-                         const char *beg, const char *end)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct Curl_X509certificate cert;
-  struct Curl_asn1Element dn;
-  struct Curl_asn1Element elem;
-  struct Curl_asn1Element ext;
-  struct Curl_asn1Element name;
-  const char *p;
-  const char *q;
-  char *dnsname;
-  int matched = -1;
-  size_t addrlen = (size_t) -1;
-  ssize_t len;
-  size_t hostlen;
-
-#ifdef ENABLE_IPV6
-  struct in6_addr addr;
-#else
-  struct in_addr addr;
-#endif
-
-  /* Verify that connection server matches info in X509 certificate at
-     `beg'..`end'. */
-
-  if(!conn_config->verifyhost)
-    return CURLE_OK;
-
-  if(Curl_parseX509(&cert, beg, end))
-    return CURLE_PEER_FAILED_VERIFICATION;
-
-  hostlen = strlen(connssl->hostname);
-
-  /* Get the server IP address. */
-#ifdef ENABLE_IPV6
-  if(cf->conn->bits.ipv6_ip &&
-     Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
-    addrlen = sizeof(struct in6_addr);
-  else
-#endif
-  if(Curl_inet_pton(AF_INET, connssl->hostname, &addr))
-    addrlen = sizeof(struct in_addr);
-
-  /* Process extensions. */
-  for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) {
-    p = getASN1Element(&ext, p, cert.extensions.end);
-    if(!p)
-      return CURLE_PEER_FAILED_VERIFICATION;
-
-    /* Check if extension is a subjectAlternativeName. */
-    ext.beg = checkOID(ext.beg, ext.end, sanOID);
-    if(ext.beg) {
-      ext.beg = getASN1Element(&elem, ext.beg, ext.end);
-      if(!ext.beg)
-        return CURLE_PEER_FAILED_VERIFICATION;
-      /* Skip critical if present. */
-      if(elem.tag == CURL_ASN1_BOOLEAN) {
-        ext.beg = getASN1Element(&elem, ext.beg, ext.end);
-        if(!ext.beg)
-          return CURLE_PEER_FAILED_VERIFICATION;
-      }
-      /* Parse the octet string contents: is a single sequence. */
-      if(!getASN1Element(&elem, elem.beg, elem.end))
-        return CURLE_PEER_FAILED_VERIFICATION;
-      /* Check all GeneralNames. */
-      for(q = elem.beg; matched != 1 && q < elem.end;) {
-        q = getASN1Element(&name, q, elem.end);
-        if(!q)
-          break;
-        switch(name.tag) {
-        case 2: /* DNS name. */
-          len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
-                            name.beg, name.end);
-          if(len > 0 && (size_t)len == strlen(dnsname))
-            matched = Curl_cert_hostcheck(dnsname, (size_t)len,
-                                          connssl->hostname, hostlen);
-          else
-            matched = 0;
-          free(dnsname);
-          break;
-
-        case 7: /* IP address. */
-          matched = (size_t)(name.end - name.beg) == addrlen &&
-            !memcmp(&addr, name.beg, addrlen);
-          break;
-        }
-      }
-    }
-  }
-
-  switch(matched) {
-  case 1:
-    /* an alternative name matched the server hostname */
-    infof(data, "  subjectAltName: %s matched", connssl->dispname);
-    return CURLE_OK;
-  case 0:
-    /* an alternative name field existed, but didn't match and then
-       we MUST fail */
-    infof(data, "  subjectAltName does not match %s", connssl->dispname);
-    return CURLE_PEER_FAILED_VERIFICATION;
-  }
-
-  /* Process subject. */
-  name.header = NULL;
-  name.beg = name.end = "";
-  q = cert.subject.beg;
-  /* we have to look to the last occurrence of a commonName in the
-     distinguished one to get the most significant one. */
-  while(q < cert.subject.end) {
-    q = getASN1Element(&dn, q, cert.subject.end);
-    if(!q)
-      break;
-    for(p = dn.beg; p < dn.end;) {
-      p = getASN1Element(&elem, p, dn.end);
-      if(!p)
-        return CURLE_PEER_FAILED_VERIFICATION;
-      /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */
-      elem.beg = checkOID(elem.beg, elem.end, cnOID);
-      if(elem.beg)
-        name = elem;    /* Latch CN. */
-    }
-  }
-
-  /* Check the CN if found. */
-  if(!getASN1Element(&elem, name.beg, name.end))
-    failf(data, "SSL: unable to obtain common name from peer certificate");
-  else {
-    len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
-    if(len < 0) {
-      free(dnsname);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    if(strlen(dnsname) != (size_t) len)         /* Nul byte in string ? */
-      failf(data, "SSL: illegal cert name field");
-    else if(Curl_cert_hostcheck((const char *) dnsname,
-                                len, connssl->hostname, hostlen)) {
-      infof(data, "  common name: %s (matched)", dnsname);
-      free(dnsname);
-      return CURLE_OK;
-    }
-    else
-      failf(data, "SSL: certificate subject name '%s' does not match "
-            "target host name '%s'", dnsname, connssl->dispname);
-    free(dnsname);
-  }
-
-  return CURLE_PEER_FAILED_VERIFICATION;
-}
-
-#endif /* WANT_VERIFYHOST */
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index 7e077f8..c80937b 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -37,7 +37,7 @@
 
 #include "warnless.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 #undef read
 #undef write
 #endif
@@ -367,7 +367,7 @@
 
 #endif /* USE_WINSOCK */
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 ssize_t curlx_read(int fd, void *buf, size_t count)
 {
@@ -379,8 +379,8 @@
   return (ssize_t)write(fd, buf, curlx_uztoui(count));
 }
 
-/* Ensure that warnless.h continues to have an effect in "unity" builds. */
-#undef HEADER_CURL_WARNLESS_H
+#endif /* _WIN32 */
 
-#endif /* WIN32 */
-
+/* Ensure that warnless.h redefinitions continue to have an effect
+   in "unity" builds. */
+#undef HEADER_CURL_WARNLESS_H_REDEFS
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h
index 2a53016..e5a02c8 100644
--- a/Utilities/cmcurl/lib/warnless.h
+++ b/Utilities/cmcurl/lib/warnless.h
@@ -69,18 +69,13 @@
 
 #endif /* USE_WINSOCK */
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 ssize_t curlx_read(int fd, void *buf, size_t count);
 
 ssize_t curlx_write(int fd, const void *buf, size_t count);
 
-#undef  read
-#define read(fd, buf, count)  curlx_read(fd, buf, count)
-#undef  write
-#define write(fd, buf, count) curlx_write(fd, buf, count)
-
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 #if defined(__INTEL_COMPILER) && defined(__unix__)
 
@@ -97,3 +92,15 @@
 #endif /* __INTEL_COMPILER && __unix__ */
 
 #endif /* HEADER_CURL_WARNLESS_H */
+
+#ifndef HEADER_CURL_WARNLESS_H_REDEFS
+#define HEADER_CURL_WARNLESS_H_REDEFS
+
+#if defined(_WIN32)
+#undef  read
+#define read(fd, buf, count)  curlx_read(fd, buf, count)
+#undef  write
+#define write(fd, buf, count) curlx_write(fd, buf, count)
+#endif
+
+#endif /* HEADER_CURL_WARNLESS_H_REDEFS */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
index 3c1964b..d976518 100644
--- a/Utilities/cmcurl/lib/ws.c
+++ b/Utilities/cmcurl/lib/ws.c
@@ -24,7 +24,7 @@
 #include "curl_setup.h"
 #include <curl/curl.h>
 
-#ifdef USE_WEBSOCKETS
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
 
 #include "urldata.h"
 #include "bufq.h"
@@ -225,6 +225,10 @@
       dec->payload_len = (dec->head[2] << 8) | dec->head[3];
       break;
     case 10:
+      if(dec->head[2] > 127) {
+        failf(data, "WS: frame length longer than 64 signed not supported");
+        return CURLE_RECV_ERROR;
+      }
       dec->payload_len = ((curl_off_t)dec->head[2] << 56) |
         (curl_off_t)dec->head[3] << 48 |
         (curl_off_t)dec->head[4] << 40 |
@@ -274,8 +278,8 @@
     dec->payload_offset += (curl_off_t)nwritten;
     remain = dec->payload_len - dec->payload_offset;
     /* infof(data, "WS-DEC: passed  %zd bytes payload, %"
-                CURL_FORMAT_CURL_OFF_T " remain",
-          nwritten, remain); */
+             CURL_FORMAT_CURL_OFF_T " remain",
+             nwritten, remain); */
   }
 
   return remain? CURLE_AGAIN : CURLE_OK;
@@ -296,7 +300,7 @@
   case WS_DEC_INIT:
     ws_dec_reset(dec);
     dec->state = WS_DEC_HEAD;
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case WS_DEC_HEAD:
     result = ws_dec_read_head(dec, data, inraw);
     if(result) {
@@ -321,7 +325,7 @@
       dec->state = WS_DEC_INIT;
       break;
     }
-    /* FALLTHROUGH */
+    FALLTHROUGH();
   case WS_DEC_PAYLOAD:
     result = ws_dec_pass_payload(dec, data, inraw, write_payload, write_ctx);
     ws_dec_info(dec, data, "passing");
@@ -350,6 +354,136 @@
   ws->frame.bytesleft = (payload_len - payload_offset - cur_len);
 }
 
+/* WebSockets decoding client writer */
+struct ws_cw_ctx {
+  struct Curl_cwriter super;
+  struct bufq buf;
+};
+
+static CURLcode ws_cw_init(struct Curl_easy *data,
+                           struct Curl_cwriter *writer)
+{
+  struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+  (void)data;
+  Curl_bufq_init2(&ctx->buf, WS_CHUNK_SIZE, 1, BUFQ_OPT_SOFT_LIMIT);
+  return CURLE_OK;
+}
+
+static void ws_cw_close(struct Curl_easy *data, struct Curl_cwriter *writer)
+{
+  struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+  (void) data;
+  Curl_bufq_free(&ctx->buf);
+}
+
+struct ws_cw_dec_ctx {
+  struct Curl_easy *data;
+  struct websocket *ws;
+  struct Curl_cwriter *next_writer;
+  int cw_type;
+};
+
+static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen,
+                              int frame_age, int frame_flags,
+                              curl_off_t payload_offset,
+                              curl_off_t payload_len,
+                              void *user_data,
+                              CURLcode *err)
+{
+  struct ws_cw_dec_ctx *ctx = user_data;
+  struct Curl_easy *data = ctx->data;
+  struct websocket *ws = ctx->ws;
+  curl_off_t remain = (payload_len - (payload_offset + buflen));
+
+  (void)frame_age;
+  if((frame_flags & CURLWS_PING) && !remain) {
+    /* auto-respond to PINGs, only works for single-frame payloads atm */
+    size_t bytes;
+    infof(data, "WS: auto-respond to PING with a PONG");
+    /* send back the exact same content as a PONG */
+    *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
+    if(*err)
+      return -1;
+  }
+  else if(buflen || !remain) {
+    /* forward the decoded frame to the next client writer. */
+    update_meta(ws, frame_age, frame_flags, payload_offset,
+                payload_len, buflen);
+
+    *err = Curl_cwriter_write(data, ctx->next_writer, ctx->cw_type,
+                              (const char *)buf, buflen);
+    if(*err)
+      return -1;
+  }
+  *err = CURLE_OK;
+  return (ssize_t)buflen;
+}
+
+static CURLcode ws_cw_write(struct Curl_easy *data,
+                            struct Curl_cwriter *writer, int type,
+                            const char *buf, size_t nbytes)
+{
+  struct ws_cw_ctx *ctx = (struct ws_cw_ctx *)writer;
+  struct websocket *ws;
+  CURLcode result;
+
+  if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
+  ws = data->conn->proto.ws;
+  if(!ws) {
+    failf(data, "WS: not a websocket transfer");
+    return CURLE_FAILED_INIT;
+  }
+
+  if(nbytes) {
+    ssize_t nwritten;
+    nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf,
+                               nbytes, &result);
+    if(nwritten < 0) {
+      infof(data, "WS: error adding data to buffer %d", result);
+      return result;
+    }
+  }
+
+  while(!Curl_bufq_is_empty(&ctx->buf)) {
+    struct ws_cw_dec_ctx pass_ctx;
+    pass_ctx.data = data;
+    pass_ctx.ws = ws;
+    pass_ctx.next_writer = writer->next;
+    pass_ctx.cw_type = type;
+    result = ws_dec_pass(&ws->dec, data, &ctx->buf,
+                         ws_cw_dec_next, &pass_ctx);
+    if(result == CURLE_AGAIN)
+      /* insufficient amount of data, keep it for later.
+       * we pretend to have written all since we have a copy */
+      return CURLE_OK;
+    else if(result) {
+      infof(data, "WS: decode error %d", (int)result);
+      return result;
+    }
+  }
+
+  if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
+    infof(data, "WS: decode ending with %zd frame bytes remaining",
+          Curl_bufq_len(&ctx->buf));
+    return CURLE_RECV_ERROR;
+  }
+
+  return CURLE_OK;
+}
+
+/* WebSocket payload decoding client writer. */
+static const struct Curl_cwtype ws_cw_decode = {
+  "ws-decode",
+  NULL,
+  ws_cw_init,
+  ws_cw_write,
+  ws_cw_close,
+  sizeof(struct ws_cw_ctx)
+};
+
+
 static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
                         const char *msg)
 {
@@ -410,6 +544,13 @@
   size_t hlen;
   ssize_t n;
 
+  if(payload_len < 0) {
+    failf(data, "WS: starting new frame with negative payload length %"
+                CURL_FORMAT_CURL_OFF_T, payload_len);
+    *err = CURLE_SEND_ERROR;
+    return -1;
+  }
+
   if(enc->payload_remain > 0) {
     /* trying to write a new frame before the previous one is finished */
     failf(data, "WS: starting new frame with %zd bytes from last one"
@@ -607,6 +748,7 @@
 {
   struct SingleRequest *k = &data->req;
   struct websocket *ws;
+  struct Curl_cwriter *ws_dec_writer;
   CURLcode result;
 
   DEBUGASSERT(data->conn);
@@ -616,7 +758,8 @@
     if(!ws)
       return CURLE_OUT_OF_MEMORY;
     data->conn->proto.ws = ws;
-    Curl_bufq_init(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT);
+    Curl_bufq_init2(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
+                    BUFQ_OPT_SOFT_LIMIT);
     Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
                     BUFQ_OPT_SOFT_LIMIT);
     ws_dec_init(&ws->dec);
@@ -655,6 +798,18 @@
   infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
         ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
 
+  /* Install our client writer that decodes WS frames payload */
+  result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode,
+                               CURL_CW_CONTENT_DECODE);
+  if(result)
+    return result;
+
+  result = Curl_cwriter_add(data, ws_dec_writer);
+  if(result) {
+    Curl_cwriter_free(data, ws_dec_writer);
+    return result;
+  }
+
   if(data->set.connect_only) {
     ssize_t nwritten;
     /* In CONNECT_ONLY setup, the payloads from `mem` need to be received
@@ -666,107 +821,17 @@
       return result;
     infof(data, "%zu bytes websocket payload", nread);
   }
+  else { /* !connect_only */
+    /* And pass any additional data to the writers */
+    if(nread) {
+      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread);
+    }
+  }
   k->upgr101 = UPGR101_RECEIVED;
 
   return result;
 }
 
-static ssize_t ws_client_write(const unsigned char *buf, size_t buflen,
-                               int frame_age, int frame_flags,
-                               curl_off_t payload_offset,
-                               curl_off_t payload_len,
-                               void *userp,
-                               CURLcode *err)
-{
-  struct Curl_easy *data = userp;
-  struct websocket *ws;
-  size_t wrote;
-  curl_off_t remain = (payload_len - (payload_offset + buflen));
-
-  (void)frame_age;
-  if(!data->conn || !data->conn->proto.ws) {
-    *err = CURLE_FAILED_INIT;
-    return -1;
-  }
-  ws = data->conn->proto.ws;
-
-  if((frame_flags & CURLWS_PING) && !remain) {
-    /* auto-respond to PINGs, only works for single-frame payloads atm */
-    size_t bytes;
-    infof(data, "WS: auto-respond to PING with a PONG");
-    /* send back the exact same content as a PONG */
-    *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
-    if(*err)
-      return -1;
-  }
-  else if(buflen || !remain) {
-    /* deliver the decoded frame to the user callback. The application
-     * may invoke curl_ws_meta() to access frame information. */
-    update_meta(ws, frame_age, frame_flags, payload_offset,
-                payload_len, buflen);
-    Curl_set_in_callback(data, true);
-    wrote = data->set.fwrite_func((char *)buf, 1,
-                                  buflen, data->set.out);
-    Curl_set_in_callback(data, false);
-    if(wrote != buflen) {
-      *err = CURLE_RECV_ERROR;
-      return -1;
-    }
-  }
-  *err = CURLE_OK;
-  return (ssize_t)buflen;
-}
-
-/* Curl_ws_writecb() is the write callback for websocket traffic. The
-   websocket data is provided to this raw, in chunks. This function should
-   handle/decode the data and call the "real" underlying callback accordingly.
-*/
-size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
-                       size_t nitems, void *userp)
-{
-  struct Curl_easy *data = userp;
-
-  if(data->set.ws_raw_mode)
-    return data->set.fwrite_func(buffer, size, nitems, data->set.out);
-  else if(nitems) {
-    struct websocket *ws;
-    CURLcode result;
-
-    if(!data->conn || !data->conn->proto.ws) {
-      failf(data, "WS: not a websocket transfer");
-      return nitems - 1;
-    }
-    ws = data->conn->proto.ws;
-
-    if(buffer) {
-      ssize_t nwritten;
-
-      nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)buffer,
-                                 nitems, &result);
-      if(nwritten < 0) {
-        infof(data, "WS: error adding data to buffer %d", (int)result);
-        return nitems - 1;
-      }
-      buffer = NULL;
-    }
-
-    while(!Curl_bufq_is_empty(&ws->recvbuf)) {
-
-      result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
-                           ws_client_write, data);
-      if(result == CURLE_AGAIN)
-        /* insufficient amount of data, keep it for later.
-         * we pretend to have written all since we have a copy */
-        return nitems;
-      else if(result) {
-        infof(data, "WS: decode error %d", (int)result);
-        return nitems - 1;
-      }
-    }
-  }
-  return nitems;
-}
-
 struct ws_collect {
   struct Curl_easy *data;
   void *buffer;
@@ -925,8 +990,8 @@
   *metap = &ws->frame;
   *nread = ws->frame.len;
   /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
-              CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)",
-        buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */
+           CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)",
+           buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */
   return CURLE_OK;
 }
 
@@ -997,8 +1062,11 @@
   ws = data->conn->proto.ws;
 
   if(data->set.ws_raw_mode) {
-    if(fragsize || flags)
+    if(fragsize || flags) {
+      DEBUGF(infof(data, "ws_send: "
+                   "fragsize and flags cannot be non-zero in raw mode"));
       return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
     if(!buflen)
       /* nothing to do */
       return CURLE_OK;
@@ -1071,14 +1139,23 @@
   }
 }
 
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+                              struct connectdata *conn)
+{
+  /* websockets is 1.1 only (for now) */
+  data->state.httpwant = CURL_HTTP_VERSION_1_1;
+  return Curl_http_setup_conn(data, conn);
+}
+
+
 void Curl_ws_done(struct Curl_easy *data)
 {
   (void)data;
 }
 
-CURLcode Curl_ws_disconnect(struct Curl_easy *data,
-                            struct connectdata *conn,
-                            bool dead_connection)
+static CURLcode ws_disconnect(struct Curl_easy *data,
+                              struct connectdata *conn,
+                              bool dead_connection)
 {
   (void)data;
   (void)dead_connection;
@@ -1096,6 +1173,57 @@
   return NULL;
 }
 
+const struct Curl_handler Curl_handler_ws = {
+  "WS",                                 /* scheme */
+  ws_setup_conn,                        /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  Curl_http_getsock_do,                 /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ws_disconnect,                        /* disconnect */
+  Curl_http_write_resp,                 /* write_resp */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_WS,                         /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+  PROTOPT_USERPWDCTRL
+};
+
+#ifdef USE_SSL
+const struct Curl_handler Curl_handler_wss = {
+  "WSS",                                /* scheme */
+  ws_setup_conn,                        /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  NULL,                                 /* connecting */
+  ZERO_NULL,                            /* doing */
+  NULL,                                 /* proto_getsock */
+  Curl_http_getsock_do,                 /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ws_disconnect,                        /* disconnect */
+  Curl_http_write_resp,                 /* write_resp */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  PORT_HTTPS,                           /* defport */
+  CURLPROTO_WSS,                        /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
+  PROTOPT_USERPWDCTRL
+};
+#endif
+
+
 #else
 
 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h
index 0308a42..5f40d45 100644
--- a/Utilities/cmcurl/lib/ws.h
+++ b/Utilities/cmcurl/lib/ws.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#ifdef USE_WEBSOCKETS
+#if defined(USE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
 
 #ifdef USE_HYPER
 #define REQTYPE void
@@ -75,11 +75,14 @@
 
 CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
 CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
-size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
 void Curl_ws_done(struct Curl_easy *data);
-CURLcode Curl_ws_disconnect(struct Curl_easy *data,
-                            struct connectdata *conn,
-                            bool dead_connection);
+
+extern const struct Curl_handler Curl_handler_ws;
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_wss;
+#endif
+
+
 #else
 #define Curl_ws_request(x,y) CURLE_OK
 #define Curl_ws_done(x) Curl_nop_stmt
diff --git a/Utilities/cmjsoncpp/include/json/value.h b/Utilities/cmjsoncpp/include/json/value.h
index f6ac9e3..421fef8 100644
--- a/Utilities/cmjsoncpp/include/json/value.h
+++ b/Utilities/cmjsoncpp/include/json/value.h
@@ -33,6 +33,10 @@
 #if __clang_major__ == 3 && __clang_minor__ <= 8
 #define JSONCPP_TEMPLATE_DELETE
 #endif
+#elif defined(__EDG__) && defined(__LCC__)
+#if __LCC__ < 123
+#define JSONCPP_TEMPLATE_DELETE
+#endif
 #endif
 #if !defined(JSONCPP_TEMPLATE_DELETE)
 #define JSONCPP_TEMPLATE_DELETE = delete
diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt
index 027de5c..e47184b 100644
--- a/Utilities/cmlibarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/CMakeLists.txt
@@ -69,6 +69,7 @@
 SET(BSDCPIO_VERSION_STRING     "${VERSION}")
 SET(BSDTAR_VERSION_STRING      "${VERSION}")
 SET(BSDCAT_VERSION_STRING      "${VERSION}")
+SET(BSDUNZIP_VERSION_STRING    "${VERSION}")
 SET(LIBARCHIVE_VERSION_NUMBER  "${_version_number}")
 SET(LIBARCHIVE_VERSION_STRING  "${VERSION}")
 
@@ -224,6 +225,8 @@
 # Enable CTest/CDash support
 include(CTest)
 
+option(BUILD_SHARED_LIBS "Build shared libraries" ON)
+
 OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" OFF)
 OPTION(ENABLE_NETTLE "Enable use of Nettle" OFF)
 OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
@@ -248,6 +251,13 @@
 OPTION(ENABLE_CPIO_SHARED "Enable dynamic build of cpio" FALSE)
 OPTION(ENABLE_CAT "Enable cat building" ON)
 OPTION(ENABLE_CAT_SHARED "Enable dynamic build of cat" FALSE)
+IF(WIN32 AND NOT CYGWIN)
+	SET(ENABLE_UNZIP FALSE)
+	SET(ENABLE_UNZIP_SHARED FALSE)
+ELSE()
+	OPTION(ENABLE_UNZIP "Enable unzip building" ON)
+	OPTION(ENABLE_UNZIP_SHARED "Enable dynamic build of unzip" FALSE)
+ENDIF()
 OPTION(ENABLE_XATTR "Enable extended attribute support" ON)
 OPTION(ENABLE_ACL "Enable ACL support" ON)
 OPTION(ENABLE_ICONV "Enable iconv support" ON)
@@ -324,6 +334,7 @@
 
 IF(MINGW)
   ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO)
+  ADD_DEFINITIONS(-D__MINGW_USE_VC2005_COMPAT)
 ENDIF()
 
 #
@@ -394,7 +405,11 @@
       IF("${TRY_TYPE}" MATCHES "COMPILES")
         CHECK_C_SOURCE_COMPILES("${SAMPLE_SOURCE}" ${VAR})
       ELSEIF("${TRY_TYPE}" MATCHES "RUNS")
-        CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR})
+        IF(CMAKE_CROSSCOMPILING)
+          MESSAGE(WARNING "Cannot test run \"${VAR}\" when cross-compiling")
+        ELSE(CMAKE_CROSSCOMPILING)
+          CHECK_C_SOURCE_RUNS("${SAMPLE_SOURCE}" ${VAR})
+        ENDIF(CMAKE_CROSSCOMPILING)
       ELSE("${TRY_TYPE}" MATCHES "COMPILES")
         MESSAGE(FATAL_ERROR "UNKNOWN KEYWORD \"${TRY_TYPE}\" FOR TRY_TYPE")
       ENDIF("${TRY_TYPE}" MATCHES "COMPILES")
@@ -533,15 +548,19 @@
       COMPILES
       "#include <lzma.h>\nint main() {return (int)lzma_version_number(); }"
       "WITHOUT_LZMA_API_STATIC;LZMA_API_STATIC")
+    CHECK_C_SOURCE_COMPILES(
+      "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}"
+      HAVE_LZMA_STREAM_ENCODER_MT)
     IF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
       ADD_DEFINITIONS(-DLZMA_API_STATIC)
-    ENDIF(NOT WITHOUT_LZMA_API_STATIC AND LZMA_API_STATIC)
+    ENDIF()
   ELSE()
     ADD_DEFINITIONS(-DLZMA_API_STATIC)
   ENDIF()
   CMAKE_POP_CHECK_STATE()
 ELSE(LIBLZMA_FOUND)
 # LZMA not found and will not be used.
+  SET(HAVE_LZMA_STREAM_ENCODER_MT 0)
 ENDIF(LIBLZMA_FOUND)
 #
 # Find LZO2
@@ -590,6 +609,7 @@
   SET(HAVE_BLAKE2_H 1)
   SET(ARCHIVE_BLAKE2 FALSE)
   LIST(APPEND ADDITIONAL_LIBS ${LIBB2_LIBRARY})
+  INCLUDE_DIRECTORIES(${LIBB2_INCLUDE_DIR})
   CMAKE_PUSH_CHECK_STATE()
   SET(CMAKE_REQUIRED_LIBRARIES ${LIBB2_LIBRARY})
   SET(CMAKE_REQUIRED_INCLUDES ${LIBB2_INCLUDE_DIR})
@@ -708,6 +728,7 @@
 int main(void) { return EXT2_IOC_GETFLAGS; }" HAVE_WORKING_EXT2_IOC_GETFLAGS)
 
 LA_CHECK_INCLUDE_FILE("fcntl.h" HAVE_FCNTL_H)
+LA_CHECK_INCLUDE_FILE("fnmatch.h" HAVE_FNMATCH_H)
 LA_CHECK_INCLUDE_FILE("grp.h" HAVE_GRP_H)
 LA_CHECK_INCLUDE_FILE("io.h" HAVE_IO_H)
 LA_CHECK_INCLUDE_FILE("langinfo.h" HAVE_LANGINFO_H)
@@ -745,6 +766,7 @@
 LA_CHECK_INCLUDE_FILE("sys/mount.h" HAVE_SYS_MOUNT_H)
 LA_CHECK_INCLUDE_FILE("sys/param.h" HAVE_SYS_PARAM_H)
 LA_CHECK_INCLUDE_FILE("sys/poll.h" HAVE_SYS_POLL_H)
+LA_CHECK_INCLUDE_FILE("sys/queue.h" HAVE_SYS_QUEUE_H)
 LA_CHECK_INCLUDE_FILE("sys/richacl.h" HAVE_SYS_RICHACL_H)
 LA_CHECK_INCLUDE_FILE("sys/select.h" HAVE_SYS_SELECT_H)
 LA_CHECK_INCLUDE_FILE("sys/stat.h" HAVE_SYS_STAT_H)
@@ -764,9 +786,9 @@
 LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H)
 LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H)
 IF(ENABLE_CNG)
-  LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H)
+  LA_CHECK_INCLUDE_FILE("bcrypt.h" HAVE_BCRYPT_H)
   IF(HAVE_BCRYPT_H)
-    LIST(APPEND ADDITIONAL_LIBS "Bcrypt")
+    LIST(APPEND ADDITIONAL_LIBS "bcrypt")
   ENDIF(HAVE_BCRYPT_H)
 ELSE(ENABLE_CNG)
   UNSET(HAVE_BCRYPT_H CACHE)
@@ -842,6 +864,10 @@
   IF(OPENSSL_FOUND)
     SET(HAVE_LIBCRYPTO 1)
     INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
+    SET(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+    SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+    LA_CHECK_INCLUDE_FILE("openssl/evp.h" HAVE_OPENSSL_EVP_H)
+    CHECK_FUNCTION_EXISTS(PKCS5_PBKDF2_HMAC_SHA1 HAVE_PKCS5_PBKDF2_HMAC_SHA1)
   ENDIF(OPENSSL_FOUND)
 ELSE()
   SET(OPENSSL_FOUND FALSE) # Override cached value
@@ -1379,6 +1405,7 @@
 CHECK_FUNCTION_EXISTS_GLIBC(fchown HAVE_FCHOWN)
 CHECK_FUNCTION_EXISTS_GLIBC(fcntl HAVE_FCNTL)
 CHECK_FUNCTION_EXISTS_GLIBC(fdopendir HAVE_FDOPENDIR)
+CHECK_FUNCTION_EXISTS_GLIBC(fnmatch HAVE_FNMATCH)
 CHECK_FUNCTION_EXISTS_GLIBC(fork HAVE_FORK)
 CHECK_FUNCTION_EXISTS_GLIBC(fstat HAVE_FSTAT)
 CHECK_FUNCTION_EXISTS_GLIBC(fstatat HAVE_FSTATAT)
@@ -1391,13 +1418,16 @@
 CHECK_FUNCTION_EXISTS_GLIBC(geteuid HAVE_GETEUID)
 CHECK_FUNCTION_EXISTS_GLIBC(getgrgid_r HAVE_GETGRGID_R)
 CHECK_FUNCTION_EXISTS_GLIBC(getgrnam_r HAVE_GETGRNAM_R)
+CHECK_FUNCTION_EXISTS_GLIBC(getline HAVE_GETLINE)
 CHECK_FUNCTION_EXISTS_GLIBC(getpwnam_r HAVE_GETPWNAM_R)
 CHECK_FUNCTION_EXISTS_GLIBC(getpwuid_r HAVE_GETPWUID_R)
 CHECK_FUNCTION_EXISTS_GLIBC(getpid HAVE_GETPID)
 CHECK_FUNCTION_EXISTS_GLIBC(getvfsbyname HAVE_GETVFSBYNAME)
 CHECK_FUNCTION_EXISTS_GLIBC(gmtime_r HAVE_GMTIME_R)
 CHECK_FUNCTION_EXISTS_GLIBC(lchflags HAVE_LCHFLAGS)
-CHECK_FUNCTION_EXISTS_GLIBC(lchmod HAVE_LCHMOD)
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "LCC" OR NOT CMAKE_C_COMPILER_VERSION VERSION_LESS_EQUAL "1.23")
+  CHECK_FUNCTION_EXISTS_GLIBC(lchmod HAVE_LCHMOD)
+endif()
 CHECK_FUNCTION_EXISTS_GLIBC(lchown HAVE_LCHOWN)
 CHECK_FUNCTION_EXISTS_GLIBC(link HAVE_LINK)
 CHECK_FUNCTION_EXISTS_GLIBC(linkat HAVE_LINKAT)
@@ -1443,12 +1473,12 @@
 CHECK_FUNCTION_EXISTS_GLIBC(wcscpy HAVE_WCSCPY)
 CHECK_FUNCTION_EXISTS_GLIBC(wcslen HAVE_WCSLEN)
 CHECK_FUNCTION_EXISTS_GLIBC(wctomb HAVE_WCTOMB)
-CHECK_FUNCTION_EXISTS_GLIBC(_ctime64_s HAVE__CTIME64_S)
 CHECK_FUNCTION_EXISTS_GLIBC(_fseeki64 HAVE__FSEEKI64)
 CHECK_FUNCTION_EXISTS_GLIBC(_get_timezone HAVE__GET_TIMEZONE)
-CHECK_FUNCTION_EXISTS_GLIBC(_gmtime64_s HAVE__GMTIME64_S)
-CHECK_FUNCTION_EXISTS_GLIBC(_localtime64_s HAVE__LOCALTIME64_S)
-CHECK_FUNCTION_EXISTS_GLIBC(_mkgmtime64 HAVE__MKGMTIME64)
+CHECK_SYMBOL_EXISTS(ctime_s "time.h" HAVE_CTIME_S)
+CHECK_SYMBOL_EXISTS(gmtime_s "time.h" HAVE_GMTIME_S)
+CHECK_SYMBOL_EXISTS(localtime_s "time.h" HAVE_LOCALTIME_S)
+CHECK_SYMBOL_EXISTS(_mkgmtime "time.h" HAVE__MKGMTIME)
 
 SET(CMAKE_REQUIRED_LIBRARIES "")
 CHECK_FUNCTION_EXISTS(cygwin_conv_path HAVE_CYGWIN_CONV_PATH)
@@ -1491,7 +1521,6 @@
   "#include <fcntl.h>\n#include <unistd.h>\nint main() {char buf[10]; return readlinkat(AT_FDCWD, \"\", buf, 0);}"
   HAVE_READLINKAT)
 
-
 # To verify major(), we need to both include the header
 # of interest and verify that the result can be linked.
 # CHECK_FUNCTION_EXISTS doesn't accept a header argument,
@@ -1503,20 +1532,6 @@
   "#include <sys/sysmacros.h>\nint main() { return major(256); }"
   MAJOR_IN_SYSMACROS)
 
-IF(ENABLE_LZMA)
-CMAKE_PUSH_CHECK_STATE()
-SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES})
-SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR})
-
-CHECK_C_SOURCE_COMPILES(
-  "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}"
-  HAVE_LZMA_STREAM_ENCODER_MT)
-
-CMAKE_POP_CHECK_STATE()
-ELSE()
-  SET(HAVE_LZMA_STREAM_ENCODER_MT 0)
-ENDIF(ENABLE_LZMA)
-
 IF(HAVE_STRERROR_R)
   SET(HAVE_DECL_STRERROR_R 1)
 ENDIF(HAVE_STRERROR_R)
@@ -1578,7 +1593,7 @@
 #
 #
 CHECK_STRUCT_HAS_MEMBER("struct tm" tm_sec
-    "sys/types.h;sys/time.h;time.h" TIME_WITH_SYS_TIME)
+    "sys/types.h;sys/time.h;time.h" HAVE_SYS_TIME_H)
 
 CHECK_TYPE_SIZE(dev_t       DEV_T)
 IF(NOT HAVE_DEV_T)
@@ -2076,6 +2091,7 @@
 add_subdirectory(cat)
 add_subdirectory(tar)
 add_subdirectory(cpio)
+add_subdirectory(unzip)
 ENDIF()
 
 install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibarchive)
diff --git a/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake
index a916395..aa40485 100644
--- a/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake
+++ b/Utilities/cmlibarchive/build/cmake/FindMbedTLS.cmake
@@ -7,7 +7,7 @@
 set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
 
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(MBEDTLS DEFAULT_MSG
+find_package_handle_standard_args(MbedTLS DEFAULT_MSG
     MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
 
 mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in
index e44a514..493c388 100644
--- a/Utilities/cmlibarchive/build/cmake/config.h.in
+++ b/Utilities/cmlibarchive/build/cmake/config.h.in
@@ -38,6 +38,9 @@
 /* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
 #cmakedefine ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
 
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_MD5_MBEDTLS 1
+
 /* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_MD5_NETTLE 1
 
@@ -53,6 +56,9 @@
 /* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_RMD160_NETTLE 1
 
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_RMD160_MBEDTLS 1
+
 /* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
 #cmakedefine ARCHIVE_CRYPTO_RMD160_OPENSSL 1
 
@@ -62,6 +68,9 @@
 /* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
 
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_SHA1_MBEDTLS 1
+
 /* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA1_NETTLE 1
 
@@ -83,6 +92,9 @@
 /* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
 
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_SHA256_MBEDTLS 1
+
 /* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA256_NETTLE 1
 
@@ -104,6 +116,9 @@
 /* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
 
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_SHA384_MBEDTLS 1
+
 /* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA384_NETTLE 1
 
@@ -125,6 +140,9 @@
 /* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
 
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+#cmakedefine ARCHIVE_CRYPTO_SHA512_MBEDTLS 1
+
 /* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
 #cmakedefine ARCHIVE_CRYPTO_SHA512_NETTLE 1
 
@@ -155,6 +173,9 @@
 /* Version number of bsdcat */
 #cmakedefine BSDCAT_VERSION_STRING "@BSDCAT_VERSION_STRING@"
 
+/* Version number of bsdunzip */
+#cmakedefine BSDUNZIP_VERSION_STRING "@BSDUNZIP_VERSION_STRING@"
+
 /* Define to 1 if you have the `acl_create_entry' function. */
 #cmakedefine HAVE_ACL_CREATE_ENTRY 1
 
@@ -197,7 +218,7 @@
 /* Define to 1 if you have the <attr/xattr.h> header file. */
 #cmakedefine HAVE_ATTR_XATTR_H 1
 
-/* Define to 1 if you have the <Bcrypt.h> header file. */
+/* Define to 1 if you have the <bcrypt.h> header file. */
 #cmakedefine HAVE_BCRYPT_H 1
 
 /* Define to 1 if you have the <bsdxml.h> header file. */
@@ -357,6 +378,12 @@
 /* Define to 1 if you have the `flistxattr' function. */
 #cmakedefine HAVE_FLISTXATTR 1
 
+/* Define to 1 if you have the `fnmatch' function. */
+#cmakedefine HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#cmakedefine HAVE_FNMATCH_H 1
+
 /* Define to 1 if you have the `fork' function. */
 #cmakedefine HAVE_FORK 1
 
@@ -405,6 +432,9 @@
 /* Define to 1 if you have the `getgrnam_r' function. */
 #cmakedefine HAVE_GETGRNAM_R 1
 
+/* Define to 1 if you have the `getline' function. */
+#cmakedefine HAVE_GETLINE 1
+
 /* Define to 1 if you have the `getpid' function. */
 #cmakedefine HAVE_GETPID 1
 
@@ -611,6 +641,15 @@
 /* Define to 1 if you have the <lzo/lzoconf.h> header file. */
 #cmakedefine HAVE_LZO_LZOCONF_H 1
 
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+#cmakedefine HAVE_MBEDTLS_AES_H 1
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+#cmakedefine HAVE_MBEDTLS_MD_H 1
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+#cmakedefine HAVE_MBEDTLS_PKCS5_H 1
+
 /* Define to 1 if you have the `mbrtowc' function. */
 #cmakedefine HAVE_MBRTOWC 1
 
@@ -662,6 +701,9 @@
 /* Define to 1 if you have the `openat' function. */
 #cmakedefine HAVE_OPENAT 1
 
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+#cmakedefine HAVE_OPENSSL_EVP_H 1
+
 /* Define to 1 if you have the <paths.h> header file. */
 #cmakedefine HAVE_PATHS_H 1
 
@@ -771,6 +813,12 @@
 /* Define to 1 if you have the `strrchr' function. */
 #cmakedefine HAVE_STRRCHR 1
 
+/* Define to 1 if the system has the type `struct statfs'. */
+#cmakedefine HAVE_STRUCT_STATFS 1
+
+/* Define to 1 if `f_iosize' is a member of `struct statfs'. */
+#cmakedefine HAVE_STRUCT_STATFS_F_IOSIZE 1
+
 /* Define to 1 if `f_namemax' is a member of `struct statfs'. */
 #cmakedefine HAVE_STRUCT_STATFS_F_NAMEMAX 1
 
@@ -854,6 +902,9 @@
 /* Define to 1 if you have the <sys/poll.h> header file. */
 #cmakedefine HAVE_SYS_POLL_H 1
 
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#cmakedefine HAVE_SYS_QUEUE_H 1
+
 /* Define to 1 if you have the <sys/richacl.h> header file. */
 #cmakedefine HAVE_SYS_RICHACL_H 1
 
@@ -993,8 +1044,8 @@
 /* Define to 1 if you have the <zstd.h> header file. */
 #cmakedefine HAVE_ZSTD_H 1
 
-/* Define to 1 if you have the `_ctime64_s' function. */
-#cmakedefine HAVE__CTIME64_S 1
+/* Define to 1 if you have the `ctime_s' function. */
+#cmakedefine HAVE_CTIME_S 1
 
 /* Define to 1 if you have the `_fseeki64' function. */
 #cmakedefine HAVE__FSEEKI64 1
@@ -1002,14 +1053,14 @@
 /* Define to 1 if you have the `_get_timezone' function. */
 #cmakedefine HAVE__GET_TIMEZONE 1
 
-/* Define to 1 if you have the `_gmtime64_s' function. */
-#cmakedefine HAVE__GMTIME64_S 1
+/* Define to 1 if you have the `gmtime_s' function. */
+#cmakedefine HAVE_GMTIME_S 1
 
-/* Define to 1 if you have the `_localtime64_s' function. */
-#cmakedefine HAVE__LOCALTIME64_S 1
+/* Define to 1 if you have the `localtime_s' function. */
+#cmakedefine HAVE_LOCALTIME_S 1
 
-/* Define to 1 if you have the `_mkgmtime64' function. */
-#cmakedefine HAVE__MKGMTIME64 1
+/* Define to 1 if you have the `_mkgmtime' function. */
+#cmakedefine HAVE__MKGMTIME 1
 
 /* Define as const if the declaration of iconv() needs const. */
 #define ICONV_CONST @ICONV_CONST@
diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version
index 1af1bec..414ae6d 100644
--- a/Utilities/cmlibarchive/build/version
+++ b/Utilities/cmlibarchive/build/version
@@ -1 +1 @@
-3006002
+3007002
diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt
index e820853..ac0bd2c 100644
--- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt
@@ -252,10 +252,12 @@
 
 IF(0) # CMake does not build libarchive's full package.
 # Libarchive is a shared library
-ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
-TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .)
-TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
-SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
+IF(BUILD_SHARED_LIBS)
+  ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
+  TARGET_INCLUDE_DIRECTORIES(archive PUBLIC .)
+  TARGET_LINK_LIBRARIES(archive ${ADDITIONAL_LIBS})
+  SET_TARGET_PROPERTIES(archive PROPERTIES SOVERSION ${SOVERSION})
+ENDIF(BUILD_SHARED_LIBS)
 
 # archive_static is a static library
 ADD_LIBRARY(archive_static STATIC ${libarchive_SOURCES} ${include_HEADERS})
@@ -263,13 +265,19 @@
 SET_TARGET_PROPERTIES(archive_static PROPERTIES COMPILE_DEFINITIONS
   LIBARCHIVE_STATIC)
 # On Posix systems, libarchive.so and libarchive.a can co-exist.
-IF(NOT WIN32 OR CYGWIN)
+IF(NOT WIN32 OR CYGWIN OR NOT BUILD_SHARED_LIBS)
   SET_TARGET_PROPERTIES(archive_static PROPERTIES OUTPUT_NAME archive)
-ENDIF(NOT WIN32 OR CYGWIN)
+ENDIF(NOT WIN32 OR CYGWIN OR NOT BUILD_SHARED_LIBS)
 
 IF(ENABLE_INSTALL)
   # How to install the libraries
-  INSTALL(TARGETS archive archive_static
+  IF(BUILD_SHARED_LIBS)
+    INSTALL(TARGETS archive
+            RUNTIME DESTINATION bin
+            LIBRARY DESTINATION lib
+            ARCHIVE DESTINATION lib)
+  ENDIF(BUILD_SHARED_LIBS)
+  INSTALL(TARGETS archive_static
           RUNTIME DESTINATION bin
           LIBRARY DESTINATION lib
           ARCHIVE DESTINATION lib)
diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h
index 180f3e4..a89f33b 100644
--- a/Utilities/cmlibarchive/libarchive/archive.h
+++ b/Utilities/cmlibarchive/libarchive/archive.h
@@ -36,7 +36,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3006002
+#define	ARCHIVE_VERSION_NUMBER 3007002
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -154,7 +154,7 @@
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.6.2"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.7.2"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_digest.c b/Utilities/cmlibarchive/libarchive/archive_digest.c
index 3361b19..3776831 100644
--- a/Utilities/cmlibarchive/libarchive/archive_digest.c
+++ b/Utilities/cmlibarchive/libarchive/archive_digest.c
@@ -36,6 +36,11 @@
 #error Cannot use both OpenSSL and libmd.
 #endif
 
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
 /*
  * Message digest functions for Windows platform.
  */
@@ -48,6 +53,26 @@
 /*
  * Initialize a Message digest.
  */
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+static int
+win_crypto_init(Digest_CTX *ctx, const WCHAR *algo)
+{
+	NTSTATUS status;
+	ctx->valid = 0;
+
+	status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return (ARCHIVE_FAILED);
+	status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+		return (ARCHIVE_FAILED);
+	}
+
+	ctx->valid = 1;
+	return (ARCHIVE_OK);
+}
+#else
 static int
 win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
 {
@@ -70,6 +95,7 @@
 	ctx->valid = 1;
 	return (ARCHIVE_OK);
 }
+#endif
 
 /*
  * Update a Message digest.
@@ -81,23 +107,37 @@
 	if (!ctx->valid)
 		return (ARCHIVE_FAILED);
 
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCryptHashData(ctx->hHash,
+		      (PUCHAR)(uintptr_t)buf,
+		      (ULONG)len, 0);
+#else
 	CryptHashData(ctx->hash,
 		      (unsigned char *)(uintptr_t)buf,
 		      (DWORD)len, 0);
+#endif
 	return (ARCHIVE_OK);
 }
 
 static int
 win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
 {
+#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
 	DWORD siglen = (DWORD)bufsize;
+#endif
 
 	if (!ctx->valid)
 		return (ARCHIVE_FAILED);
 
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0);
+	BCryptDestroyHash(ctx->hHash);
+	BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+#else
 	CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
 	CryptDestroyHash(ctx->hash);
 	CryptReleaseContext(ctx->cryptProv, 0);
+#endif
 	ctx->valid = 0;
 	return (ARCHIVE_OK);
 }
@@ -276,7 +316,11 @@
 static int
 __archive_md5init(archive_md5_ctx *ctx)
 {
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM));
+#else
   return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
+#endif
 }
 
 static int
@@ -659,7 +703,11 @@
 static int
 __archive_sha1init(archive_sha1_ctx *ctx)
 {
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM));
+#else
   return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
+#endif
 }
 
 static int
@@ -919,7 +967,11 @@
 static int
 __archive_sha256init(archive_sha256_ctx *ctx)
 {
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM));
+#else
   return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
+#endif
 }
 
 static int
@@ -1155,7 +1207,11 @@
 static int
 __archive_sha384init(archive_sha384_ctx *ctx)
 {
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM));
+#else
   return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
+#endif
 }
 
 static int
@@ -1415,7 +1471,11 @@
 static int
 __archive_sha512init(archive_sha512_ctx *ctx)
 {
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM));
+#else
   return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
+#endif
 }
 
 static int
diff --git a/Utilities/cmlibarchive/libarchive/archive_digest_private.h b/Utilities/cmlibarchive/libarchive/archive_digest_private.h
index 9b3bd66..339b4ed 100644
--- a/Utilities/cmlibarchive/libarchive/archive_digest_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_digest_private.h
@@ -164,6 +164,15 @@
   defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
   defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
   defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+typedef struct {
+  int   valid;
+  BCRYPT_ALG_HANDLE  hAlg;
+  BCRYPT_HASH_HANDLE hHash;
+} Digest_CTX;
+#else
 #include <windows.h>
 #include <wincrypt.h>
 typedef struct {
@@ -172,6 +181,7 @@
   HCRYPTHASH  hash;
 } Digest_CTX;
 #endif
+#endif
 
 /* typedefs */
 #if defined(ARCHIVE_CRYPTO_MD5_LIBC)
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h
index 91ef0c9..0e4ccbb 100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry.h
+++ b/Utilities/cmlibarchive/libarchive/archive_entry.h
@@ -30,7 +30,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3006002
+#define	ARCHIVE_VERSION_NUMBER 3007002
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
diff --git a/Utilities/cmlibarchive/libarchive/archive_getdate.c b/Utilities/cmlibarchive/libarchive/archive_getdate.c
index 5b0b775..fc9516e 100644
--- a/Utilities/cmlibarchive/libarchive/archive_getdate.c
+++ b/Utilities/cmlibarchive/libarchive/archive_getdate.c
@@ -700,13 +700,9 @@
 	time_t		Julian;
 	int		i;
 	struct tm	*ltime;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
 	struct tm	tmbuf;
 #endif
-#if defined(HAVE__LOCALTIME64_S)
-	errno_t		terr;
-	__time64_t	tmptime;
-#endif
 
 	if (Year < 69)
 		Year += 2000;
@@ -733,15 +729,10 @@
 	Julian *= DAY;
 	Julian += Timezone;
 	Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
 	ltime = localtime_r(&Julian, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = Julian;
-	terr = _localtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		ltime = NULL;
-	else
-		ltime = &tmbuf;
 #else
 	ltime = localtime(&Julian);
 #endif
@@ -757,36 +748,21 @@
 	time_t		StartDay;
 	time_t		FutureDay;
 	struct tm	*ltime;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
 	struct tm	tmbuf;
 #endif
-#if defined(HAVE__LOCALTIME64_S)
-	errno_t		terr;
-	__time64_t	tmptime;
-#endif
-
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
 	ltime = localtime_r(&Start, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = Start;
-	terr = _localtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		ltime = NULL;
-	else
-		ltime = &tmbuf;
 #else
 	ltime = localtime(&Start);
 #endif
 	StartDay = (ltime->tm_hour + 1) % 24;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
 	ltime = localtime_r(&Future, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = Future;
-	terr = _localtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		ltime = NULL;
-	else
-		ltime = &tmbuf;
 #else
 	ltime = localtime(&Future);
 #endif
@@ -801,24 +777,15 @@
 {
 	struct tm	*tm;
 	time_t	t, now;
-#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
 	struct tm	tmbuf;
 #endif
-#if defined(HAVE__GMTIME64_S)
-	errno_t		terr;
-	__time64_t	tmptime;
-#endif
 
 	t = Start - zone;
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+	tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_GMTIME_R)
 	tm = gmtime_r(&t, &tmbuf);
-#elif defined(HAVE__GMTIME64_S)
-	tmptime = t;
-	terr = _gmtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		tm = NULL;
-	else
-		tm = &tmbuf;
 #else
 	tm = gmtime(&t);
 #endif
@@ -837,25 +804,16 @@
 	struct tm	*tm;
 	time_t	Month;
 	time_t	Year;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
 	struct tm	tmbuf;
 #endif
-#if defined(HAVE__LOCALTIME64_S)
-	errno_t		terr;
-	__time64_t	tmptime;
-#endif
 
 	if (RelMonth == 0)
 		return 0;
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
 	tm = localtime_r(&Start, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = Start;
-	terr = _localtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		tm = NULL;
-	else
-		tm = &tmbuf;
 #else
 	tm = localtime(&Start);
 #endif
@@ -995,10 +953,6 @@
 	time_t		Start;
 	time_t		tod;
 	long		tzone;
-#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S)
-	errno_t		terr;
-	__time64_t	tmptime;
-#endif
 
 	/* Clear out the parsed token array. */
 	memset(tokens, 0, sizeof(tokens));
@@ -1007,36 +961,26 @@
 	gds = &_gds;
 
 	/* Look up the current time. */
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	tm = localtime_s(&local, &now) ? NULL : &local;
+#elif defined(HAVE_LOCALTIME_R)
 	tm = localtime_r(&now, &local);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = now;
-	terr = _localtime64_s(&local, &tmptime);
-	if (terr)
-		tm = NULL;
-	else
-		tm = &local;
 #else
 	memset(&local, 0, sizeof(local));
 	tm = localtime(&now);
 #endif
 	if (tm == NULL)
 		return -1;
-#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S)
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
 	local = *tm;
 #endif
 
 	/* Look up UTC if we can and use that to determine the current
 	 * timezone offset. */
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+	gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
 	gmt_ptr = gmtime_r(&now, &gmt);
-#elif defined(HAVE__GMTIME64_S)
-	tmptime = now;
-	terr = _gmtime64_s(&gmt, &tmptime);
-	if (terr)
-		gmt_ptr = NULL;
-	else
-		gmt_ptr = &gmt;
 #else
 	memset(&gmt, 0, sizeof(gmt));
 	gmt_ptr = gmtime(&now);
@@ -1078,15 +1022,10 @@
 	 * time components instead of the local timezone. */
 	if (gds->HaveZone && gmt_ptr != NULL) {
 		now -= gds->Timezone;
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+		gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
 		gmt_ptr = gmtime_r(&now, &gmt);
-#elif defined(HAVE__GMTIME64_S)
-		tmptime = now;
-		terr = _gmtime64_s(&gmt, &tmptime);
-		if (terr)
-			gmt_ptr = NULL;
-		else
-			gmt_ptr = &gmt;
 #else
 		gmt_ptr = gmtime(&now);
 #endif
diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac.c b/Utilities/cmlibarchive/libarchive/archive_hmac.c
index 012fe15..edb3bf5 100644
--- a/Utilities/cmlibarchive/libarchive/archive_hmac.c
+++ b/Utilities/cmlibarchive/libarchive/archive_hmac.c
@@ -231,15 +231,20 @@
 __hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
 {
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
-	OSSL_PARAM params[2];
+	EVP_MAC *mac;
 
-	EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	char sha1[] = "SHA1";
+	OSSL_PARAM params[] = {
+		OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1),
+		OSSL_PARAM_END
+	};
+
+	mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
 	*ctx = EVP_MAC_CTX_new(mac);
+	EVP_MAC_free(mac);
 	if (*ctx == NULL)
 		return -1;
-	EVP_MAC_free(mac);
-	params[0] = OSSL_PARAM_construct_utf8_string("digest", "SHA1", 0);
-	params[1] = OSSL_PARAM_construct_end();
+
 	EVP_MAC_init(*ctx, key, key_len, params);
 #else
 	*ctx = HMAC_CTX_new();
diff --git a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h
index 50044a0..d0fda7f 100644
--- a/Utilities/cmlibarchive/libarchive/archive_hmac_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_hmac_private.h
@@ -77,6 +77,8 @@
 #include <openssl/opensslv.h>
 #include <openssl/hmac.h>
 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/params.h>
+
 typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
 
 #else
diff --git a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h
index ebb0670..8ac4772 100644
--- a/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_openssl_evp_private.h
@@ -33,7 +33,8 @@
 #include <openssl/evp.h>
 #include <openssl/opensslv.h>
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+    (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
 #include <stdlib.h> /* malloc, free */
 #include <string.h> /* memset */
 static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
diff --git a/Utilities/cmlibarchive/libarchive/archive_random.c b/Utilities/cmlibarchive/libarchive/archive_random.c
index 9d1aa49..a410dc0 100644
--- a/Utilities/cmlibarchive/libarchive/archive_random.c
+++ b/Utilities/cmlibarchive/libarchive/archive_random.c
@@ -51,16 +51,27 @@
 #include <pthread.h>
 #endif
 
-static void arc4random_buf(void *, size_t);
+static void la_arc4random_buf(void *, size_t);
 
 #endif /* HAVE_ARC4RANDOM_BUF */
 
 #include "archive.h"
 #include "archive_random_private.h"
 
-#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
 #include <wincrypt.h>
 #endif
+#endif
 
 #ifndef O_CLOEXEC
 #define O_CLOEXEC	0
@@ -75,6 +86,20 @@
 archive_random(void *buf, size_t nbytes)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	NTSTATUS status;
+	BCRYPT_ALG_HANDLE hAlg;
+
+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return ARCHIVE_FAILED;
+	status = BCryptGenRandom(hAlg, buf, (ULONG)nbytes, 0);
+	BCryptCloseAlgorithmProvider(hAlg, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return ARCHIVE_FAILED;
+
+	return ARCHIVE_OK;
+# else
 	HCRYPTPROV hProv;
 	BOOL success;
 
@@ -92,6 +117,10 @@
 	}
 	/* TODO: Does this case really happen? */
 	return ARCHIVE_FAILED;
+# endif
+#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+	la_arc4random_buf(buf, nbytes);
+	return ARCHIVE_OK;
 #else
 	arc4random_buf(buf, nbytes);
 	return ARCHIVE_OK;
@@ -256,7 +285,7 @@
 }
 
 static void
-arc4random_buf(void *_buf, size_t n)
+la_arc4random_buf(void *_buf, size_t n)
 {
 	uint8_t *buf = (uint8_t *)_buf;
 	_ARC4_LOCK();
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c
index b4398f1..f16ca5c 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_data_into_fd.c
@@ -95,8 +95,13 @@
 	    "archive_read_data_into_fd");
 
 	can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
-	if (!can_lseek)
+	if (!can_lseek) {
 		nulls = calloc(1, nulls_size);
+		if (!nulls) {
+			r = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+	}
 
 	while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
 	    ARCHIVE_OK) {
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
index c964d3f..ab5306d 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
@@ -1678,6 +1678,11 @@
 	else
 		t->current_filesystem->name_max = nm;
 #endif
+	if (t->current_filesystem->name_max == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Cannot determine name_max");
+		return (ARCHIVE_FAILED);
+	}
 #endif /* USE_READDIR_R */
 	return (ARCHIVE_OK);
 }
@@ -1868,8 +1873,17 @@
 
 #if defined(USE_READDIR_R)
 	/* Set maximum filename length. */
+#if defined(HAVE_STATVFS)
+	t->current_filesystem->name_max = svfs.f_namemax;
+#else
 	t->current_filesystem->name_max = sfs.f_namelen;
 #endif
+	if (t->current_filesystem->name_max == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Cannot determine name_max");
+		return (ARCHIVE_FAILED);
+	}
+#endif
 	return (ARCHIVE_OK);
 }
 
@@ -1950,6 +1964,11 @@
 #if defined(USE_READDIR_R)
 	/* Set maximum filename length. */
 	t->current_filesystem->name_max = svfs.f_namemax;
+	if (t->current_filesystem->name_max == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Cannot determine name_max");
+		return (ARCHIVE_FAILED);
+	}
 #endif
 	return (ARCHIVE_OK);
 }
@@ -2004,6 +2023,11 @@
 	else
 		t->current_filesystem->name_max = nm;
 #  endif /* _PC_NAME_MAX */
+	if (t->current_filesystem->name_max == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Cannot determine name_max");
+		return (ARCHIVE_FAILED);
+	}
 #endif /* USE_READDIR_R */
 	return (ARCHIVE_OK);
 }
@@ -2554,7 +2578,11 @@
 #else
 		if (tree_enter_working_dir(t) != 0)
 			return NULL;
+#ifdef HAVE_LSTAT
 		if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#else
+		if (la_stat(tree_current_access_path(t), &t->lst) != 0)
+#endif
 #endif
 			return NULL;
 		t->flags |= hasLstat;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c
index f9d1395..f92a78a 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_windows.c
@@ -418,9 +418,19 @@
 	    FILE_FLAG_OPEN_REPARSE_POINT;
 	int ret;
 
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(path, 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+	    OPEN_EXISTING, &createExParams);
+#else
 	h = CreateFileW(path, 0,
 	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 	    OPEN_EXISTING, flag, NULL);
+#endif
 	if (h == INVALID_HANDLE_VALUE) {
 		la_dosmaperr(GetLastError());
 		return (-1);
@@ -1067,16 +1077,29 @@
 	if (archive_entry_filetype(entry) == AE_IFREG &&
 	    archive_entry_size(entry) > 0) {
 		DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 		if (t->async_io)
 			flags |= FILE_FLAG_OVERLAPPED;
 		if (t->direct_io)
 			flags |= FILE_FLAG_NO_BUFFERING;
 		else
 			flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileFlags = flags;
+		t->entry_fh = CreateFile2(tree_current_access_path(t),
+		    GENERIC_READ,
+		    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+		    OPEN_EXISTING, &createExParams);
+#else
 		t->entry_fh = CreateFileW(tree_current_access_path(t),
 		    GENERIC_READ,
 		    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 		    NULL, OPEN_EXISTING, flags, NULL);
+#endif
 		if (t->entry_fh == INVALID_HANDLE_VALUE) {
 			la_dosmaperr(GetLastError());
 			archive_set_error(&a->archive, errno,
@@ -1547,6 +1570,9 @@
 {
 	HANDLE handle;
 	int r = 0;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
 	if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
 		return (0);
@@ -1560,8 +1586,16 @@
 	if ((t->flags & needsRestoreTimes) == 0)
 		return (r);
 
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+	handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES,
+		    0, OPEN_EXISTING, &createExParams);
+#else
 	handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
 		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
 	if (handle == INVALID_HANDLE_VALUE) {
 		errno = EINVAL;
 		return (-1);
@@ -2046,12 +2080,24 @@
 	HANDLE h;
 	int r;
 	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
-	
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
 	if (sim_lstat && tree_current_is_physical_link(t))
 		flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(tree_current_access_path(t), 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+	    OPEN_EXISTING, &createExParams);
+#else
 	h = CreateFileW(tree_current_access_path(t), 0,
 	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 	    OPEN_EXISTING, flag, NULL);
+#endif
 	if (h == INVALID_HANDLE_VALUE) {
 		la_dosmaperr(GetLastError());
 		t->tree_errno = errno;
@@ -2257,7 +2303,10 @@
 		} else {
 			WIN32_FIND_DATAW findData;
 			DWORD flag, desiredAccess;
-	
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
 			h = FindFirstFileW(path, &findData);
 			if (h == INVALID_HANDLE_VALUE) {
 				la_dosmaperr(GetLastError());
@@ -2279,9 +2328,18 @@
 			} else
 				desiredAccess = GENERIC_READ;
 
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileFlags = flag;
+			h = CreateFile2(path, desiredAccess,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			    OPEN_EXISTING, &createExParams);
+#else
 			h = CreateFileW(path, desiredAccess,
 			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 			    OPEN_EXISTING, flag, NULL);
+#endif
 			if (h == INVALID_HANDLE_VALUE) {
 				la_dosmaperr(GetLastError());
 				archive_set_error(&a->archive, errno,
@@ -2342,9 +2400,19 @@
 		if (fd >= 0) {
 			h = (HANDLE)_get_osfhandle(fd);
 		} else {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+			h = CreateFile2(path, GENERIC_READ,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			    OPEN_EXISTING, &createExParams);
+#else
 			h = CreateFileW(path, GENERIC_READ,
 			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
 			    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
 			if (h == INVALID_HANDLE_VALUE) {
 				la_dosmaperr(GetLastError());
 				archive_set_error(&a->archive, errno,
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c
index 101dae6..03719e8 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_open_file.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_open_file.c
@@ -154,10 +154,10 @@
 #ifdef __ANDROID__
         /* fileno() isn't safe on all platforms ... see above. */
 	if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
-#elif HAVE_FSEEKO
-	if (fseeko(mine->f, skip, SEEK_CUR) != 0)
 #elif HAVE__FSEEKI64
 	if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE_FSEEKO
+	if (fseeko(mine->f, skip, SEEK_CUR) != 0)
 #else
 	if (fseek(mine->f, skip, SEEK_CUR) != 0)
 #endif
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3
index b2db4cb..162b79d 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_set_options.3
+++ b/Utilities/cmlibarchive/libarchive/archive_read_set_options.3
@@ -255,6 +255,27 @@
 Without this option, only the contents of
 the first concatenated archive would be read.
 .El
+.It Format zip
+.Bl -tag -compact -width indent
+.It Cm compat-2x
+Libarchive 2.x incorrectly encoded Unicode filenames on
+some platforms.
+This option mimics the libarchive 2.x filename handling
+so that such archives can be read correctly.
+.It Cm hdrcharset
+The value is used as a character set name that will be
+used when translating file names.
+.It Cm ignorecrc32
+Skip the CRC32 check.
+Mostly used for testing.
+.It Cm mac-ext
+Support Mac OS metadata extension that records data in special
+files beginning with a period and underscore.
+Defaults to enabled on Mac OS, disabled on other platforms.
+Use
+.Cm !mac-ext
+to disable.
+.El
 .El
 .\"
 .Sh ERRORS
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c
index a5243af..9e5f6d9 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_bzip2.c
@@ -230,7 +230,7 @@
 
 	/* Empty our output buffer. */
 	state->stream.next_out = state->out_block;
-	state->stream.avail_out = state->out_block_size;
+	state->stream.avail_out = (uint32_t)state->out_block_size;
 
 	/* Try to fill the output buffer. */
 	for (;;) {
@@ -288,7 +288,7 @@
 			return (ARCHIVE_FATAL);
 		}
 		state->stream.next_in = (char *)(uintptr_t)read_buf;
-		state->stream.avail_in = ret;
+		state->stream.avail_in = (uint32_t)ret;
 		/* There is no more data, return whatever we have. */
 		if (ret == 0) {
 			state->eof = 1;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c
index 1e99542..d0fc1a8 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_lz4.c
@@ -584,7 +584,7 @@
 		    state->out_block + prefix64k, (int)compressed_size,
 		    state->flags.block_maximum_size,
 		    state->out_block,
-		    prefix64k);
+		    (int)prefix64k);
 #else
 		uncompressed_size = LZ4_decompress_safe_withPrefix64k(
 		    read_buf + 4,
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c
index c66c247..802165c 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_uu.c
@@ -41,6 +41,7 @@
 #endif
 
 #include "archive.h"
+#include "archive_entry.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
@@ -61,12 +62,17 @@
 #define ST_UUEND	2
 #define ST_READ_BASE64	3
 #define ST_IGNORE	4
+	mode_t		mode;
+	int		mode_set;
+	char		*name;
 };
 
 static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
 		    struct archive_read_filter *filter);
 static int	uudecode_bidder_init(struct archive_read_filter *);
 
+static int	uudecode_read_header(struct archive_read_filter *,
+		    struct archive_entry *entry);
 static ssize_t	uudecode_filter_read(struct archive_read_filter *,
 		    const void **);
 static int	uudecode_filter_close(struct archive_read_filter *);
@@ -359,6 +365,7 @@
 uudecode_reader_vtable = {
 	.read = uudecode_filter_read,
 	.close = uudecode_filter_close,
+	.read_header = uudecode_read_header
 };
 
 static int
@@ -389,6 +396,8 @@
 	uudecode->in_allocated = IN_BUFF_SIZE;
 	uudecode->out_buff = out_buff;
 	uudecode->state = ST_FIND_HEAD;
+	uudecode->mode_set = 0;
+	uudecode->name = NULL;
 	self->vtable = &uudecode_reader_vtable;
 
 	return (ARCHIVE_OK);
@@ -434,6 +443,22 @@
 	return (ARCHIVE_OK);
 }
 
+static int
+uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+
+	struct uudecode *uudecode;
+	uudecode = (struct uudecode *)self->data;
+
+	if (uudecode->mode_set != 0)
+		archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
+
+	if (uudecode->name != NULL)
+		archive_entry_set_pathname(entry, uudecode->name);
+
+	return (ARCHIVE_OK);
+}
+
 static ssize_t
 uudecode_filter_read(struct archive_read_filter *self, const void **buff)
 {
@@ -443,7 +468,7 @@
 	ssize_t avail_in, ravail;
 	ssize_t used;
 	ssize_t total;
-	ssize_t len, llen, nl;
+	ssize_t len, llen, nl, namelen;
 
 	uudecode = (struct uudecode *)self->data;
 
@@ -551,6 +576,28 @@
 					uudecode->state = ST_READ_UU;
 				else
 					uudecode->state = ST_READ_BASE64;
+				uudecode->mode = (mode_t)(
+				    ((int)(b[l] - '0') * 64) +
+				    ((int)(b[l+1] - '0') * 8) +
+				     (int)(b[l+2] - '0'));
+				uudecode->mode_set = 1;
+				namelen = len - nl - 4 - l;
+				if (namelen > 1) {
+					if (uudecode->name != NULL)
+						free(uudecode->name);
+					uudecode->name = malloc(namelen + 1);
+			                if (uudecode->name == NULL) {
+					archive_set_error(
+					    &self->archive->archive,
+					    ENOMEM,
+					    "Can't allocate data for uudecode");
+						return (ARCHIVE_FATAL);
+					}
+					strncpy(uudecode->name,
+					    (const char *)(b + l + 4),
+					    namelen);
+					uudecode->name[namelen] = '\0';
+				}
 			}
 			break;
 		case ST_READ_UU:
@@ -683,6 +730,7 @@
 	uudecode = (struct uudecode *)self->data;
 	free(uudecode->in_buff);
 	free(uudecode->out_buff);
+	free(uudecode->name);
 	free(uudecode);
 
 	return (ARCHIVE_OK);
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
index 29d4d62..8d20d7c 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
@@ -115,9 +115,9 @@
 	unsigned prefix;
 
 	/* Zstd frame magic values */
-	const unsigned zstd_magic = 0xFD2FB528U;
-	const unsigned zstd_magic_skippable_start = 0x184D2A50U;
-	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
+	unsigned zstd_magic = 0xFD2FB528U;
+	unsigned zstd_magic_skippable_start = 0x184D2A50U;
+	unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
 
 	(void) self; /* UNUSED */
 
@@ -170,7 +170,7 @@
 zstd_bidder_init(struct archive_read_filter *self)
 {
 	struct private_data *state;
-	const size_t out_block_size = ZSTD_DStreamOutSize();
+	size_t out_block_size = ZSTD_DStreamOutSize();
 	void *out_block;
 	ZSTD_DStream *dstream;
 
@@ -211,6 +211,7 @@
 	ssize_t avail_in;
 	ZSTD_outBuffer out;
 	ZSTD_inBuffer in;
+	size_t ret;
 
 	state = (struct private_data *)self->data;
 
@@ -219,7 +220,7 @@
 	/* Try to fill the output buffer. */
 	while (out.pos < out.size && !state->eof) {
 		if (!state->in_frame) {
-			const size_t ret = ZSTD_initDStream(state->dstream);
+			ret = ZSTD_initDStream(state->dstream);
 			if (ZSTD_isError(ret)) {
 				archive_set_error(&self->archive->archive,
 				    ARCHIVE_ERRNO_MISC,
@@ -249,8 +250,7 @@
 		in.pos = 0;
 
 		{
-			const size_t ret =
-			    ZSTD_decompressStream(state->dstream, &out, &in);
+			ret = ZSTD_decompressStream(state->dstream, &out, &in);
 
 			if (ZSTD_isError(ret)) {
 				archive_set_error(&self->archive->archive,
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
index a4d9dcf..0bfbf1f 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_7zip.c
@@ -41,6 +41,9 @@
 #ifdef HAVE_ZLIB_H
 #include <cm3p/zlib.h>
 #endif
+#ifdef HAVE_ZSTD_H
+#include <cm3p/zstd.h>
+#endif
 
 #ifdef __clang_analyzer__
 #include <assert.h>
@@ -84,8 +87,11 @@
 #define _7Z_IA64	0x03030401
 #define _7Z_ARM		0x03030501
 #define _7Z_ARMTHUMB	0x03030701
+#define _7Z_ARM64	0xa
 #define _7Z_SPARC	0x03030805
 
+#define _7Z_ZSTD	0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
 /*
  * 7-Zip header property IDs.
  */
@@ -114,6 +120,30 @@
 #define kEncodedHeader		0x17
 #define kDummy			0x19
 
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_HIDDEN
+#define FILE_ATTRIBUTE_HIDDEN 0x00000002
+#endif
+
+#ifndef FILE_ATTRIBUTE_SYSTEM
+#define FILE_ATTRIBUTE_SYSTEM 0x00000004
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
 struct _7z_digests {
 	unsigned char	*defineds;
 	uint32_t	*digests;
@@ -282,6 +312,11 @@
 	z_stream		 stream;
 	int			 stream_valid;
 #endif
+	/* Decoding Zstandard data. */
+#if HAVE_ZSTD_H
+	ZSTD_DStream		 *zstd_dstream;
+	int		         zstdstream_valid;
+#endif
 	/* Decoding PPMd data. */
 	int			 ppmd7_stat;
 	CPpmd7			 ppmd7_context;
@@ -401,6 +436,9 @@
 		    int);
 static void	x86_Init(struct _7zip *);
 static size_t	x86_Convert(struct _7zip *, uint8_t *, size_t);
+static void	arm_Init(struct _7zip *);
+static size_t	arm_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t	arm64_Convert(struct _7zip *, uint8_t *, size_t);
 static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
 
 
@@ -729,6 +767,37 @@
 		archive_entry_set_size(entry, 0);
 	}
 
+	// These attributes are supported by the windows implementation of archive_write_disk.
+	const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
+
+	if (zip_entry->attr & supported_attrs) {
+		char *fflags_text, *ptr;
+		/* allocate for "rdonly,hidden,system," */
+		fflags_text = malloc(22 * sizeof(char));
+		if (fflags_text != NULL) {
+			ptr = fflags_text; 
+			if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) { 
+ 			strcpy(ptr, "rdonly,"); 
+ 			ptr = ptr + 7; 
+ 		} 
+ 		if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) { 
+ 			strcpy(ptr, "hidden,"); 
+ 			ptr = ptr + 7; 
+ 		} 
+ 		if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) { 
+ 			strcpy(ptr, "system,"); 
+ 			ptr = ptr + 7; 
+ 		} 
+ 		if (ptr > fflags_text) { 
+ 			/* Delete trailing comma */ 
+ 			*(ptr - 1) = '\0'; 
+ 			archive_entry_copy_fflags_text(entry, 
+				fflags_text); 
+ 		} 
+ 		free(fflags_text); 
+		}
+	}
+
 	/* If there's no body, force read_data() to return EOF immediately. */
 	if (zip->entry_bytes_remaining < 1)
 		zip->end_of_entry = 1;
@@ -1034,10 +1103,13 @@
 	case _7Z_COPY:
 	case _7Z_BZ2:
 	case _7Z_DEFLATE:
+	case _7Z_ZSTD:
 	case _7Z_PPMD:
 		if (coder2 != NULL) {
 			if (coder2->codec != _7Z_X86 &&
-			    coder2->codec != _7Z_X86_BCJ2) {
+			    coder2->codec != _7Z_X86_BCJ2 &&
+			    coder2->codec != _7Z_ARM &&
+			    coder2->codec != _7Z_ARM64) {
 				archive_set_error(&a->archive,
 				    ARCHIVE_ERRNO_MISC,
 				    "Unsupported filter %lx for %lx",
@@ -1048,6 +1120,8 @@
 			zip->bcj_state = 0;
 			if (coder2->codec == _7Z_X86)
 				x86_Init(zip);
+			else if (coder2->codec == _7Z_ARM)
+				arm_Init(zip);
 		}
 		break;
 	default:
@@ -1144,6 +1218,12 @@
 				filters[fi].id = LZMA_FILTER_ARMTHUMB;
 				fi++;
 				break;
+#ifdef LZMA_FILTER_ARM64
+			case _7Z_ARM64:
+				filters[fi].id = LZMA_FILTER_ARM64;
+				fi++;
+				break;
+#endif
 			case _7Z_SPARC:
 				filters[fi].id = LZMA_FILTER_SPARC;
 				fi++;
@@ -1229,6 +1309,22 @@
 		    "BZ2 codec is unsupported");
 		return (ARCHIVE_FAILED);
 #endif
+	case _7Z_ZSTD:
+	{
+#if defined(HAVE_ZSTD_H)
+		if (zip->zstdstream_valid) {
+			ZSTD_freeDStream(zip->zstd_dstream);
+			zip->zstdstream_valid = 0;
+		}
+		zip->zstd_dstream = ZSTD_createDStream();
+		zip->zstdstream_valid = 1;
+		break;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"ZSTD codec is unsupported");
+		return (ARCHIVE_FAILED);
+#endif
+	}
 	case _7Z_DEFLATE:
 #ifdef HAVE_ZLIB_H
 		if (zip->stream_valid)
@@ -1299,6 +1395,7 @@
 	case _7Z_IA64:
 	case _7Z_ARM:
 	case _7Z_ARMTHUMB:
+	case _7Z_ARM64:
 	case _7Z_SPARC:
 	case _7Z_DELTA:
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -1443,9 +1540,9 @@
 #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
 	case _7Z_BZ2:
 		zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
-		zip->bzstream.avail_in = t_avail_in;
+		zip->bzstream.avail_in = (uint32_t)t_avail_in;
 		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
-		zip->bzstream.avail_out = t_avail_out;
+		zip->bzstream.avail_out = (uint32_t)t_avail_out;
 		r = BZ2_bzDecompress(&(zip->bzstream));
 		switch (r) {
 		case BZ_STREAM_END: /* Found end of stream. */
@@ -1495,6 +1592,22 @@
 		t_avail_out = zip->stream.avail_out;
 		break;
 #endif
+#ifdef HAVE_ZSTD_H
+	case _7Z_ZSTD:
+	{
+		ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
+		ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+
+		size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+		if (ZSTD_isError(zret)) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+			return ARCHIVE_FAILED;
+		}
+		t_avail_in -= input.pos;
+		t_avail_out -= output.pos;
+		break;
+	}
+#endif
 	case _7Z_PPMD:
 	{
 		uint64_t flush_bytes;
@@ -1579,16 +1692,23 @@
 	/*
 	 * Decord BCJ.
 	 */
-	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
-		size_t l = x86_Convert(zip, buff, *outbytes);
-		zip->odd_bcj_size = *outbytes - l;
-		if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
-		    o_avail_in && ret != ARCHIVE_EOF) {
-			memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
-			    zip->odd_bcj_size);
-			*outbytes = l;
-		} else
-			zip->odd_bcj_size = 0;
+	if (zip->codec != _7Z_LZMA2) {
+		if (zip->codec2 == _7Z_X86) {
+			size_t l = x86_Convert(zip, buff, *outbytes);
+
+			zip->odd_bcj_size = *outbytes - l;
+			if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
+		    	o_avail_in && ret != ARCHIVE_EOF) {
+				memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
+			    	zip->odd_bcj_size);
+				*outbytes = l;
+			} else
+				zip->odd_bcj_size = 0;
+		} else if (zip->codec2 == _7Z_ARM) {
+			*outbytes = arm_Convert(zip, buff, *outbytes);
+		} else if (zip->codec2 == _7Z_ARM64) {
+			*outbytes = arm64_Convert(zip, buff, *outbytes);
+		}
 	}
 
 	/*
@@ -2612,6 +2732,28 @@
 			entries[i].flg |= HAS_STREAM;
 		/* The high 16 bits of attributes is a posix file mode. */
 		entries[i].mode = entries[i].attr >> 16;
+
+		if (!(entries[i].attr & FILE_ATTRIBUTE_UNIX_EXTENSION)) {
+			// Only windows permissions specified for this entry. Translate to
+			// reasonable corresponding unix permissions.
+
+			if (entries[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
+				if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+					// Read-only directory.
+					entries[i].mode = AE_IFDIR | 0555;
+				} else {
+					// Read-write directory.
+					entries[i].mode = AE_IFDIR | 0755;
+				}
+			} else if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+				// Readonly file.
+				entries[i].mode = AE_IFREG | 0444;
+			} else {
+				// Assume read-write file.
+				entries[i].mode = AE_IFREG | 0644;
+			}
+		}
+
 		if (entries[i].flg & HAS_STREAM) {
 			if ((size_t)sindex >= si->ss.unpack_streams)
 				return (-1);
@@ -2652,7 +2794,7 @@
 			}
 			entries[i].ssIndex = -1;
 		}
-		if (entries[i].attr & 0x01)
+		if (entries[i].attr & FILE_ATTRIBUTE_READONLY)
 			entries[i].mode &= ~0222;/* Read only. */
 
 		if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
@@ -3737,6 +3879,116 @@
 	return (bufferPos);
 }
 
+static void
+arm_Init(struct _7zip *zip)
+{
+	zip->bcj_ip = 8;
+}
+
+static size_t
+arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+	// in https://git.tukaani.org/xz-embedded.git
+
+	/*
+	 * Branch/Call/Jump (BCJ) filter decoders
+	 *
+	 * Authors: Lasse Collin <lasse.collin@tukaani.org>
+	 *          Igor Pavlov <https://7-zip.org/>
+	 *
+	 * This file has been put into the public domain.
+	 * You can do whatever you want with this file.
+	 */
+
+	size_t i;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		if (buf[i + 3] == 0xEB) {
+			// Calculate the transformed addr.
+			addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+				| ((uint32_t)buf[i + 2] << 16);
+			addr <<= 2;
+			addr -= zip->bcj_ip + (uint32_t)i;
+			addr >>= 2;
+
+			// Store the transformed addr in buf.
+			buf[i] = (uint8_t)addr;
+			buf[i + 1] = (uint8_t)(addr >> 8);
+			buf[i + 2] = (uint8_t)(addr >> 16);
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
+static size_t
+arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+	// in https://git.tukaani.org/xz-embedded.git
+
+	/*
+	 * Branch/Call/Jump (BCJ) filter decoders
+	 *
+	 * Authors: Lasse Collin <lasse.collin@tukaani.org>
+	 *          Igor Pavlov <https://7-zip.org/>
+	 *
+	 * This file has been put into the public domain.
+	 * You can do whatever you want with this file.
+	 */
+
+	size_t i;
+	uint32_t instr;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		instr = (uint32_t)buf[i]
+			| ((uint32_t)buf[i+1] << 8)
+			| ((uint32_t)buf[i+2] << 16)
+			| ((uint32_t)buf[i+3] << 24);
+
+		if ((instr >> 26) == 0x25) {
+			/* BL instruction */
+			addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2);
+			instr = 0x94000000 | (addr & 0x03FFFFFF);
+
+			buf[i]   = (uint8_t)instr;
+			buf[i+1] = (uint8_t)(instr >> 8);
+			buf[i+2] = (uint8_t)(instr >> 16);
+			buf[i+3] = (uint8_t)(instr >> 24);
+		} else if ((instr & 0x9F000000) == 0x90000000) {
+			/* ADRP instruction */
+			addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+			/* Only convert values in the range +/-512 MiB. */
+			if ((addr + 0x020000) & 0x1C0000)
+				continue;
+
+			addr -= (zip->bcj_ip + (uint32_t)i) >> 12;
+
+			instr &= 0x9000001F;
+			instr |= (addr & 3) << 29;
+			instr |= (addr & 0x03FFFC) << 3;
+			instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+			buf[i]   = (uint8_t)instr;
+			buf[i+1] = (uint8_t)(instr >> 8);
+			buf[i+2] = (uint8_t)(instr >> 16);
+			buf[i+3] = (uint8_t)(instr >> 24);
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
 /*
  * Brought from LZMA SDK.
  *
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
index 6fcfbfc..e57b8c3 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
@@ -2294,10 +2294,10 @@
 		 		   (br->cache_buffer << 48) |
 				    ((uint64_t)strm->next_in[1]) << 40 |
 				    ((uint64_t)strm->next_in[0]) << 32 |
-				    ((uint32_t)strm->next_in[3]) << 24 |
-				    ((uint32_t)strm->next_in[2]) << 16 |
-				    ((uint32_t)strm->next_in[5]) << 8 |
-				     (uint32_t)strm->next_in[4];
+				    ((uint64_t)strm->next_in[3]) << 24 |
+				    ((uint64_t)strm->next_in[2]) << 16 |
+				    ((uint64_t)strm->next_in[5]) << 8 |
+				     (uint64_t)strm->next_in[4];
 				strm->next_in += 6;
 				strm->avail_in -= 6;
 				br->cache_avail += 6 * 8;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c
index 6b8ae33..9adcfd3 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cpio.c
@@ -441,7 +441,7 @@
 
 	/* Compare name to "TRAILER!!!" to test for end-of-archive. */
 	if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
-	    11) == 0) {
+	    10) == 0) {
 		/* TODO: Store file location of start of block. */
 		archive_clear_error(&a->archive);
 		return (ARCHIVE_EOF);
@@ -985,14 +985,14 @@
 static int64_t
 le4(const unsigned char *p)
 {
-	return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8));
+	return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8));
 }
 
 
 static int64_t
 be4(const unsigned char *p)
 {
-	return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3]));
+	return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
 }
 
 /*
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
index 91b9187..a6219fa 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_iso9660.c
@@ -1901,7 +1901,7 @@
 	 * NUMBER of RRIP "PX" extension.
 	 * Note: Old mkisofs did not record that FILE SERIAL NUMBER
 	 * in ISO images.
-	 * Note2: xorriso set 0 to the location of a symlink file. 
+	 * Note2: xorriso set 0 to the location of a symlink file.
 	 */
 	if (file->size == 0 && location >= 0) {
 		/* If file->size is zero, its location points wrong place,
@@ -1955,7 +1955,7 @@
 			 * made by makefs is not zero and its location is
 			 * the same as those of next regular file. That is
 			 * the same as hard like file and it causes unexpected
-			 * error. 
+			 * error.
 			 */
 			if (file->size > 0 &&
 			    (file->mode & AE_IFMT) == AE_IFLNK) {
@@ -2747,7 +2747,7 @@
 			 * If directory entries all which are descendant of
 			 * rr_moved are still remaining, expose their.
 			 */
-			if (iso9660->re_files.first != NULL && 
+			if (iso9660->re_files.first != NULL &&
 			    iso9660->rr_moved != NULL &&
 			    iso9660->rr_moved->rr_moved_has_re_only)
 				/* Expose "rr_moved" entry. */
@@ -3182,11 +3182,11 @@
 static time_t
 time_from_tm(struct tm *t)
 {
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
         /* Use platform timegm() if available. */
         return (timegm(t));
-#elif HAVE__MKGMTIME64
-        return (_mkgmtime64(t));
 #else
         /* Else use direct calculation using POSIX assumptions. */
         /* First, fix up tm_yday based on the year/month/day. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
index 8b7bf66..1c64b29 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_lha.c
@@ -1819,7 +1819,7 @@
 		 * remove the statement which will not be executed. */
 #undef bswap16
 #ifndef __has_builtin
-#  define __has_builtin(x) 0
+#define __has_builtin(x) 0
 #endif
 #if defined(_MSC_VER) && _MSC_VER >= 1400  /* Visual Studio */
 #  define bswap16(x) _byteswap_ushort(x)
@@ -1827,7 +1827,7 @@
 /* GCC 4.8 and later has __builtin_bswap16() */
 #  define bswap16(x) __builtin_bswap16(x)
 #elif defined(__clang__) && __has_builtin(__builtin_bswap16)
-/* All clang versions have __builtin_bswap16() */
+/* Newer clang versions have __builtin_bswap16() */
 #  define bswap16(x) __builtin_bswap16(x)
 #else
 #  define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
@@ -2012,10 +2012,10 @@
 				    ((uint64_t)strm->next_in[0]) << 48 |
 				    ((uint64_t)strm->next_in[1]) << 40 |
 				    ((uint64_t)strm->next_in[2]) << 32 |
-				    ((uint32_t)strm->next_in[3]) << 24 |
-				    ((uint32_t)strm->next_in[4]) << 16 |
-				    ((uint32_t)strm->next_in[5]) << 8 |
-				     (uint32_t)strm->next_in[6];
+				    ((uint64_t)strm->next_in[3]) << 24 |
+				    ((uint64_t)strm->next_in[4]) << 16 |
+				    ((uint64_t)strm->next_in[5]) << 8 |
+				     (uint64_t)strm->next_in[6];
 				strm->next_in += 7;
 				strm->avail_in -= 7;
 				br->cache_avail += 7 * 8;
@@ -2025,10 +2025,10 @@
 		 		   (br->cache_buffer << 48) |
 				    ((uint64_t)strm->next_in[0]) << 40 |
 				    ((uint64_t)strm->next_in[1]) << 32 |
-				    ((uint32_t)strm->next_in[2]) << 24 |
-				    ((uint32_t)strm->next_in[3]) << 16 |
-				    ((uint32_t)strm->next_in[4]) << 8 |
-				     (uint32_t)strm->next_in[5];
+				    ((uint64_t)strm->next_in[2]) << 24 |
+				    ((uint64_t)strm->next_in[3]) << 16 |
+				    ((uint64_t)strm->next_in[4]) << 8 |
+				     (uint64_t)strm->next_in[5];
 				strm->next_in += 6;
 				strm->avail_in -= 6;
 				br->cache_avail += 6 * 8;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
index 2bc3ba0..a5fa30e 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
@@ -1280,7 +1280,13 @@
 				mtree->fd = -1;
 				st = NULL;
 			}
-		} else if (lstat(path, st) == -1) {
+		}
+#ifdef HAVE_LSTAT
+		else if (lstat(path, st) == -1)
+#else
+		else if (la_stat(path, st) == -1)
+#endif
+		{
 			st = NULL;
 		}
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
index 41d6cb2..a1c5495 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
@@ -1064,7 +1064,7 @@
 		      return (ARCHIVE_FATAL);
 	      }
 	      p = h;
-	      crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read);
+	      crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read);
 	      __archive_read_consume(a, to_read);
 	      skip -= to_read;
       }
@@ -1832,13 +1832,9 @@
   struct tm *tm;
   time_t t;
   long nsec;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
   struct tm tmbuf;
 #endif
-#if defined(HAVE__LOCALTIME64_S)
-  errno_t terr;
-  __time64_t tmptime;
-#endif
 
   if (p + 2 > endp)
     return (-1);
@@ -1870,15 +1866,10 @@
         rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
         p++;
       }
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+      tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
       tm = localtime_r(&t, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-      tmptime = t;
-      terr = _localtime64_s(&tmbuf, &tmptime);
-      if (terr)
-        tm = NULL;
-      else
-        tm = &tmbuf;
 #else
       tm = localtime(&t);
 #endif
@@ -3451,7 +3442,7 @@
   prog = calloc(1, sizeof(*prog));
   if (!prog)
     return NULL;
-  prog->fingerprint = crc32(0, bytes, length) | ((uint64_t)length << 32);
+  prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32);
 
   if (membr_bits(&br, 1))
   {
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
index aa7b861..7f1efb8 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
@@ -2475,7 +2475,7 @@
 		 * `stored_crc32` info filled in. */
 		if(rar->file.stored_crc32 > 0) {
 			rar->file.calculated_crc32 =
-				crc32(rar->file.calculated_crc32, p, to_read);
+				crc32(rar->file.calculated_crc32, p, (unsigned int)to_read);
 		}
 
 		/* Check if the file uses an optional BLAKE2sp checksum
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
index 2732996..61ab29e 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
@@ -530,11 +530,11 @@
 static time_t
 time_from_tm(struct tm *t)
 {
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
         /* Use platform timegm() if available. */
         return (timegm(t));
-#elif HAVE__MKGMTIME64
-        return (_mkgmtime64(t));
 #else
         /* Else use direct calculation using POSIX assumptions. */
         /* First, fix up tm_yday based on the year/month/day. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
index 330df58..efed86d 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
@@ -1127,7 +1127,7 @@
 			x |= p[1] - '0';
 		else
 			return (-1);
-		
+
 		*b++ = x;
 		bsize--;
 		p += 2;
@@ -1139,11 +1139,11 @@
 static time_t
 time_from_tm(struct tm *t)
 {
-#if HAVE_TIMEGM
+#if HAVE__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
         /* Use platform timegm() if available. */
         return (timegm(t));
-#elif HAVE__MKGMTIME64
-        return (_mkgmtime64(t));
 #else
         /* Else use direct calculation using POSIX assumptions. */
         /* First, fix up tm_yday based on the year/month/day. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
index e126ae3..e8b20f5 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
@@ -2186,11 +2186,11 @@
 
 	/* Setup buffer boundaries. */
 	zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
-	zip->bzstream.avail_in = in_bytes;
+	zip->bzstream.avail_in = (uint32_t)in_bytes;
 	zip->bzstream.total_in_hi32 = 0;
 	zip->bzstream.total_in_lo32 = 0;
 	zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
-	zip->bzstream.avail_out = zip->uncompressed_buffer_size;
+	zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size;
 	zip->bzstream.total_out_hi32 = 0;
 	zip->bzstream.total_out_lo32 = 0;
 
@@ -2227,7 +2227,7 @@
 	to_consume = zip->bzstream.total_in_lo32;
 	__archive_read_consume(a, to_consume);
 
-	total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) +
+	total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) |
 	    zip->bzstream.total_out_lo32;
 
 	zip->entry_bytes_remaining -= to_consume;
diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c
index 69458e1..accf526 100644
--- a/Utilities/cmlibarchive/libarchive/archive_string.c
+++ b/Utilities/cmlibarchive/libarchive/archive_string.c
@@ -1324,6 +1324,10 @@
 }
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#  define GetOEMCP() CP_OEMCP
+# endif
+
 static unsigned
 my_atoi(const char *p)
 {
diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c
index 83586b5..0680711 100644
--- a/Utilities/cmlibarchive/libarchive/archive_util.c
+++ b/Utilities/cmlibarchive/libarchive/archive_util.c
@@ -42,9 +42,20 @@
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
-#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
 #include <wincrypt.h>
 #endif
+#endif
 #ifdef HAVE_ZLIB_H
 #include <cm3p/zlib.h>
 #endif
@@ -233,14 +244,16 @@
 		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
 		L'u', L'v', L'w', L'x', L'y', L'z'
 	};
-	HCRYPTPROV hProv;
 	struct archive_wstring temp_name;
 	wchar_t *ws;
 	DWORD attr;
 	wchar_t *xp, *ep;
 	int fd;
-
-	hProv = (HCRYPTPROV)NULL;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCRYPT_ALG_HANDLE hAlg = NULL;
+#else
+	HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+#endif
 	fd = -1;
 	ws = NULL;
 
@@ -314,23 +327,42 @@
 			abort();
 	}
 
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
+		NULL, 0))) {
+		la_dosmaperr(GetLastError());
+		goto exit_tmpfile;
+	}
+#else
 	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
 		CRYPT_VERIFYCONTEXT)) {
 		la_dosmaperr(GetLastError());
 		goto exit_tmpfile;
 	}
+#endif
 
 	for (;;) {
 		wchar_t *p;
 		HANDLE h;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
 		/* Generate a random file name through CryptGenRandom(). */
 		p = xp;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+		if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
+		    (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+#else
 		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
 		    (BYTE*)p)) {
 			la_dosmaperr(GetLastError());
 			goto exit_tmpfile;
 		}
+#endif
 		for (; p < ep; p++)
 			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
 
@@ -347,6 +379,17 @@
 			/* mkstemp */
 			attr = FILE_ATTRIBUTE_NORMAL;
 		}
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileAttributes = attr & 0xFFFF;
+		createExParams.dwFileFlags = attr & 0xFFF00000;
+		h = CreateFile2(ws,
+		    GENERIC_READ | GENERIC_WRITE | DELETE,
+		    0,/* Not share */
+			CREATE_NEW,
+			&createExParams);
+#else
 		h = CreateFileW(ws,
 		    GENERIC_READ | GENERIC_WRITE | DELETE,
 		    0,/* Not share */
@@ -354,6 +397,7 @@
 		    CREATE_NEW,/* Create a new file only */
 		    attr,
 		    NULL);
+#endif
 		if (h == INVALID_HANDLE_VALUE) {
 			/* The same file already exists. retry with
 			 * a new filename. */
@@ -372,8 +416,13 @@
 			break;/* success! */
 	}
 exit_tmpfile:
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	if (hAlg != NULL)
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+#else
 	if (hProv != (HCRYPTPROV)NULL)
 		CryptReleaseContext(hProv, 0);
+#endif
 	free(ws);
 	if (template == temp_name.s)
 		archive_wstring_free(&temp_name);
diff --git a/Utilities/cmlibarchive/libarchive/archive_windows.c b/Utilities/cmlibarchive/libarchive/archive_windows.c
index 624e270..ebc5eef 100644
--- a/Utilities/cmlibarchive/libarchive/archive_windows.c
+++ b/Utilities/cmlibarchive/libarchive/archive_windows.c
@@ -234,7 +234,11 @@
 {
 	wchar_t *wpath;
 	HANDLE handle;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
 	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
 	    hTemplateFile);
@@ -242,12 +246,25 @@
 		return (handle);
 	if (GetLastError() != ERROR_PATH_NOT_FOUND)
 		return (handle);
+#endif
 	wpath = __la_win_permissive_name(path);
 	if (wpath == NULL)
-		return (handle);
+		return INVALID_HANDLE_VALUE;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
+	createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
+	createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000;
+	createExParams.lpSecurityAttributes = lpSecurityAttributes;
+	createExParams.hTemplateFile = hTemplateFile;
+	handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
+	    dwCreationDisposition, &createExParams);
+#else /* !WINAPI_PARTITION_DESKTOP */
 	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
 	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
 	    hTemplateFile);
+#endif /* !WINAPI_PARTITION_DESKTOP */
 	free(wpath);
 	return (handle);
 }
@@ -305,7 +322,10 @@
 		 * "Permission denied" error.
 		 */
 		attr = GetFileAttributesA(path);
-		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+		{
 			ws = __la_win_permissive_name(path);
 			if (ws == NULL) {
 				errno = EINVAL;
@@ -320,7 +340,7 @@
 		}
 		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
 			HANDLE handle;
-
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
 			if (ws != NULL)
 				handle = CreateFileW(ws, 0, 0, NULL,
 				    OPEN_EXISTING,
@@ -333,6 +353,15 @@
 				    FILE_FLAG_BACKUP_SEMANTICS |
 				    FILE_ATTRIBUTE_READONLY,
 					NULL);
+#else /* !WINAPI_PARTITION_DESKTOP */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+			handle = CreateFile2(ws, 0, 0,
+				OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
 			free(ws);
 			if (handle == INVALID_HANDLE_VALUE) {
 				la_dosmaperr(GetLastError());
diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c
index 27626b5..ec3c95c 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write.c
@@ -310,6 +310,25 @@
 	return (__archive_write_filter(a->filter_first, buff, length));
 }
 
+static int
+__archive_write_filters_flush(struct archive_write *a)
+{
+	struct archive_write_filter *f;
+	int ret, ret1;
+
+	ret = ARCHIVE_OK;
+	for (f = a->filter_first; f != NULL; f = f->next_filter) {
+		if (f->flush != NULL && f->bytes_written > 0) {
+			ret1 = (f->flush)(f);
+			if (ret1 < ret)
+				ret = ret1;
+			if (ret1 < ARCHIVE_WARN)
+				f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+		}
+	}
+	return (ret);
+}
+
 int
 __archive_write_nulls(struct archive_write *a, size_t length)
 {
@@ -740,6 +759,18 @@
 		return (ARCHIVE_FAILED);
 	}
 
+	/* Flush filters at boundary. */
+	r2 = __archive_write_filters_flush(a);
+	if (r2 == ARCHIVE_FAILED) {
+		return (ARCHIVE_FAILED);
+	}
+	if (r2 == ARCHIVE_FATAL) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	if (r2 < ret)
+		ret = r2;
+
 	/* Format and write header. */
 	r2 = ((a->format_write_header)(a, entry));
 	if (r2 == ARCHIVE_FAILED) {
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c
index 0637e96..9c2144a 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_bzip2.c
@@ -190,7 +190,7 @@
 
 	memset(&data->stream, 0, sizeof(data->stream));
 	data->stream.next_out = data->compressed;
-	data->stream.avail_out = data->compressed_buffer_size;
+	data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
 	f->write = archive_compressor_bzip2_write;
 
 	/* Initialize compression library */
@@ -244,7 +244,7 @@
 
 	/* Compress input data to output buffer */
 	SET_NEXT_IN(data, buff);
-	data->stream.avail_in = length;
+	data->stream.avail_in = (uint32_t)length;
 	if (drive_compressor(f, data, 0))
 		return (ARCHIVE_FATAL);
 	return (ARCHIVE_OK);
@@ -313,7 +313,7 @@
 				return (ARCHIVE_FATAL);
 			}
 			data->stream.next_out = data->compressed;
-			data->stream.avail_out = data->compressed_buffer_size;
+			data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
 		}
 
 		/* If there's nothing to do, we're done. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c
index d404fae..3ed269f 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_compress.c
@@ -352,7 +352,7 @@
 	while (length--) {
 		c = *bp++;
 		state->in_count++;
-		state->cur_fcode = (c << 16) + state->cur_code;
+		state->cur_fcode = (c << 16) | state->cur_code;
 		i = ((c << HSHIFT) ^ state->cur_code);	/* Xor hashing. */
 
 		if (state->hashtab[i] == state->cur_fcode) {
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c
index cf19fad..6ac4503 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_lz4.c
@@ -518,10 +518,10 @@
 	} else {
 		/* The buffer is not compressed. The compressed size was
 		 * bigger than its uncompressed size. */
-		archive_le32enc(data->out, length | 0x80000000);
+		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
 		data->out += 4;
 		memcpy(data->out, p, length);
-		outsize = length;
+		outsize = (uint32_t)length;
 	}
 	data->out += outsize;
 	if (data->block_checksum) {
@@ -603,10 +603,10 @@
 	} else {
 		/* The buffer is not compressed. The compressed size was
 		 * bigger than its uncompressed size. */
-		archive_le32enc(data->out, length | 0x80000000);
+		archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
 		data->out += 4;
 		memcpy(data->out, p, length);
-		outsize = length;
+		outsize = (uint32_t)length;
 	}
 	data->out += outsize;
 	if (data->block_checksum) {
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
index 7d36d58..3d6b3d1 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
@@ -31,6 +31,9 @@
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -50,10 +53,22 @@
 
 struct private_data {
 	int		 compression_level;
-	int      threads;
+	int		 threads;
+	int		 long_distance;
 #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+	enum {
+		running,
+		finishing,
+		resetting,
+	} state;
+	int		 frame_per_file;
+	size_t		 min_frame_size;
+	size_t		 max_frame_size;
+	size_t		 cur_frame;
+	size_t		 cur_frame_in;
+	size_t		 cur_frame_out;
+	size_t		 total_in;
 	ZSTD_CStream	*cstream;
-	int64_t		 total_in;
 	ZSTD_outBuffer	 out;
 #else
 	struct archive_write_program_data *pdata;
@@ -67,14 +82,18 @@
 #define CLEVEL_STD_MAX 19 /* without using --ultra */
 #define CLEVEL_MAX 22
 
+#define LONG_STD 27
+
 #define MINVER_NEGCLEVEL 10304
 #define MINVER_MINCLEVEL 10306
+#define MINVER_LONG 10302
 
 static int archive_compressor_zstd_options(struct archive_write_filter *,
 		    const char *, const char *);
 static int archive_compressor_zstd_open(struct archive_write_filter *);
 static int archive_compressor_zstd_write(struct archive_write_filter *,
 		    const void *, size_t);
+static int archive_compressor_zstd_flush(struct archive_write_filter *);
 static int archive_compressor_zstd_close(struct archive_write_filter *);
 static int archive_compressor_zstd_free(struct archive_write_filter *);
 #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
@@ -103,13 +122,20 @@
 	f->data = data;
 	f->open = &archive_compressor_zstd_open;
 	f->options = &archive_compressor_zstd_options;
+	f->flush = &archive_compressor_zstd_flush;
 	f->close = &archive_compressor_zstd_close;
 	f->free = &archive_compressor_zstd_free;
 	f->code = ARCHIVE_FILTER_ZSTD;
 	f->name = "zstd";
 	data->compression_level = CLEVEL_DEFAULT;
 	data->threads = 0;
+	data->long_distance = 0;
 #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+	data->frame_per_file = 0;
+	data->min_frame_size = 0;
+	data->max_frame_size = SIZE_MAX;
+	data->cur_frame_in = 0;
+	data->cur_frame_out = 0;
 	data->cstream = ZSTD_createCStream();
 	if (data->cstream == NULL) {
 		free(data);
@@ -147,29 +173,18 @@
 	return (ARCHIVE_OK);
 }
 
-static int string_is_numeric (const char* value)
+static int string_to_number(const char *string, intmax_t *numberp)
 {
-       size_t len = strlen(value);
-       size_t i;
+	char *end;
 
-       if (len == 0) {
-               return (ARCHIVE_WARN);
-       }
-       else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {
-               return (ARCHIVE_WARN);
-       }
-       else if (!(value[0] >= '0' && value[0] <= '9') &&
-                value[0] != '-' && value[0] != '+') {
-               return (ARCHIVE_WARN);
-       }
-
-       for (i = 1; i < len; i++) {
-               if (!(value[i] >= '0' && value[i] <= '9')) {
-                       return (ARCHIVE_WARN);
-               }
-       }
-
-       return (ARCHIVE_OK);
+	if (string == NULL || *string == '\0')
+		return (ARCHIVE_WARN);
+	*numberp = strtoimax(string, &end, 10);
+	if (end == string || *end != '\0' || errno == EOVERFLOW) {
+		*numberp = 0;
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
 }
 
 /*
@@ -182,13 +197,13 @@
 	struct private_data *data = (struct private_data *)f->data;
 
 	if (strcmp(key, "compression-level") == 0) {
-		int level = atoi(value);
+		intmax_t level;
+		if (string_to_number(value, &level) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
 		/* If we don't have the library, hard-code the max level */
 		int minimum = CLEVEL_MIN;
 		int maximum = CLEVEL_MAX;
-		if (string_is_numeric(value) != ARCHIVE_OK) {
-			return (ARCHIVE_WARN);
-		}
 #if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
 		maximum = ZSTD_maxCLevel();
 #if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
@@ -204,21 +219,65 @@
 		if (level < minimum || level > maximum) {
 			return (ARCHIVE_WARN);
 		}
-		data->compression_level = level;
+		data->compression_level = (int)level;
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "threads") == 0) {
-		int threads = atoi(value);
-		if (string_is_numeric(value) != ARCHIVE_OK) {
+		intmax_t threads;
+		if (string_to_number(value, &threads) != ARCHIVE_OK) {
 			return (ARCHIVE_WARN);
 		}
-
-		int minimum = 0;
-
-		if (threads < minimum) {
+		if (threads < 0) {
 			return (ARCHIVE_WARN);
 		}
-
-		data->threads = threads;
+		data->threads = (int)threads;
+		return (ARCHIVE_OK);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+	} else if (strcmp(key, "frame-per-file") == 0) {
+		data->frame_per_file = 1;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "min-frame-size") == 0) {
+		intmax_t min_frame_size;
+		if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		if (min_frame_size < 0) {
+			return (ARCHIVE_WARN);
+		}
+		data->min_frame_size = min_frame_size;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "max-frame-size") == 0) {
+		intmax_t max_frame_size;
+		if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		if (max_frame_size < 1024) {
+			return (ARCHIVE_WARN);
+		}
+		data->max_frame_size = max_frame_size;
+		return (ARCHIVE_OK);
+#endif
+	}
+	else if (strcmp(key, "long") == 0) {
+		intmax_t long_distance;
+		if (string_to_number(value, &long_distance) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR && ZSTD_VERSION_NUMBER >= MINVER_LONG
+		ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog);
+		if (ZSTD_isError(bounds.error)) {
+			int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+			if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+				return (ARCHIVE_WARN);
+		} else {
+			if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound)
+				return (ARCHIVE_WARN);
+		}
+#else
+		int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+		if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+		    return (ARCHIVE_WARN);
+#endif
+		data->long_distance = (int)long_distance;
 		return (ARCHIVE_OK);
 	}
 
@@ -270,6 +329,10 @@
 
 	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
 
+#if ZSTD_VERSION_NUMBER >= MINVER_LONG
+	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance);
+#endif
+
 	return (ARCHIVE_OK);
 }
 
@@ -281,15 +344,22 @@
     size_t length)
 {
 	struct private_data *data = (struct private_data *)f->data;
-	int ret;
 
-	/* Update statistics */
-	data->total_in += length;
+	return (drive_compressor(f, data, 0, buff, length));
+}
 
-	if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK)
-		return (ret);
+/*
+ * Flush the compressed stream.
+ */
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
 
-	return (ARCHIVE_OK);
+	if (data->frame_per_file && data->state == running &&
+	    data->cur_frame_out > data->min_frame_size)
+		data->state = finishing;
+	return (drive_compressor(f, data, 1, NULL, 0));
 }
 
 /*
@@ -300,57 +370,72 @@
 {
 	struct private_data *data = (struct private_data *)f->data;
 
-	/* Finish zstd frame */
-	return drive_compressor(f, data, 1, NULL, 0);
+	if (data->state == running)
+		data->state = finishing;
+	return (drive_compressor(f, data, 1, NULL, 0));
 }
 
 /*
  * Utility function to push input data through compressor,
  * writing full output blocks as necessary.
- *
- * Note that this handles both the regular write case (finishing ==
- * false) and the end-of-archive case (finishing == true).
  */
 static int
 drive_compressor(struct archive_write_filter *f,
-    struct private_data *data, int finishing, const void *src, size_t length)
+    struct private_data *data, int flush, const void *src, size_t length)
 {
-	ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 };
+	ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
+	size_t ipos, opos, zstdret = 0;
+	int ret;
 
 	for (;;) {
-		if (data->out.pos == data->out.size) {
-			const int ret = __archive_write_filter(f->next_filter,
-			    data->out.dst, data->out.size);
+		ipos = in.pos;
+		opos = data->out.pos;
+		switch (data->state) {
+		case running:
+			if (in.pos == in.size)
+				return (ARCHIVE_OK);
+			zstdret = ZSTD_compressStream(data->cstream,
+			    &data->out, &in);
+			if (ZSTD_isError(zstdret))
+				goto zstd_fatal;
+			break;
+		case finishing:
+			zstdret = ZSTD_endStream(data->cstream, &data->out);
+			if (ZSTD_isError(zstdret))
+				goto zstd_fatal;
+			if (zstdret == 0)
+				data->state = resetting;
+			break;
+		case resetting:
+			ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
+			data->cur_frame++;
+			data->cur_frame_in = 0;
+			data->cur_frame_out = 0;
+			data->state = running;
+			break;
+		}
+		data->total_in += in.pos - ipos;
+		data->cur_frame_in += in.pos - ipos;
+		data->cur_frame_out += data->out.pos - opos;
+		if (data->state == running &&
+		    data->cur_frame_in >= data->max_frame_size) {
+			data->state = finishing;
+		}
+		if (data->out.pos == data->out.size ||
+		    (flush && data->out.pos > 0)) {
+			ret = __archive_write_filter(f->next_filter,
+			    data->out.dst, data->out.pos);
 			if (ret != ARCHIVE_OK)
-				return (ARCHIVE_FATAL);
+				goto fatal;
 			data->out.pos = 0;
 		}
-
-		/* If there's nothing to do, we're done. */
-		if (!finishing && in.pos == in.size)
-			return (ARCHIVE_OK);
-
-		{
-			const size_t zstdret = !finishing ?
-			    ZSTD_compressStream(data->cstream, &data->out, &in)
-			    : ZSTD_endStream(data->cstream, &data->out);
-
-			if (ZSTD_isError(zstdret)) {
-				archive_set_error(f->archive,
-				    ARCHIVE_ERRNO_MISC,
-				    "Zstd compression failed: %s",
-				    ZSTD_getErrorName(zstdret));
-				return (ARCHIVE_FATAL);
-			}
-
-			/* If we're finishing, 0 means nothing left to flush */
-			if (finishing && zstdret == 0) {
-				const int ret = __archive_write_filter(f->next_filter,
-				    data->out.dst, data->out.pos);
-				return (ret);
-			}
-		}
 	}
+zstd_fatal:
+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+	    "Zstd compression failed: %s",
+	    ZSTD_getErrorName(zstdret));
+fatal:
+	return (ARCHIVE_FATAL);
 }
 
 #else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
@@ -367,17 +452,9 @@
 	archive_strcpy(&as, "zstd --no-check");
 
 	if (data->compression_level < CLEVEL_STD_MIN) {
-		struct archive_string as2;
-		archive_string_init(&as2);
-		archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);
-		archive_string_concat(&as, &as2);
-		archive_string_free(&as2);
+		archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
 	} else {
-		struct archive_string as2;
-		archive_string_init(&as2);
-		archive_string_sprintf(&as2, " -%d", data->compression_level);
-		archive_string_concat(&as, &as2);
-		archive_string_free(&as2);
+		archive_string_sprintf(&as, " -%d", data->compression_level);
 	}
 
 	if (data->compression_level > CLEVEL_STD_MAX) {
@@ -385,11 +462,11 @@
 	}
 
 	if (data->threads != 0) {
-		struct archive_string as2;
-		archive_string_init(&as2);
-		archive_string_sprintf(&as2, " --threads=%d", data->threads);
-		archive_string_concat(&as, &as2);
-		archive_string_free(&as2);
+		archive_string_sprintf(&as, " --threads=%d", data->threads);
+	}
+
+	if (data->long_distance != 0) {
+		archive_string_sprintf(&as, " --long=%d", data->long_distance);
 	}
 
 	f->write = archive_compressor_zstd_write;
@@ -408,6 +485,14 @@
 }
 
 static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+	(void)f; /* UNUSED */
+
+	return (ARCHIVE_OK);
+}
+
+static int
 archive_compressor_zstd_close(struct archive_write_filter *f)
 {
 	struct private_data *data = (struct private_data *)f->data;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
index bd5180e..d676ed6 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
@@ -397,6 +397,7 @@
 static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
 static ssize_t	write_data_block(struct archive_write_disk *,
 		    const char *, size_t);
+static void close_file_descriptor(struct archive_write_disk *);
 
 static int	_archive_write_disk_close(struct archive *);
 static int	_archive_write_disk_free(struct archive *);
@@ -514,7 +515,12 @@
 	 * XXX At this point, symlinks should not be hit, otherwise
 	 * XXX a race occurred.  Do we want to check explicitly for that?
 	 */
-	if (lstat(a->name, &a->st) == 0) {
+#ifdef HAVE_LSTAT
+	if (lstat(a->name, &a->st) == 0)
+#else
+	if (la_stat(a->name, &a->st) == 0)
+#endif
+	{
 		a->pst = &a->st;
 		return (ARCHIVE_OK);
 	}
@@ -1605,12 +1611,12 @@
 			    "Seek failed");
 			return (ARCHIVE_FATAL);
 		} else if (a->offset > a->fd_offset) {
-			int64_t skip = a->offset - a->fd_offset;
+			uint64_t skip = a->offset - a->fd_offset;
 			char nullblock[1024];
 
 			memset(nullblock, 0, sizeof(nullblock));
 			while (skip > 0) {
-				if (skip > (int64_t)sizeof(nullblock))
+				if (skip > sizeof(nullblock))
 					bytes_written = hfs_write_decmpfs_block(
 					    a, nullblock, sizeof(nullblock));
 				else
@@ -1725,8 +1731,10 @@
 			else
 				r = hfs_write_data_block(
 				    a, null_d, a->file_remaining_bytes);
-			if (r < 0)
+			if (r < 0) {
+				close_file_descriptor(a);
 				return ((int)r);
+			}
 		}
 #endif
 	} else {
@@ -1735,6 +1743,7 @@
 		    a->filesize == 0) {
 			archive_set_error(&a->archive, errno,
 			    "File size could not be restored");
+			close_file_descriptor(a);
 			return (ARCHIVE_FAILED);
 		}
 #endif
@@ -1744,8 +1753,10 @@
 		 * to see what happened.
 		 */
 		a->pst = NULL;
-		if ((ret = lazy_stat(a)) != ARCHIVE_OK)
-			return (ret);
+        if ((ret = lazy_stat(a)) != ARCHIVE_OK) {
+            close_file_descriptor(a);
+            return (ret);
+        }
 		/* We can use lseek()/write() to extend the file if
 		 * ftruncate didn't work or isn't available. */
 		if (a->st.st_size < a->filesize) {
@@ -1753,11 +1764,13 @@
 			if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
 				archive_set_error(&a->archive, errno,
 				    "Seek failed");
+				close_file_descriptor(a);
 				return (ARCHIVE_FATAL);
 			}
 			if (write(a->fd, &nul, 1) < 0) {
 				archive_set_error(&a->archive, errno,
 				    "Write to restore size failed");
+				close_file_descriptor(a);
 				return (ARCHIVE_FATAL);
 			}
 			a->pst = NULL;
@@ -2154,7 +2167,11 @@
 		 * then don't follow it.
 		 */
 		if (r != 0 || !S_ISDIR(a->mode))
+#ifdef HAVE_LSTAT
 			r = lstat(a->name, &a->st);
+#else
+			r = la_stat(a->name, &a->st);
+#endif
 		if (r != 0) {
 			archive_set_error(&a->archive, errno,
 			    "Can't stat existing object");
@@ -2550,7 +2567,12 @@
 					goto skip_fixup_entry;
 				} else
 #endif
-				if (lstat(p->name, &st) != 0 ||
+				if (
+#ifdef HAVE_LSTAT
+					lstat(p->name, &st) != 0 ||
+#else
+					la_stat(p->name, &st) != 0 ||
+#endif
 				    la_verify_filetype(st.st_mode,
 				    p->filetype) == 0) {
 					goto skip_fixup_entry;
@@ -2565,7 +2587,12 @@
 				goto skip_fixup_entry;
 			} else
 #endif
-			if (lstat(p->name, &st) != 0 ||
+			if (
+#ifdef HAVE_LSTAT
+				lstat(p->name, &st) != 0 ||
+#else
+				la_stat(p->name, &st) != 0 ||
+#endif
 			    la_verify_filetype(st.st_mode,
 			    p->filetype) == 0) {
 				goto skip_fixup_entry;
@@ -2785,8 +2812,8 @@
     !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))
 	/* Platform doesn't have lstat, so we can't look for symlinks. */
 	(void)path; /* UNUSED */
-	(void)error_number; /* UNUSED */
-	(void)error_string; /* UNUSED */
+	(void)a_eno; /* UNUSED */
+	(void)a_estr; /* UNUSED */
 	(void)flags; /* UNUSED */
 	(void)checking_linkname; /* UNUSED */
 	return (ARCHIVE_OK);
@@ -2859,8 +2886,10 @@
 		/* Check that we haven't hit a symlink. */
 #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
 		r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);
-#else
+#elif defined(HAVE_LSTAT)
 		r = lstat(head, &st);
+#else
+		r = la_stat(head, &st);
 #endif
 		if (r != 0) {
 			tail[0] = c;
@@ -3558,7 +3587,9 @@
 	(void)fd; /* UNUSED */
 	(void)mode; /* UNUSED */
 	(void)name; /* UNUSED */
+	(void)atime; /* UNUSED */
 	(void)atime_nsec; /* UNUSED */
+	(void)mtime; /* UNUSED */
 	(void)mtime_nsec; /* UNUSED */
 	return (ARCHIVE_WARN);
 #endif
@@ -4391,7 +4422,12 @@
 	 */
 	archive_strncpy(&datafork, pathname, p - pathname);
 	archive_strcat(&datafork, p + 2);
-	if (lstat(datafork.s, &st) == -1 ||
+	if (
+#ifdef HAVE_LSTAT
+		lstat(datafork.s, &st) == -1 ||
+#else
+		la_stat(datafork.s, &st) == -1 ||
+#endif
 	    (st.st_mode & AE_IFMT) != AE_IFREG)
 		goto skip_appledouble;
 
@@ -4707,5 +4743,17 @@
 }
 #endif
 
+/*
+ * Close the file descriptor if one is open.
+ */
+static void close_file_descriptor(struct archive_write_disk* a)
+{
+	if (a->fd >= 0) {
+		close(a->fd);
+		a->fd = -1;
+	}
+}
+
+
 #endif /* !_WIN32 || __CYGWIN__ */
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
index 88df3ce..7b9ea74 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
@@ -254,9 +254,9 @@
  * which is high-16-bits of nFileIndexHigh. */
 #define bhfi_ino(bhfi)	\
 	((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
-    + (bhfi)->nFileIndexLow)
+    | (bhfi)->nFileIndexLow)
 #define bhfi_size(bhfi)	\
-    ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow)
+    ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
 
 static int
 file_information(struct archive_write_disk *a, wchar_t *path,
@@ -266,6 +266,9 @@
 	int r;
 	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
 	WIN32_FIND_DATAW	findData;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
 	if (sim_lstat || mode != NULL) {
 		h = FindFirstFileW(path, &findData);
@@ -290,14 +293,27 @@
 		(findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))
 		flag |= FILE_FLAG_OPEN_REPARSE_POINT;
 
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(a->name, 0, 0,
+		OPEN_EXISTING, &createExParams);
+#else
 	h = CreateFileW(a->name, 0, 0, NULL,
 	    OPEN_EXISTING, flag, NULL);
+#endif
 	if (h == INVALID_HANDLE_VALUE &&
 	    GetLastError() == ERROR_INVALID_NAME) {
 		wchar_t *full;
 		full = __la_win_permissive_name_w(path);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		h = CreateFile2(full, 0, 0,
+			OPEN_EXISTING, &createExParams);
+#else
 		h = CreateFileW(full, 0, 0, NULL,
 		    OPEN_EXISTING, flag, NULL);
+#endif
 		free(full);
 	}
 	if (h == INVALID_HANDLE_VALUE) {
@@ -559,6 +575,7 @@
 	return (fd);
 }
 
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
 static void *
 la_GetFunctionKernel32(const char *name)
 {
@@ -574,18 +591,24 @@
 	}
 	return (void *)GetProcAddress(lib, name);
 }
+#endif
 
 static int
 la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
 {
-	static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES);
-	static int set;
+	static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
 	BOOL ret;
 
+#if _WIN32_WINNT < _WIN32_WINNT_XP
+	static int set;
+/* CreateHardLinkW is available since XP and always loaded */
 	if (!set) {
 		set = 1;
 		f = la_GetFunctionKernel32("CreateHardLinkW");
 	}
+#else
+	f = CreateHardLinkW;
+#endif
 	if (!f) {
 		errno = ENOTSUP;
 		return (0);
@@ -624,7 +647,6 @@
 la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
     int linktype) {
 	static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
-	static int set;
 	wchar_t *ttarget, *p;
 	size_t len;
 	DWORD attrs = 0;
@@ -632,10 +654,20 @@
 	DWORD newflags = 0;
 	BOOL ret = 0;
 
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+/* CreateSymbolicLinkW is available since Vista and always loaded */
+	static int set;
 	if (!set) {
 		set = 1;
 		f = la_GetFunctionKernel32("CreateSymbolicLinkW");
 	}
+#else
+# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	f = CreateSymbolicLinkW;
+# else
+	f = NULL;
+# endif
+#endif
 	if (!f)
 		return (0);
 
@@ -1185,6 +1217,8 @@
 		if (la_ftruncate(a->fh, a->filesize) == -1) {
 			archive_set_error(&a->archive, errno,
 			    "File size could not be restored");
+			CloseHandle(a->fh);
+			a->fh = INVALID_HANDLE_VALUE;
 			return (ARCHIVE_FAILED);
 		}
 	}
@@ -1656,6 +1690,9 @@
 	mode_t final_mode, mode;
 	int r;
 	DWORD attrs = 0;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
 	/* We identify hard/symlinks according to the link names. */
 	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
@@ -1719,8 +1756,16 @@
 			a->todo = 0;
 			a->deferred = 0;
 		} else if (r == 0 && a->filesize > 0) {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+			a->fh = CreateFile2(namefull, GENERIC_WRITE, 0,
+			    TRUNCATE_EXISTING, &createExParams);
+#else
 			a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
 			    TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
 			if (a->fh == INVALID_HANDLE_VALUE) {
 				la_dosmaperr(GetLastError());
 				r = errno;
@@ -1783,14 +1828,27 @@
 		a->tmpname = NULL;
 		fullname = a->name;
 		/* O_WRONLY | O_CREAT | O_EXCL */
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+		a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+		    CREATE_NEW, &createExParams);
+#else
 		a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
 		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
 		if (a->fh == INVALID_HANDLE_VALUE &&
 		    GetLastError() == ERROR_INVALID_NAME &&
 		    fullname == a->name) {
 			fullname = __la_win_permissive_name_w(a->name);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+			    CREATE_NEW, &createExParams);
+#else
 			a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
 			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
 		}
 		if (a->fh == INVALID_HANDLE_VALUE) {
 			if (GetLastError() == ERROR_ACCESS_DENIED) {
@@ -2551,14 +2609,25 @@
 		hw = NULL;
 	} else {
 		wchar_t *ws;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
 
 		if (S_ISLNK(mode))
 			return (ARCHIVE_OK);
 		ws = __la_win_permissive_name_w(name);
 		if (ws == NULL)
 			goto settimes_failed;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+		hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0,
+		    OPEN_EXISTING, &createExParams);
+#else
 		hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
 		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
 		free(ws);
 		if (hw == INVALID_HANDLE_VALUE)
 			goto settimes_failed;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_private.h b/Utilities/cmlibarchive/libarchive/archive_write_private.h
index 155fdd7..6522e65 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_write_private.h
@@ -53,6 +53,7 @@
 	    const char *key, const char *value);
 	int	(*open)(struct archive_write_filter *);
 	int	(*write)(struct archive_write_filter *, const void *, size_t);
+	int	(*flush)(struct archive_write_filter *);
 	int	(*close)(struct archive_write_filter *);
 	int	(*free)(struct archive_write_filter *);
 	void	 *data;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
index 87b3586..1d7249f 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
@@ -91,6 +91,26 @@
 #define kAttributes		0x15
 #define kEncodedHeader		0x17
 
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+#ifndef FILE_ATTRIBUTE_ARCHIVE
+#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
 enum la_zaction {
 	ARCHIVE_Z_FINISH,
 	ARCHIVE_Z_RUN
@@ -165,7 +185,7 @@
 	mode_t			 mode;
 	uint32_t		 crc32;
 
-	signed int		 dir:1;
+	unsigned		 dir:1;
 };
 
 struct _7zip {
@@ -1424,14 +1444,19 @@
 		 * High 16bits is unix mode.
 		 * Low 16bits is Windows attributes.
 		 */
-		uint32_t encattr, attr;
+		uint32_t encattr, attr = 0;
+
 		if (file->dir)
-			attr = 0x8010;
+			attr |= FILE_ATTRIBUTE_DIRECTORY;
 		else
-			attr = 0x8020;
+			attr |= FILE_ATTRIBUTE_ARCHIVE;
+
 		if ((file->mode & 0222) == 0)
-			attr |= 1;/* Read Only. */
+			attr |= FILE_ATTRIBUTE_READONLY;
+
+		attr |= FILE_ATTRIBUTE_UNIX_EXTENSION;
 		attr |= ((uint32_t)file->mode) << 16;
+
 		archive_le32enc(&encattr, attr);
 		r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);
 		if (r < 0)
@@ -1809,11 +1834,11 @@
 	 * of ugly hackery to convert a const * pointer to
 	 * a non-const pointer. */
 	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
-	strm->avail_in = lastrm->avail_in;
+	strm->avail_in = (uint32_t)lastrm->avail_in;
 	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
 	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
 	strm->next_out = (char *)lastrm->next_out;
-	strm->avail_out = lastrm->avail_out;
+	strm->avail_out = (uint32_t)lastrm->avail_out;
 	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
 	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
 	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
@@ -1842,11 +1867,11 @@
 	 * of ugly hackery to convert a const * pointer to
 	 * a non-const pointer. */
 	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
-	strm->avail_in = lastrm->avail_in;
+	strm->avail_in = (uint32_t)lastrm->avail_in;
 	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
 	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
 	strm->next_out = (char *)lastrm->next_out;
-	strm->avail_out = lastrm->avail_out;
+	strm->avail_out = (uint32_t)lastrm->avail_out;
 	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
 	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
 	r = BZ2_bzCompress(strm,
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
index ebd33c5..1b03170 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
@@ -293,12 +293,12 @@
 		struct extr_rec	*current;
 	}			 extr_rec_list;
 
-	signed int		 virtual:1;
+	unsigned int		 virtual:1;
 	/* If set to one, this file type is a directory.
 	 * A convenience flag to be used as
 	 * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
 	 */
-	signed int		 dir:1;
+	unsigned int		 dir:1;
 };
 
 struct hardlink {
@@ -656,7 +656,7 @@
 #define VOLUME_IDENTIFIER_SIZE		32
 
 	/*
-	 * Usage  : !zisofs [DEFAULT] 
+	 * Usage  : !zisofs [DEFAULT]
 	 *        :    Disable to generate RRIP 'ZF' extension.
 	 *        : zisofs
 	 *        :    Make files zisofs file and generate RRIP 'ZF'
@@ -693,7 +693,7 @@
 	uint64_t		 bytes_remaining;
 	int			 need_multi_extent;
 
-	/* Temporary string buffer for Joliet extension. */ 
+	/* Temporary string buffer for Joliet extension. */
 	struct archive_string	 utf16be;
 	struct archive_string	 mbs;
 
@@ -759,9 +759,9 @@
 
 	/* Used for making zisofs. */
 	struct {
-		signed int	 detect_magic:1;
-		signed int	 making:1;
-		signed int	 allzero:1;
+		unsigned int	 detect_magic:1;
+		unsigned int	 making:1;
+		unsigned int	 allzero:1;
 		unsigned char	 magic_buffer[64];
 		int		 magic_cnt;
 
@@ -2525,12 +2525,11 @@
 static void
 get_tmfromtime(struct tm *tm, time_t *t)
 {
-#if HAVE_LOCALTIME_R
+#if HAVE_LOCALTIME_S
+	localtime_s(tm, t);
+#elif HAVE_LOCALTIME_R
 	tzset();
 	localtime_r(t, tm);
-#elif HAVE__LOCALTIME64_S
-	__time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits
-	_localtime64_s(tm, &tmp_t);
 #else
 	memcpy(tm, localtime(t), sizeof(*tm));
 #endif
@@ -4078,11 +4077,8 @@
 	}
 	memset(info.s, 0, info_size);
 	opt = 0;
-#if defined(HAVE__CTIME64_S)
-	{
-		__time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits
-		_ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp));
-	}
+#if defined(HAVE_CTIME_S)
+	ctime_s(buf, sizeof(buf), &(iso9660->birth_time));
 #elif defined(HAVE_CTIME_R)
 	ctime_r(&(iso9660->birth_time), buf);
 #else
@@ -7811,8 +7807,8 @@
 	uint64_t	 pz_uncompressed_size;
 	size_t		 uncompressed_buffer_size;
 
-	signed int	 initialized:1;
-	signed int	 header_passed:1;
+	unsigned int	 initialized:1;
+	unsigned int	 header_passed:1;
 
 	uint32_t	 pz_offset;
 	unsigned char	*block_pointers;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c
index cf1f477..1eb9a9a 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_pax.c
@@ -100,6 +100,7 @@
 static void		 sparse_list_clear(struct pax *);
 static int		 sparse_list_add(struct pax *, int64_t, int64_t);
 static char		*url_encode(const char *in);
+static time_t		 get_ustar_max_mtime(void);
 
 /*
  * Set output format to 'restricted pax' format.
@@ -367,10 +368,12 @@
 	struct archive_string s;
 	char *encoded_value;
 
+	if (encoded_name == NULL)
+		return;
+
 	if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
 		encoded_value = base64_encode((const char *)value, value_len);
-
-		if (encoded_name != NULL && encoded_value != NULL) {
+		if (encoded_value != NULL) {
 			archive_string_init(&s);
 			archive_strcpy(&s, "LIBARCHIVE.xattr.");
 			archive_strcat(&s, encoded_name);
@@ -403,17 +406,22 @@
 
 		archive_entry_xattr_next(entry, &name, &value, &size);
 		url_encoded_name = url_encode(name);
-		if (url_encoded_name != NULL) {
+		if (url_encoded_name == NULL)
+			goto malloc_error;
+		else {
 			/* Convert narrow-character to UTF-8. */
 			r = archive_strcpy_l(&(pax->l_url_encoded_name),
 			    url_encoded_name, pax->sconv_utf8);
 			free(url_encoded_name); /* Done with this. */
 			if (r == 0)
 				encoded_name = pax->l_url_encoded_name.s;
-			else if (errno == ENOMEM) {
-				archive_set_error(&a->archive, ENOMEM,
-				    "Can't allocate memory for Linkname");
-				return (ARCHIVE_FATAL);
+			else if (r == -1)
+				goto malloc_error;
+			else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Error encoding pax extended attribute");
+				return (ARCHIVE_FAILED);
 			}
 		}
 
@@ -422,6 +430,9 @@
 
 	}
 	return (ARCHIVE_OK);
+malloc_error:
+	archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
 }
 
 static int
@@ -595,6 +606,8 @@
 	need_extension = 0;
 	pax = (struct pax *)a->format_data;
 
+	const time_t ustar_max_mtime = get_ustar_max_mtime();
+
 	/* Sanity check. */
 	if (archive_entry_pathname(entry_original) == NULL) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
@@ -1116,16 +1129,13 @@
 	}
 
 	/*
-	 * Technically, the mtime field in the ustar header can
-	 * support 33 bits, but many platforms use signed 32-bit time
-	 * values.  The cutoff of 0x7fffffff here is a compromise.
 	 * Yes, this check is duplicated just below; this helps to
 	 * avoid writing an mtime attribute just to handle a
 	 * high-resolution timestamp in "restricted pax" mode.
 	 */
 	if (!need_extension &&
 	    ((archive_entry_mtime(entry_main) < 0)
-		|| (archive_entry_mtime(entry_main) >= 0x7fffffff)))
+		|| (archive_entry_mtime(entry_main) >= ustar_max_mtime)))
 		need_extension = 1;
 
 	/* I use a star-compatible file flag attribute. */
@@ -1190,7 +1200,7 @@
 	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
 	    need_extension) {
 		if (archive_entry_mtime(entry_main) < 0  ||
-		    archive_entry_mtime(entry_main) >= 0x7fffffff  ||
+		    archive_entry_mtime(entry_main) >= ustar_max_mtime  ||
 		    archive_entry_mtime_nsec(entry_main) != 0)
 			add_pax_attr_time(&(pax->pax_header), "mtime",
 			    archive_entry_mtime(entry_main),
@@ -1428,7 +1438,7 @@
 		/* Copy mtime, but clip to ustar limits. */
 		s = archive_entry_mtime(entry_main);
 		if (s < 0) { s = 0; }
-		if (s >= 0x7fffffff) { s = 0x7fffffff; }
+		if (s > ustar_max_mtime) { s = ustar_max_mtime; }
 		archive_entry_set_mtime(pax_attr_entry, s, 0);
 
 		/* Standard ustar doesn't support atime. */
@@ -1904,14 +1914,19 @@
 {
 	const char *s;
 	char *d;
-	int out_len = 0;
+	size_t out_len = 0;
 	char *out;
 
 	for (s = in; *s != '\0'; s++) {
-		if (*s < 33 || *s > 126 || *s == '%' || *s == '=')
+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+			if (SIZE_MAX - out_len < 4)
+				return (NULL);
 			out_len += 3;
-		else
+		} else {
+			if (SIZE_MAX - out_len < 2)
+				return (NULL);
 			out_len++;
+		}
 	}
 
 	out = (char *)malloc(out_len + 1);
@@ -2046,3 +2061,18 @@
 	return (_sparse_list_add_block(pax, offset, length, 0));
 }
 
+static time_t
+get_ustar_max_mtime(void)
+{
+	/*
+	 * Technically, the mtime field in the ustar header can
+	 * support 33 bits. We are using all of them to keep
+	 * tar/test/test_option_C_mtree.c simple and passing after 2038.
+	 * For platforms that use signed 32-bit time values we
+	 * use the 32-bit maximum.
+	 */
+	if (sizeof(time_t) > sizeof(int32_t))
+		return (time_t)0x1ffffffff;
+	else
+		return (time_t)0x7fffffff;
+}
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c
index 46b0573..0ef003e 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_warc.c
@@ -329,30 +329,21 @@
 {
 /** like strftime(3) but for time_t objects */
 	struct tm *rt;
-#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
 	struct tm timeHere;
 #endif
-#if defined(HAVE__GMTIME64_S)
-	errno_t terr;
-	__time64_t tmptime;
-#endif
 	char strtime[100];
 	size_t len;
 
-#ifdef HAVE_GMTIME_R
-	if ((rt = gmtime_r(&t, &timeHere)) == NULL)
-		return;
-#elif defined(HAVE__GMTIME64_S)
-	tmptime = t;
-	terr = _gmtime64_s(&timeHere, &tmptime);
-	if (terr)
-		rt = NULL;
-	else
-		rt = &timeHere;
+#if defined(HAVE_GMTIME_S)
+	rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+#elif defined(HAVE_GMTIME_R)
+	rt = gmtime_r(&t, &timeHere);
 #else
-	if ((rt = gmtime(&t)) == NULL)
-		return;
+	rt = gmtime(&t);
 #endif
+	if (!rt)
+		return;
 	/* leave the hard yacker to our role model strftime() */
 	len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
 	archive_strncat(as, strtime, len);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
index 1e35375..1e82aa2 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
@@ -212,8 +212,8 @@
 	struct heap_data	 data;
         struct archive_string    script;
 
-	signed int		 virtual:1;
-	signed int		 dir:1;
+	unsigned int		 virtual:1;
+	unsigned int		 dir:1;
 };
 
 struct hardlink {
@@ -906,15 +906,11 @@
 {
 	char timestr[100];
 	struct tm tm;
-#if defined(HAVE__GMTIME64_S)
-	__time64_t tmptime;
-#endif
 
-#if defined(HAVE_GMTIME_R)
+#if defined(HAVE_GMTIME_S)
+	gmtime_s(&tm, &t);
+#elif defined(HAVE_GMTIME_R)
 	gmtime_r(&t, &tm);
-#elif defined(HAVE__GMTIME64_S)
-	tmptime = t;
-	_gmtime64_s(&tm, &tmptime);
 #else
 	memcpy(&tm, gmtime(&t), sizeof(tm));
 #endif
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
index 530e1e8..d610300 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
@@ -1382,25 +1382,14 @@
 {
 	struct tm *t;
 	unsigned int dt;
-#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
 	struct tm tmbuf;
 #endif
-#if defined(HAVE__LOCALTIME64_S)
-	errno_t terr;
-	__time64_t tmptime;
-#endif
 
-	/* This will not preserve time when creating/extracting the archive
-	 * on two systems with different time zones. */
-#if defined(HAVE_LOCALTIME_R)
+#if defined(HAVE_LOCALTIME_S)
+	t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
 	t = localtime_r(&unix_time, &tmbuf);
-#elif defined(HAVE__LOCALTIME64_S)
-	tmptime = unix_time;
-	terr = _localtime64_s(&tmbuf, &tmptime);
-	if (terr)
-		t = NULL;
-	else
-		t = &tmbuf;
 #else
 	t = localtime(&unix_time);
 #endif
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
index dd57358..f4b5081 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
@@ -257,6 +257,15 @@
 The value is interpreted as a decimal integer specifying the
 compression level. Supported values depend on the library version,
 common values are from 1 to 22.
+.It Cm long
+Enables long distance matching. The value is interpreted as a
+decimal integer specifying log2 window size in bytes. Values from
+10 to 30 for 32 bit, or 31 for 64 bit, are supported.
+.It Cm threads
+The value is interpreted as a decimal integer specifying the
+number of threads for multi-threaded zstd compression.
+If set to 0, zstd will attempt to detect and use the number
+of physical CPU cores.
 .El
 .It Format 7zip
 .Bl -tag -compact -width indent
diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h
index 758621c..669f272 100644
--- a/Utilities/cmlibarchive/libarchive/config_freebsd.h
+++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h
@@ -111,6 +111,8 @@
 #define HAVE_FCNTL 1
 #define HAVE_FCNTL_H 1
 #define HAVE_FDOPENDIR 1
+#define HAVE_FNMATCH 1
+#define HAVE_FNMATCH_H 1
 #define HAVE_FORK 1
 #define HAVE_FSEEKO 1
 #define HAVE_FSTAT 1
@@ -123,6 +125,8 @@
 #define HAVE_GETEUID 1
 #define HAVE_GETGRGID_R 1
 #define HAVE_GETGRNAM_R 1
+#define HAVE_GETLINE 1
+#define HAVE_GETOPT_OPTRESET 1
 #define HAVE_GETPID 1
 #define HAVE_GETPWNAM_R 1
 #define HAVE_GETPWUID_R 1
@@ -201,6 +205,7 @@
 #define HAVE_SYS_MOUNT_H 1
 #define HAVE_SYS_PARAM_H 1
 #define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_QUEUE_H 1
 #define HAVE_SYS_SELECT_H 1
 #define HAVE_SYS_STATVFS_H 1
 #define HAVE_SYS_STAT_H 1
@@ -234,7 +239,7 @@
 #define HAVE_WMEMCPY 1
 #define HAVE_WMEMMOVE 1
 #define HAVE_ZLIB_H 1
-#define TIME_WITH_SYS_TIME 1
+#define HAVE_SYS_TIME_H 1
 
 #if __FreeBSD_version >= 800505
 #define HAVE_LIBLZMA 1
diff --git a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
index 0b96397..9e49c56 100644
--- a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
+++ b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
@@ -31,6 +31,7 @@
 
 #include "filter_fork.h"
 
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
 /* There are some editions of Windows ("nano server," for example) that
  * do not host user32.dll. If we want to keep running on those editions,
  * we need to delay-load WaitForInputIdle. */
@@ -224,6 +225,14 @@
 	__archive_cmdline_free(acmd);
 	return ARCHIVE_FAILED;
 }
+#else /* !WINAPI_PARTITION_DESKTOP */
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
+{
+	(void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
+	return ARCHIVE_FAILED;
+}
+#endif /* !WINAPI_PARTITION_DESKTOP */
 
 void
 __archive_check_child(int in, int out)
diff --git a/Utilities/cmlibarchive/libarchive/xxhash.c b/Utilities/cmlibarchive/libarchive/xxhash.c
index f96e9d9..beacd23 100644
--- a/Utilities/cmlibarchive/libarchive/xxhash.c
+++ b/Utilities/cmlibarchive/libarchive/xxhash.c
@@ -149,6 +149,10 @@
 
 #if GCC_VERSION >= 409
 __attribute__((__no_sanitize_undefined__))
+#else
+#  if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+#  endif
 #endif
 #if defined(_MSC_VER)
 static __inline U32 A32(const void * x)
diff --git a/Utilities/cmliblzma/common/sysdefs.h b/Utilities/cmliblzma/common/sysdefs.h
index 86c5da0..6e3495e 100644
--- a/Utilities/cmliblzma/common/sysdefs.h
+++ b/Utilities/cmliblzma/common/sysdefs.h
@@ -172,9 +172,9 @@
 #	include <memory.h>
 #endif
 
-// As of MSVC 2013, inline and restrict are supported with
+// As of MSVC 2013 and LCC <= 1.21, inline and restrict are supported with
 // non-standard keywords.
-#if defined(_WIN32) && defined(_MSC_VER)
+#if (defined(_WIN32) && defined(_MSC_VER)) || (defined(__EDG__) && defined(__LCC__))
 #	ifndef inline
 #		define inline __inline
 #	endif
diff --git a/Utilities/cmlibrhash/CMakeLists.txt b/Utilities/cmlibrhash/CMakeLists.txt
index 9f532ad..317c5f8 100644
--- a/Utilities/cmlibrhash/CMakeLists.txt
+++ b/Utilities/cmlibrhash/CMakeLists.txt
@@ -28,6 +28,7 @@
   librhash/sha512.c
   librhash/sha512.h
   librhash/ustd.h
+  librhash/util.c
   librhash/util.h
   )
 
@@ -36,5 +37,6 @@
   )
 
 add_library(cmlibrhash ${librhash_sources})
+target_compile_definitions(cmlibrhash PRIVATE NO_IMPORT_EXPORT)
 
 install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmlibrhash)
diff --git a/Utilities/cmlibrhash/librhash/algorithms.c b/Utilities/cmlibrhash/librhash/algorithms.c
index cdd4053..08e8e4e 100644
--- a/Utilities/cmlibrhash/librhash/algorithms.c
+++ b/Utilities/cmlibrhash/librhash/algorithms.c
@@ -14,16 +14,15 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <stdio.h>
-#include <assert.h>
-
+#include "algorithms.h"
 #include "byte_order.h"
 #include "rhash.h"
-#include "algorithms.h"
 
-/* header files of all supported hash sums */
+/* header files of all supported hash functions */
 #if 0
 #include "aich.h"
+#include "blake2b.h"
+#include "blake2s.h"
 #include "crc32.h"
 #include "ed2k.h"
 #include "edonr.h"
@@ -48,6 +47,11 @@
 #endif
 
 #ifdef USE_OPENSSL
+# include "plug_openssl.h"
+#endif /* USE_OPENSSL */
+#include <assert.h>
+
+#ifdef USE_OPENSSL
 /* note: BTIH and AICH depends on the used SHA1 algorithm */
 # define NEED_OPENSSL_INIT (RHASH_MD4 | RHASH_MD5 | \
 	RHASH_SHA1 | RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | RHASH_SHA512 | \
@@ -55,6 +59,7 @@
 #else
 # define NEED_OPENSSL_INIT 0
 #endif /* USE_OPENSSL */
+
 #ifdef GENERATE_GOST94_LOOKUP_TABLE
 # define NEED_GOST94_INIT (RHASH_GOST94 | RHASH_GOST94_CRYPTOPRO)
 #else
@@ -85,10 +90,10 @@
 rhash_info info_sha1 = { RHASH_SHA1,      F_BE32, 20, "SHA1", "sha1" };
 #if 0
 rhash_info info_tiger = { RHASH_TIGER,    F_LE64, 24, "TIGER", "tiger" };
-rhash_info info_tth  = { RHASH_TTH,       F_BS32, 24, "TTH", "tree:tiger" };
-rhash_info info_btih = { RHASH_BTIH,      0, 20, "BTIH", "btih" };
+rhash_info info_tth  = { RHASH_TTH,       F_BS32 | F_SPCEXP, 24, "TTH", "tree:tiger" };
+rhash_info info_btih = { RHASH_BTIH,      F_SPCEXP, 20, "BTIH", "btih" };
 rhash_info info_ed2k = { RHASH_ED2K,      F_LE32, 16, "ED2K", "ed2k" };
-rhash_info info_aich = { RHASH_AICH,      F_BS32, 20, "AICH", "aich" };
+rhash_info info_aich = { RHASH_AICH,      F_BS32 | F_SPCEXP, 20, "AICH", "aich" };
 rhash_info info_whirlpool = { RHASH_WHIRLPOOL, F_BE64, 64, "WHIRLPOOL", "whirlpool" };
 rhash_info info_rmd160 = { RHASH_RIPEMD160,  F_LE32, 20, "RIPEMD-160", "ripemd160" };
 rhash_info info_gost12_256 = { RHASH_GOST12_256, F_LE64, 32, "GOST12-256", "gost12-256" };
@@ -106,6 +111,8 @@
 #if 0
 rhash_info info_edr256 = { RHASH_EDONR256,   F_LE32, 32, "EDON-R256", "edon-r256" };
 rhash_info info_edr512 = { RHASH_EDONR512,   F_LE64, 64, "EDON-R512", "edon-r512" };
+rhash_info info_blake2s = { RHASH_BLAKE2S,   F_LE32, 32, "BLAKE2S", "blake2s" };
+rhash_info info_blake2b = { RHASH_BLAKE2B,   F_LE64, 64, "BLAKE2B", "blake2b" };
 #endif
 rhash_info info_sha3_224 = { RHASH_SHA3_224, F_LE64, 28, "SHA3-224", "sha3-224" };
 rhash_info info_sha3_256 = { RHASH_SHA3_256, F_LE64, 32, "SHA3-256", "sha3-256" };
@@ -113,14 +120,13 @@
 rhash_info info_sha3_512 = { RHASH_SHA3_512, F_LE64, 64, "SHA3-512", "sha3-512" };
 
 /* some helper macros */
-#define dgshft(name) (((char*)&((name##_ctx*)0)->hash) - (char*)0)
-#define dgshft2(name, field) (((char*)&((name##_ctx*)0)->field) - (char*)0)
+#define dgshft(name) ((uintptr_t)((char*)&((name##_ctx*)0)->hash))
+#define dgshft2(name, field) ((uintptr_t)((char*)&((name##_ctx*)0)->field))
 #define ini(name) ((pinit_t)(name##_init))
 #define upd(name) ((pupdate_t)(name##_update))
 #define fin(name) ((pfinal_t)(name##_final))
 #define iuf(name) ini(name), upd(name), fin(name)
 #define iuf2(name1, name2) ini(name1), upd(name2), fin(name2)
-#define diuf(name) dgshft(name), ini(name), upd(name), fin(name)
 
 /* information about all supported hash functions */
 rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT] =
@@ -160,6 +166,8 @@
 	{ &info_crc32c, sizeof(uint32_t), 0, iuf(rhash_crc32c), 0 }, /* 32 bit */
 	{ &info_snf128, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru128, rhash_snefru), 0 }, /* 128 bit */
 	{ &info_snf256, sizeof(snefru_ctx), dgshft(snefru), iuf2(rhash_snefru256, rhash_snefru), 0 }, /* 256 bit */
+	{ &info_blake2s, sizeof(blake2s_ctx),  dgshft(blake2s), iuf(rhash_blake2s), 0 },  /* 256 bit */
+	{ &info_blake2b, sizeof(blake2b_ctx),  dgshft(blake2b), iuf(rhash_blake2b), 0 },  /* 512 bit */
 #endif
 };
 
@@ -280,3 +288,76 @@
 #endif
 }
 #endif
+
+#if !defined(NO_IMPORT_EXPORT)
+/**
+ * Export a hash function context to a memory region,
+ * or calculate the size required for context export.
+ *
+ * @param hash_id identifier of the hash function
+ * @param ctx the algorithm context containing current hashing state
+ * @param out pointer to the memory region or NULL
+ * @param size size of memory region
+ * @return the size of the exported data on success, 0 on fail.
+ */
+size_t rhash_export_alg(unsigned hash_id, const void* ctx, void* out, size_t size)
+{
+	switch (hash_id)
+	{
+		case RHASH_TTH:
+			return rhash_tth_export((const tth_ctx*)ctx, out, size);
+		case RHASH_AICH:
+			return rhash_aich_export((const aich_ctx*)ctx, out, size);
+	}
+	return 0;
+}
+
+/**
+ * Import a hash function context from a memory region.
+ *
+ * @param hash_id identifier of the hash function
+ * @param ctx pointer to the algorithm context
+ * @param in pointer to the data to import
+ * @param size size of data to import
+ * @return the size of the imported data on success, 0 on fail.
+ */
+size_t rhash_import_alg(unsigned hash_id, void* ctx, const void* in, size_t size)
+{
+	switch (hash_id)
+	{
+		case RHASH_TTH:
+			return rhash_tth_import((tth_ctx*)ctx, in, size);
+		case RHASH_AICH:
+			return rhash_aich_import((aich_ctx*)ctx, in, size);
+	}
+	return 0;
+}
+#endif /* !defined(NO_IMPORT_EXPORT) */
+
+#ifdef USE_OPENSSL
+void rhash_load_sha1_methods(rhash_hashing_methods* methods, int methods_type)
+{
+	int use_openssl;
+	switch (methods_type) {
+		case METHODS_OPENSSL:
+			use_openssl = 1;
+			break;
+		case METHODS_SELECTED:
+			assert(rhash_info_table[3].info->hash_id == RHASH_SHA1);
+			use_openssl = ARE_OPENSSL_METHODS(rhash_info_table[3]);
+			break;
+		default:
+			use_openssl = 0;
+			break;
+	}
+	if (use_openssl) {
+		methods->init = rhash_ossl_sha1_init();
+		methods->update = rhash_ossl_sha1_update();
+		methods->final = rhash_ossl_sha1_final();
+	} else {
+		methods->init = (pinit_t)&rhash_sha1_init;
+		methods->update = (pupdate_t)&rhash_sha1_update;
+		methods->final = (pfinal_t)&rhash_sha1_final;
+	}
+}
+#endif
diff --git a/Utilities/cmlibrhash/librhash/algorithms.h b/Utilities/cmlibrhash/librhash/algorithms.h
index 01dda88..510b2a6 100644
--- a/Utilities/cmlibrhash/librhash/algorithms.h
+++ b/Utilities/cmlibrhash/librhash/algorithms.h
@@ -47,10 +47,10 @@
 	const char* magnet_name;
 } rhash_info;
 
-typedef void (*pinit_t)(void*);
+typedef void (*pinit_t)(void* ctx);
 typedef void (*pupdate_t)(void* ctx, const void* msg, size_t size);
-typedef void (*pfinal_t)(void*, unsigned char*);
-typedef void (*pcleanup_t)(void*);
+typedef void (*pfinal_t)(void* ctx, unsigned char* result);
+typedef void (*pcleanup_t)(void* ctx);
 
 /**
  * Information about a hash function
@@ -83,11 +83,11 @@
 	struct rhash_context rc;
 	unsigned hash_vector_size; /* number of contained hash sums */
 	unsigned flags;
-	unsigned state;
-	void* callback;
+	volatile unsigned state;
+	rhash_callback_t callback;
 	void* callback_data;
 	void* bt_ctx;
-	rhash_vector_item vector[1]; /* contexts of contained hash sums */
+	rhash_vector_item vector[]; /* contexts of contained hash sums */
 } rhash_context_ext;
 
 extern rhash_hash_info rhash_hash_info_default[RHASH_HASH_COUNT];
@@ -125,8 +125,9 @@
 
 /* rhash_info flags */
 #define F_BS32 1   /* default output in base32 */
-#define F_SWAP32 2 /* Big endian flag */
+#define F_SWAP32 2 /* big endian flag */
 #define F_SWAP64 4
+#define F_SPCEXP 8 /* needs special import/export logic */
 
 /* define endianness flags */
 #if IS_LITTLE_ENDIAN
@@ -144,10 +145,35 @@
 void rhash_init_algorithms(unsigned mask);
 const rhash_info* rhash_info_by_id(unsigned hash_id); /* get hash sum info by hash id */
 
+#if !defined(NO_IMPORT_EXPORT)
+size_t rhash_export_alg(unsigned hash_id, const void* ctx, void* out, size_t size);
+size_t rhash_import_alg(unsigned hash_id, void* ctx, const void* in, size_t size);
+#endif /* !defined(NO_IMPORT_EXPORT) */
+
 #if defined(OPENSSL_RUNTIME) && !defined(USE_OPENSSL)
 # define USE_OPENSSL
 #endif
 
+#ifdef USE_OPENSSL
+typedef struct rhash_hashing_methods
+{
+	pinit_t    init;
+	pupdate_t  update;
+	pfinal_t   final;
+} rhash_hashing_methods;
+
+enum rhash_methods_type
+{
+	METHODS_RHASH,
+	METHODS_OPENSSL,
+	METHODS_SELECTED,
+};
+
+void rhash_load_sha1_methods(rhash_hashing_methods* methods, int methods_type);
+
+#define ARE_OPENSSL_METHODS(methods) ((methods).init != (void (*)(void*))&rhash_sha1_init)
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* __cplusplus */
diff --git a/Utilities/cmlibrhash/librhash/byte_order.c b/Utilities/cmlibrhash/librhash/byte_order.c
index de2c583..7a05408 100644
--- a/Utilities/cmlibrhash/librhash/byte_order.c
+++ b/Utilities/cmlibrhash/librhash/byte_order.c
@@ -74,7 +74,7 @@
 void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t length)
 {
 	/* if all pointers and length are 32-bits aligned */
-	if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | index | length ) & 3) ) {
+	if ( 0 == (( (uintptr_t)to | (uintptr_t)from | (uintptr_t)index | length ) & 3) ) {
 		/* copy memory as 32-bit words */
 		const uint32_t* src = (const uint32_t*)from;
 		const uint32_t* end = (const uint32_t*)((const char*)src + length);
@@ -101,7 +101,7 @@
 void rhash_swap_copy_str_to_u64(void* to, int index, const void* from, size_t length)
 {
 	/* if all pointers and length are 64-bits aligned */
-	if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | index | length ) & 7) ) {
+	if ( 0 == (( (uintptr_t)to | (uintptr_t)from | (uintptr_t)index | length ) & 7) ) {
 		/* copy aligned memory block as 64-bit integers */
 		const uint64_t* src = (const uint64_t*)from;
 		const uint64_t* end = (const uint64_t*)((const char*)src + length);
@@ -124,7 +124,7 @@
 void rhash_swap_copy_u64_to_str(void* to, const void* from, size_t length)
 {
 	/* if all pointers and length are 64-bits aligned */
-	if ( 0 == (( (int)((char*)to - (char*)0) | ((char*)from - (char*)0) | length ) & 7) ) {
+	if ( 0 == (( (uintptr_t)to | (uintptr_t)from | length ) & 7) ) {
 		/* copy aligned memory block as 64-bit integers */
 		const uint64_t* src = (const uint64_t*)from;
 		const uint64_t* end = (const uint64_t*)((const char*)src + length);
diff --git a/Utilities/cmlibrhash/librhash/byte_order.h b/Utilities/cmlibrhash/librhash/byte_order.h
index cfb9e25..73863e0 100644
--- a/Utilities/cmlibrhash/librhash/byte_order.h
+++ b/Utilities/cmlibrhash/librhash/byte_order.h
@@ -76,14 +76,15 @@
 #ifdef RHASH_BYTE_ORDER
 #elif defined(CPU_IA32) || defined(CPU_X64) || defined(__ia64) || defined(__ia64__) || \
       defined(__alpha__) || defined(_M_ALPHA) || defined(vax) || defined(MIPSEL) || \
-      defined(_ARM_) || defined(__arm__)
+      defined(_ARM_) || defined(__arm__) || defined(_M_ARM64) || defined(_M_ARM64EC) || \
+      defined(__loongarch64)
 #  define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_LE
 #elif defined(__sparc) || defined(__sparc__) || defined(sparc) || \
       defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_POWER) || \
       defined(__POWERPC__) || defined(POWERPC) || defined(__powerpc) || \
       defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || \
       defined(__hpux)  || defined(_MIPSEB) || defined(mc68000) || \
-      defined(__s390__) || defined(__s390x__) || defined(sel)
+      defined(__s390__) || defined(__s390x__) || defined(sel) || defined(__hppa__)
 # define RHASH_BYTE_ORDER RHASH_BYTE_ORDER_BE
 #else
 #  error "Can't detect CPU architechture"
@@ -97,8 +98,8 @@
 # define __has_builtin(x) 0
 #endif
 
-#define IS_ALIGNED_32(p) (0 == (3 & ((const char*)(p) - (const char*)0)))
-#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0)))
+#define IS_ALIGNED_32(p) (0 == (3 & (uintptr_t)(p)))
+#define IS_ALIGNED_64(p) (0 == (7 & (uintptr_t)(p)))
 
 #if defined(_MSC_VER)
 #define ALIGN_ATTR(n) __declspec(align(n))
@@ -179,9 +180,9 @@
 # define le2me_32(x) bswap_32(x)
 # define le2me_64(x) bswap_64(x)
 
-# define be32_copy(to, index, from, length) memcpy((to) + (index), (from), (length))
+# define be32_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length))
 # define le32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length))
-# define be64_copy(to, index, from, length) memcpy((to) + (index), (from), (length))
+# define be64_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length))
 # define le64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length))
 # define me64_to_be_str(to, from, length) memcpy((to), (from), (length))
 # define me64_to_le_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length))
@@ -193,9 +194,9 @@
 # define le2me_64(x) (x)
 
 # define be32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length))
-# define le32_copy(to, index, from, length) memcpy((to) + (index), (from), (length))
+# define le32_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length))
 # define be64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length))
-# define le64_copy(to, index, from, length) memcpy((to) + (index), (from), (length))
+# define le64_copy(to, index, from, length) memcpy((char*)(to) + (index), (from), (length))
 # define me64_to_be_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length))
 # define me64_to_le_str(to, from, length) memcpy((to), (from), (length))
 #endif /* IS_BIG_ENDIAN */
diff --git a/Utilities/cmlibrhash/librhash/hex.c b/Utilities/cmlibrhash/librhash/hex.c
index cfd5892..40c2089 100644
--- a/Utilities/cmlibrhash/librhash/hex.c
+++ b/Utilities/cmlibrhash/librhash/hex.c
@@ -14,7 +14,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 #include "hex.h"
-#include <assert.h>
+#include "util.h"
 #include <ctype.h>
 #include <string.h>
 
@@ -113,8 +113,8 @@
 	#ifdef __clang_analyzer__
 	memset(buffer, 0, sizeof(buffer));
 	#endif
-	assert((BASE64_LENGTH(B64_CHUNK_SIZE) + 4) <= sizeof(buffer));
-	assert((B64_CHUNK_SIZE % 6) == 0);
+	RHASH_ASSERT((BASE64_LENGTH(B64_CHUNK_SIZE) + 4) <= sizeof(buffer));
+	RHASH_ASSERT((B64_CHUNK_SIZE % 6) == 0);
 	if (url_encode) {
 		size_t result_length = 0;
 		for (; length > 0; src += B64_CHUNK_SIZE) {
diff --git a/Utilities/cmlibrhash/librhash/md5.c b/Utilities/cmlibrhash/librhash/md5.c
index 9b76822..f989e62 100644
--- a/Utilities/cmlibrhash/librhash/md5.c
+++ b/Utilities/cmlibrhash/librhash/md5.c
@@ -19,13 +19,13 @@
 #include "md5.h"
 
 /**
- * Initialize context before calculaing hash.
+ * Initialize context before calculating hash.
  *
  * @param ctx context to initialize
  */
 void rhash_md5_init(md5_ctx* ctx)
 {
-	ctx->length = 0;
+	memset(ctx, 0, sizeof(*ctx));
 
 	/* initialize state */
 	ctx->hash[0] = 0x67452301;
@@ -170,7 +170,7 @@
 	/* fill partial block */
 	if (index) {
 		unsigned left = md5_block_size - index;
-		le32_copy((char*)ctx->message, index, msg, (size < left ? size : left));
+		le32_copy(ctx->message, index, msg, (size < left ? size : left));
 		if (size < left) return;
 
 		/* process partial block */
diff --git a/Utilities/cmlibrhash/librhash/md5.h b/Utilities/cmlibrhash/librhash/md5.h
index 12a6b52..1f6c625 100644
--- a/Utilities/cmlibrhash/librhash/md5.h
+++ b/Utilities/cmlibrhash/librhash/md5.h
@@ -22,7 +22,7 @@
 
 void rhash_md5_init(md5_ctx* ctx);
 void rhash_md5_update(md5_ctx* ctx, const unsigned char* msg, size_t size);
-void rhash_md5_final(md5_ctx* ctx, unsigned char result[16]);
+void rhash_md5_final(md5_ctx* ctx, unsigned char* result);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/Utilities/cmlibrhash/librhash/rhash.c b/Utilities/cmlibrhash/librhash/rhash.c
index 2530112..4e60c21 100644
--- a/Utilities/cmlibrhash/librhash/rhash.c
+++ b/Utilities/cmlibrhash/librhash/rhash.c
@@ -38,15 +38,24 @@
 #include <string.h>
 
 #define STATE_ACTIVE  0xb01dbabe
-#define STATE_STOPED  0xdeadbeef
+#define STATE_STOPPED 0xdeadbeef
 #define STATE_DELETED 0xdecea5ed
+#define IS_BAD_STATE(s) ((s) != STATE_ACTIVE && (s) != STATE_STOPPED)
 #define RCTX_AUTO_FINAL 0x1
 #define RCTX_FINALIZED  0x2
 #define RCTX_FINALIZED_MASK (RCTX_AUTO_FINAL | RCTX_FINALIZED)
 #define RHPR_FORMAT (RHPR_RAW | RHPR_HEX | RHPR_BASE32 | RHPR_BASE64)
 #define RHPR_MODIFIER (RHPR_UPPERCASE | RHPR_URLENCODE | RHPR_REVERSE)
 
-void rhash_library_init(void)
+#define HAS_ZERO_OR_ONE_BIT(id) (((id) & ((id) - 1)) == 0)
+#define IS_VALID_HASH_MASK(bitmask) ((bitmask) != 0 && ((bitmask) & ~RHASH_ALL_HASHES) == 0)
+#define IS_VALID_HASH_ID(id) (IS_VALID_HASH_MASK(id) && HAS_ZERO_OR_ONE_BIT(id))
+
+/* each hash function context must be aligned to DEFAULT_ALIGNMENT bytes */
+#define GET_CTX_ALIGNED(size) ALIGN_SIZE_BY((size), DEFAULT_ALIGNMENT)
+#define GET_EXPORT_ALIGNED(size) ALIGN_SIZE_BY((size), 8)
+
+RHASH_API void rhash_library_init(void)
 {
 	rhash_init_algorithms(RHASH_ALL_HASHES);
 #ifdef USE_OPENSSL
@@ -54,103 +63,120 @@
 #endif
 }
 
-int RHASH_API rhash_count(void)
+RHASH_API int rhash_count(void)
 {
 	return rhash_info_size;
 }
 
 /* LOW-LEVEL LIBRHASH INTERFACE */
 
-RHASH_API rhash rhash_init(unsigned hash_id)
+/**
+ * Allocate and initialize RHash context for calculating a single or multiple hash functions.
+ * The context after usage must be freed by calling rhash_free().
+ *
+ * @param count the size of the hash_ids array, the count must be greater than zero
+ * @param hash_ids array of identifiers of hash functions. Each element must
+ *        be an identifier of one hash function
+ * @param need_init initialize context for each hash function
+ * @return initialized rhash context, NULL on fail with error code stored in errno
+ */
+static rhash_context_ext* rhash_alloc_multi(size_t count, const unsigned hash_ids[], int need_init)
 {
-	unsigned tail_bit_index; /* index of hash_id trailing bit */
-	unsigned num = 0;        /* number of hashes to compute */
+	struct rhash_hash_info* info;   /* hash algorithm information */
 	rhash_context_ext* rctx = NULL; /* allocated rhash context */
-	size_t hash_size_sum = 0;   /* size of hash contexts to store in rctx */
-
-	unsigned i, bit_index, id;
-	struct rhash_hash_info* info;
-	size_t aligned_size;
+	const size_t header_size = GET_CTX_ALIGNED(sizeof(rhash_context_ext) + sizeof(rhash_vector_item) * count);
+	size_t ctx_size_sum = 0;   /* size of hash contexts to store in rctx */
+	size_t i;
 	char* phash_ctx;
+	unsigned hash_bitmask = 0;
 
-	hash_id &= RHASH_ALL_HASHES;
-	if (hash_id == 0) {
+	if (count < 1) {
 		errno = EINVAL;
 		return NULL;
 	}
-
-	tail_bit_index = rhash_ctz(hash_id); /* get trailing bit index */
-	assert(tail_bit_index < RHASH_HASH_COUNT);
-
-	id = 1 << tail_bit_index;
-
-	if (hash_id == id) {
-		/* handle the most common case of only one hash */
-		num = 1;
-		info = &rhash_info_table[tail_bit_index];
-		hash_size_sum = info->context_size;
-	} else {
-		/* another case: hash_id contains several hashes */
-		for (bit_index = tail_bit_index; id <= hash_id; bit_index++, id = id << 1) {
-			assert(id != 0);
-			assert(bit_index < RHASH_HASH_COUNT);
-			info = &rhash_info_table[bit_index];
-			if (hash_id & id) {
-				/* align sizes by 8 bytes */
-				aligned_size = (info->context_size + 7) & ~7;
-				hash_size_sum += aligned_size;
-				num++;
-			}
+	for (i = 0; i < count; i++) {
+		unsigned hash_index;
+		if (!IS_VALID_HASH_ID(hash_ids[i])) {
+			errno = EINVAL;
+			return NULL;
 		}
-		assert(num > 1);
+		hash_bitmask |= hash_ids[i];
+		hash_index = rhash_ctz(hash_ids[i]);
+		assert(hash_index < RHASH_HASH_COUNT); /* correct until extended hash_ids are supported */
+		info = &rhash_info_table[hash_index];
+
+		/* align context sizes and sum up */
+		ctx_size_sum += GET_CTX_ALIGNED(info->context_size);
 	}
 
-	/* align the size of the rhash context common part */
-	aligned_size = ((offsetof(rhash_context_ext, vector) + sizeof(rhash_vector_item) * num) + 7) & ~7;
-	assert(aligned_size >= sizeof(rhash_context_ext));
-
-	/* allocate rhash context with enough memory to store contexts of all used hashes */
-	rctx = (rhash_context_ext*)malloc(aligned_size + hash_size_sum);
-	if (rctx == NULL) return NULL;
+	/* allocate rhash context with enough memory to store contexts of all selected hash functions */
+	rctx = (rhash_context_ext*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, header_size + ctx_size_sum);
+	if (rctx == NULL)
+		return NULL;
 
 	/* initialize common fields of the rhash context */
-	memset(rctx, 0, sizeof(rhash_context_ext));
-	rctx->rc.hash_id = hash_id;
+	memset(rctx, 0, header_size);
+	rctx->rc.hash_id = hash_bitmask;
 	rctx->flags = RCTX_AUTO_FINAL; /* turn on auto-final by default */
 	rctx->state = STATE_ACTIVE;
-	rctx->hash_vector_size = num;
+	rctx->hash_vector_size = count;
 
-	/* aligned hash contexts follows rctx->vector[num] in the same memory block */
-	phash_ctx = (char*)rctx + aligned_size;
-	assert(phash_ctx >= (char*)&rctx->vector[num]);
+	/* calculate aligned pointer >= (&rctx->vector[count]) */
+	phash_ctx = (char*)rctx + header_size;
+	assert(phash_ctx >= (char*)&rctx->vector[count]);
+	assert(phash_ctx < ((char*)&rctx->vector[count] + DEFAULT_ALIGNMENT));
 
-	/* initialize context for every hash in a loop */
-	for (bit_index = tail_bit_index, id = 1 << tail_bit_index, i = 0;
-		id <= hash_id; bit_index++, id = id << 1)
-	{
-		/* check if a hash function with given id shall be included into rctx */
-		if ((hash_id & id) != 0) {
-			info = &rhash_info_table[bit_index];
-			assert(info->context_size > 0);
-			assert(((phash_ctx - (char*)0) & 7) == 0); /* hash context is aligned */
-			assert(info->init != NULL);
+	for (i = 0; i < count; i++) {
+		unsigned hash_index = rhash_ctz(hash_ids[i]);
+		info = &rhash_info_table[hash_index];
+		assert(info->context_size > 0);
+		assert(info->init != NULL);
+		assert(IS_PTR_ALIGNED_BY(phash_ctx, DEFAULT_ALIGNMENT)); /* hash context is aligned */
 
-			rctx->vector[i].hash_info = info;
-			rctx->vector[i].context = phash_ctx;
+		rctx->vector[i].hash_info = info;
+		rctx->vector[i].context = phash_ctx;
 
 #if 0
-			/* BTIH initialization is complex, save pointer for later */
-			if ((id & RHASH_BTIH) != 0) rctx->bt_ctx = phash_ctx;
+		/* BTIH initialization is a bit complicated, so store the context pointer for later usage */
+		if ((hash_ids[i] & RHASH_BTIH) != 0)
+			rctx->bt_ctx = phash_ctx;
 #endif
-			phash_ctx += (info->context_size + 7) & ~7;
+		phash_ctx += GET_CTX_ALIGNED(info->context_size);
 
-			/* initialize the i-th hash context */
+		/* initialize the i-th hash context */
+		if (need_init)
 			info->init(rctx->vector[i].context);
-			i++;
-		}
 	}
+	return rctx;
+}
 
-	return &rctx->rc; /* return allocated and initialized rhash context */
+RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[])
+{
+	rhash_context_ext* ectx = rhash_alloc_multi(count, hash_ids, 1);
+	return &ectx->rc; /* return initialized rhash context */
+}
+
+RHASH_API rhash rhash_init(unsigned hash_id)
+{
+	if (!IS_VALID_HASH_MASK(hash_id)) {
+		errno = EINVAL;
+		return NULL;
+	}
+	if (HAS_ZERO_OR_ONE_BIT(hash_id)) {
+		return rhash_init_multi(1, &hash_id);
+	} else {
+		/* handle the depricated case, when hash_id is a bitwise union of several hash function identifiers */
+		size_t count;
+		unsigned hash_ids[32];
+		unsigned id = hash_id & -hash_id; /* get the trailing bit */
+		for (count = 0; id <= hash_id; id = id << 1) {
+			assert(id != 0);
+			if (hash_id & id)
+				hash_ids[count++] = id;
+		}
+		assert(count > 1);
+		return rhash_init_multi(count, hash_ids);
+	}
 }
 
 void rhash_free(rhash ctx)
@@ -159,7 +185,6 @@
 	unsigned i;
 
 	if (ctx == 0) return;
-	assert(ectx->hash_vector_size <= RHASH_HASH_COUNT);
 	ectx->state = STATE_DELETED; /* mark memory block as being removed */
 
 	/* clean the hash functions, which require additional clean up */
@@ -169,8 +194,7 @@
 			info->cleanup(ectx->vector[i].context);
 		}
 	}
-
-	free(ectx);
+	rhash_aligned_free(ectx);
 }
 
 RHASH_API void rhash_reset(rhash ctx)
@@ -239,6 +263,161 @@
 }
 
 /**
+ * Header block for rhash context import/export.
+ */
+typedef struct export_header
+{
+	uint32_t state;
+	uint16_t hash_vector_size;
+	uint16_t flags;
+	uint64_t msg_size;
+} export_header;
+
+/**
+ * Process export error. Returns 0 and set errno to EINVAL.
+ *
+ * @return NULL
+ */
+static size_t export_error_einval(void)
+{
+	errno = EINVAL;
+	return 0;
+}
+
+/**
+ * Process import error. Returns NULL and set errno to EINVAL.
+ *
+ * @return NULL
+ */
+static rhash import_error_einval(void)
+{
+	errno = EINVAL;
+	return NULL;
+}
+
+RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size)
+{
+#if !defined(NO_IMPORT_EXPORT)
+	size_t export_size;
+	size_t i;
+	rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
+	export_header* header = (export_header*)out;
+	unsigned* hash_ids = NULL;
+	if (!ctx || (out && size < sizeof(export_header)) || IS_BAD_STATE(ectx->state))
+		return export_error_einval();
+	export_size = sizeof(export_header) + sizeof(unsigned) * ectx->hash_vector_size;
+	if (out != NULL) {
+		memset(out, 0, size);
+		header->state = ectx->state;
+		header->hash_vector_size = (uint16_t)(ectx->hash_vector_size);
+		header->flags = (uint16_t)(ectx->flags);
+		header->msg_size = ctx->msg_size;
+		hash_ids = (unsigned*)(void*)(header + 1);
+	}
+	for (i = 0; i < ectx->hash_vector_size; i++) {
+		void* src_context = ectx->vector[i].context;
+		struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
+		unsigned is_special = (hash_info->info->flags & F_SPCEXP);
+		size_t item_size;
+		if (out != NULL) {
+			if (size <= export_size)
+				return export_error_einval();
+			hash_ids[i] = hash_info->info->hash_id;
+			if (is_special) {
+				char* dst_item;
+				size_t left_size;
+				export_size = GET_EXPORT_ALIGNED(export_size);
+				dst_item = (char*)out + export_size;
+				left_size = size - export_size;
+				item_size = rhash_export_alg(hash_info->info->hash_id,
+					src_context, dst_item, left_size);
+				if (!item_size)
+					return export_error_einval();
+			} else {
+				char* dst_item = (char*)out + export_size;
+				item_size = hash_info->context_size;
+				if (size < (export_size + item_size))
+					return export_error_einval();
+				memcpy(dst_item, src_context, item_size);
+			}
+		} else {
+			if (is_special) {
+				export_size = GET_EXPORT_ALIGNED(export_size);
+				item_size = rhash_export_alg(
+					hash_info->info->hash_id, src_context, NULL, 0);
+			} else
+				item_size = hash_info->context_size;
+		}
+		export_size += item_size;
+	}
+	if (export_size < size)
+		return export_error_einval();
+	return export_size;
+#else
+	return export_error_einval();
+#endif /* !defined(NO_IMPORT_EXPORT) */
+}
+
+RHASH_API rhash rhash_import(const void* in, size_t size)
+{
+#if !defined(NO_IMPORT_EXPORT)
+	const export_header* header = (const export_header*)in;
+	size_t i;
+	size_t imported_size;
+	const unsigned* hash_ids;
+	const char* src_item;
+	rhash_context_ext* ectx;
+	if (!header || IS_BAD_STATE(header->state) || size < sizeof(export_header))
+		return import_error_einval();
+	imported_size = sizeof(export_header) + sizeof(unsigned) * header->hash_vector_size;
+	if (!header->hash_vector_size || size < imported_size)
+		return import_error_einval();
+	hash_ids = (const unsigned*)(const void*)(header + 1);
+	ectx = (rhash_context_ext*)rhash_alloc_multi(header->hash_vector_size, hash_ids, 0);
+	if (!ectx)
+		return NULL; /* errno must be set by the previous function */
+	ectx->state = header->state;
+	ectx->hash_vector_size = header->hash_vector_size;
+	ectx->flags = header->flags;
+	ectx->rc.msg_size = header->msg_size;
+	for (i = 0; i < ectx->hash_vector_size; i++) {
+		void* dst_context = ectx->vector[i].context;
+		struct rhash_hash_info* hash_info = ectx->vector[i].hash_info;
+		unsigned is_special = (hash_info->info->flags & F_SPCEXP);
+		size_t item_size;
+
+		if (is_special) {
+			size_t left_size;
+			imported_size = GET_EXPORT_ALIGNED(imported_size);
+			src_item = (const char*)in + imported_size;
+			left_size = size - imported_size;
+			assert(size >= imported_size);
+			item_size = rhash_import_alg(hash_ids[i], dst_context, src_item, left_size);
+			imported_size += item_size;
+			if (!item_size || size < imported_size) {
+				ectx->hash_vector_size = i + 1; /* clean only initialized contextes */
+				rhash_free(&ectx->rc);
+				return import_error_einval();
+			}
+		} else {
+			src_item = (const char*)in + imported_size;
+			item_size = hash_info->context_size;
+			imported_size += item_size;
+			if (size < imported_size) {
+				ectx->hash_vector_size = i + 1;
+				rhash_free(&ectx->rc);
+				return import_error_einval();
+			}
+			memcpy(dst_context, src_item, item_size);
+		}
+	}
+	return &ectx->rc;
+#else
+	return import_error_einval();
+#endif /* !defined(NO_IMPORT_EXPORT) */
+}
+
+/**
  * Store digest for given hash_id.
  * If hash_id is zero, function stores digest for a hash with the lowest id found in the context.
  * For nonzero hash_id the context must contain it, otherwise function silently does nothing.
@@ -290,7 +469,7 @@
 
 RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data)
 {
-	((rhash_context_ext*)ctx)->callback = (void*)callback;
+	((rhash_context_ext*)ctx)->callback = callback;
 	((rhash_context_ext*)ctx)->callback_data = callback_data;
 }
 
@@ -313,26 +492,21 @@
 	rhash_context_ext* const ectx = (rhash_context_ext*)ctx;
 	const size_t block_size = 8192;
 	unsigned char* buffer;
-	unsigned char* pmem;
-	size_t length = 0, align8;
+	size_t length = 0;
 	int res = 0;
-	if (ectx->state != STATE_ACTIVE) return 0; /* do nothing if canceled */
-
+	if (ectx->state != STATE_ACTIVE)
+		return 0; /* do nothing if canceled */
 	if (ctx == NULL) {
 		errno = EINVAL;
 		return -1;
 	}
-
-	pmem = (unsigned char*)malloc(block_size + 8);
-	if (!pmem) return -1; /* errno is set to ENOMEM according to UNIX 98 */
-
-	align8 = ((unsigned char*)0 - pmem) & 7;
-	buffer = pmem + align8;
+	buffer = (unsigned char*)rhash_aligned_alloc(DEFAULT_ALIGNMENT, block_size);
+	if (!buffer)
+		return -1; /* errno is set to ENOMEM according to UNIX 98 */
 
 	while (!feof(fd)) {
-		/* stop if canceled */
-		if (ectx->state != STATE_ACTIVE) break;
-
+		if (ectx->state != STATE_ACTIVE)
+			break; /* stop if canceled */
 		length = fread(buffer, 1, block_size, fd);
 
 		if (ferror(fd)) {
@@ -346,11 +520,16 @@
 			}
 		}
 	}
-
-	free(buffer);
+	rhash_aligned_free(buffer);
 	return res;
 }
 
+#ifdef _WIN32
+# define FOPEN_MODE "rbS"
+#else
+# define FOPEN_MODE "rb"
+#endif
+
 RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result)
 {
 	FILE* fd;
@@ -363,17 +542,19 @@
 		return -1;
 	}
 
-	if ((fd = fopen(filepath, "rb")) == NULL) return -1;
+	fd = fopen(filepath, FOPEN_MODE);
+	if (!fd)
+		return -1;
 
-	if ((ctx = rhash_init(hash_id)) == NULL) {
+	ctx = rhash_init(hash_id);
+	if (!ctx) {
 		fclose(fd);
 		return -1;
 	}
-
 	res = rhash_file_update(ctx, fd); /* hash the file */
 	fclose(fd);
-
-	rhash_final(ctx, result);
+	if (res >= 0)
+		rhash_final(ctx, result);
 	rhash_free(ctx);
 	return res;
 }
@@ -393,17 +574,19 @@
 		return -1;
 	}
 
-	if ((fd = _wfsopen(filepath, L"rb", _SH_DENYWR)) == NULL) return -1;
+	fd = _wfsopen(filepath, L"rbS", _SH_DENYWR);
+	if (!fd)
+		return -1;
 
-	if ((ctx = rhash_init(hash_id)) == NULL) {
+	ctx = rhash_init(hash_id);
+	if (!ctx) {
 		fclose(fd);
 		return -1;
 	}
-
 	res = rhash_file_update(ctx, fd); /* hash the file */
 	fclose(fd);
-
-	rhash_final(ctx, result);
+	if (res >= 0)
+		rhash_final(ctx, result);
 	rhash_free(ctx);
 	return res;
 }
@@ -576,7 +759,7 @@
 	return result_length;
 }
 
-size_t RHASH_API rhash_print(char* output, rhash context, unsigned hash_id, int flags)
+RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id, int flags)
 {
 	const rhash_info* info;
 	unsigned char digest[80];
@@ -654,6 +837,7 @@
 {
 	/* for messages working with rhash context */
 	rhash_context_ext* const ctx = (rhash_context_ext*)dst;
+	(void)rdata;
 
 	switch (msg_id) {
 	case RMSG_GET_CONTEXT:
@@ -669,11 +853,11 @@
 
 	case RMSG_CANCEL:
 		/* mark rhash context as canceled, in a multithreaded program */
-		atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPED);
+		atomic_compare_and_swap(&ctx->state, STATE_ACTIVE, STATE_STOPPED);
 		return 0;
 
 	case RMSG_IS_CANCELED:
-		return (ctx->state == STATE_STOPED);
+		return (ctx->state == STATE_STOPPED);
 
 	case RMSG_GET_FINALIZED:
 		return ((ctx->flags & RCTX_FINALIZED) != 0);
@@ -695,6 +879,9 @@
 	case RMSG_GET_OPENSSL_AVAILABLE_MASK:
 		return rhash_get_openssl_available_hash_mask();
 
+	case RMSG_GET_LIBRHASH_VERSION:
+		return RHASH_XVERSION;
+
 	default:
 		return RHASH_ERROR; /* unknown message */
 	}
diff --git a/Utilities/cmlibrhash/librhash/rhash.h b/Utilities/cmlibrhash/librhash/rhash.h
index c011762..07b6d9f 100644
--- a/Utilities/cmlibrhash/librhash/rhash.h
+++ b/Utilities/cmlibrhash/librhash/rhash.h
@@ -52,9 +52,11 @@
 	RHASH_CRC32C    = 0x4000000,
 	RHASH_SNEFRU128 = 0x8000000,
 	RHASH_SNEFRU256 = 0x10000000,
+	RHASH_BLAKE2S   = 0x20000000,
+	RHASH_BLAKE2B   = 0x40000000,
 
 	/**
-	 * The bit-mask containing all supported hashe functions.
+	 * The bit-mask containing all supported hash functions.
 	 */
 	RHASH_ALL_HASHES = RHASH_CRC32 | RHASH_CRC32C | RHASH_MD4 | RHASH_MD5 |
 		RHASH_ED2K | RHASH_SHA1 |RHASH_TIGER | RHASH_TTH |
@@ -63,14 +65,18 @@
 		RHASH_HAS160 | RHASH_SNEFRU128 | RHASH_SNEFRU256 |
 		RHASH_SHA224 | RHASH_SHA256 | RHASH_SHA384 | RHASH_SHA512 |
 		RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512 |
-		RHASH_EDONR256 | RHASH_EDONR512,
+		RHASH_EDONR256 | RHASH_EDONR512 | RHASH_BLAKE2S | RHASH_BLAKE2B,
 
 	RHASH_GOST = RHASH_GOST94, /* deprecated constant name */
 	RHASH_GOST_CRYPTOPRO = RHASH_GOST94_CRYPTOPRO, /* deprecated constant name */
+
+	/* bit-flag for extra hash identifiers */
+	RHASH_EXTENDED_BIT = (int)0x80000000,
+
 	/**
 	 * The number of supported hash functions.
 	 */
-	RHASH_HASH_COUNT = 29
+	RHASH_HASH_COUNT = 31
 #else
 	RHASH_MD5        = 0x01,
 	RHASH_SHA1       = 0x02,
@@ -100,7 +106,7 @@
 /**
  * The rhash context structure contains contexts for several hash functions.
  */
-typedef struct rhash_context
+struct rhash_context
 {
 	/**
 	 * The size of the hashed message.
@@ -108,10 +114,10 @@
 	unsigned long long msg_size;
 
 	/**
-	 * The bit-mask containing identifiers of the hashes being calculated.
+	 * The bit-mask containing identifiers of the hash functions being calculated.
 	 */
 	unsigned hash_id;
-} rhash_context;
+};
 
 #ifndef LIBRHASH_RHASH_CTX_DEFINED
 #define LIBRHASH_RHASH_CTX_DEFINED
@@ -135,34 +141,34 @@
 /* HIGH-LEVEL LIBRHASH INTERFACE */
 
 /**
- * Compute a hash of the given message.
+ * Compute a message digest of the given message.
  *
- * @param hash_id id of hash sum to compute
+ * @param hash_id id of message digest to compute
  * @param message the message to process
  * @param length message length
- * @param result buffer to receive binary hash string
+ * @param result buffer to receive the binary message digest value
  * @return 0 on success, -1 on error
  */
 RHASH_API int rhash_msg(unsigned hash_id, const void* message, size_t length, unsigned char* result);
 
 /**
- * Compute a single hash for given file.
+ * Compute a single message digest for the given file.
  *
- * @param hash_id id of hash sum to compute
- * @param filepath path to the file to hash
- * @param result buffer to receive hash value with the lowest requested id
- * @return 0 on success, -1 on error and errno is set
+ * @param hash_id id of hash function to compute
+ * @param filepath path to the file to process
+ * @param result buffer to receive message digest
+ * @return 0 on success, -1 on fail with error code stored in errno
  */
 RHASH_API int rhash_file(unsigned hash_id, const char* filepath, unsigned char* result);
 
 #ifdef _WIN32
 /**
- * Compute a single hash for given file (Windows-specific function).
+ * Compute a single message digest for the given file (Windows-specific function).
  *
- * @param hash_id id of hash sum to compute
- * @param filepath path to the file to hash
- * @param result buffer to receive hash value with the lowest requested id
- * @return 0 on success, -1 on error, -1 on error and errno is set
+ * @param hash_id id of hash function to compute
+ * @param filepath path to the file to process
+ * @param result buffer to receive the binary message digest value
+ * @return 0 on success, -1 on fail with error code stored in errno
  */
 RHASH_API int rhash_wfile(unsigned hash_id, const wchar_t* filepath, unsigned char* result);
 #endif
@@ -171,45 +177,59 @@
 /* LOW-LEVEL LIBRHASH INTERFACE */
 
 /**
- * Allocate and initialize RHash context for calculating hash(es).
- * After initializing rhash_update()/rhash_final() functions should be used.
- * Then the context must be freed by calling rhash_free().
+ * Allocate and initialize RHash context for calculating a single or multiple hash functions.
+ * The context after usage must be freed by calling rhash_free().
  *
- * @param hash_id union of bit flags, containing ids of hashes to calculate.
- * @return initialized rhash context, NULL on error and errno is set
+ * @param count the size of the hash_ids array, the count must be greater than zero
+ * @param hash_ids array of identifiers of hash functions. Each element must
+ *        be an identifier of one hash function
+ * @return initialized rhash context, NULL on fail with error code stored in errno
+ */
+RHASH_API rhash rhash_init_multi(size_t count, const unsigned hash_ids[]);
+
+/**
+ * Allocate and initialize RHash context for calculating a single hash function.
+ *
+ * This function also supports a depricated way to initialize rhash context
+ * for multiple hash functions, by passing a bitwise union of several hash
+ * identifiers. Only single-bit identifiers (not greater than RHASH_SNEFRU256)
+ * can be used in such bitwise union.
+ *
+ * @param hash_id identifier of a hash function
+ * @return initialized rhash context, NULL on fail with error code stored in errno
  */
 RHASH_API rhash rhash_init(unsigned hash_id);
 
 /**
- * Calculate hashes of message.
+ * Calculate message digests of message.
  * Can be called repeatedly with chunks of the message to be hashed.
  *
  * @param ctx the rhash context
  * @param message message chunk
  * @param length length of the message chunk
- * @return 0 on success; On fail return -1 and set errno
+ * @return 0 on success, -1 on fail with error code stored in errno
  */
 RHASH_API int rhash_update(rhash ctx, const void* message, size_t length);
 
 /**
- * Hash a file or stream. Multiple hashes can be computed.
+ * Process a file or stream. Multiple message digests can be computed.
  * First, inintialize ctx parameter with rhash_init() before calling
  * rhash_file_update(). Then use rhash_final() and rhash_print()
- * to retrive hash values. Finaly call rhash_free() on ctx
+ * to retrive message digests. Finaly call rhash_free() on ctx
  * to free allocated memory or call rhash_reset() to reuse ctx.
  *
  * @param ctx rhash context
  * @param fd descriptor of the file to hash
- * @return 0 on success, -1 on error and errno is set
+ * @return 0 on success, -1 on fail with error code stored in errno
  */
 RHASH_API int rhash_file_update(rhash ctx, FILE* fd);
 
 /**
- * Finalize hash calculation and optionally store the first hash.
+ * Finalize message digest calculation and optionally store the first message digest.
  *
  * @param ctx the rhash context
- * @param first_result optional buffer to store a calculated hash with the lowest available id
- * @return 0 on success; On fail return -1 and set errno
+ * @param first_result optional buffer to store a calculated message digest with the lowest available id
+ * @return 0 on success, -1 on fail with error code stored in errno
  */
 RHASH_API int rhash_final(rhash ctx, unsigned char* first_result);
 
@@ -224,7 +244,7 @@
 /**
  * Free RHash context memory.
  *
- * @param ctx the context to free.
+ * @param ctx the context to free
  */
 RHASH_API void rhash_free(rhash ctx);
 
@@ -238,8 +258,33 @@
  * @param callback pointer to the callback function
  * @param callback_data pointer to data passed to the callback
  */
-RHASH_API void  rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data);
+RHASH_API void rhash_set_callback(rhash ctx, rhash_callback_t callback, void* callback_data);
 
+/**
+ * Export RHash context data to a memory region.
+ * The size of the memory required for export
+ * is returned by rhash_export(ctx, NULL, 0).
+ *
+ * @param ctx the rhash context to export
+ * @param out pointer to a memory region, or NULL
+ * @param size the size of a memory region
+ * @return the size of exported data on success export.
+ *         The size of memory required for export if out is NULL.
+ *         0 on fail with error code stored in errno
+ */
+RHASH_API size_t rhash_export(rhash ctx, void* out, size_t size);
+
+/**
+ * Import rhash context from a memory region.
+ * The returned rhash context must be released after usage
+ * by rhash_free().
+ *
+ * @param in pointer to a memory region
+ * @param size the size of a memory region
+ * @return imported rhash context on success,
+ *         NULL on fail with error code stored in errno
+ */
+RHASH_API rhash rhash_import(const void* in, size_t size);
 
 /* INFORMATION FUNCTIONS */
 
@@ -248,37 +293,37 @@
  *
  * @return the number of supported hash functions
  */
-RHASH_API int  rhash_count(void); /* number of supported hashes */
+RHASH_API int rhash_count(void);
 
 /**
- * Returns size of binary digest for given hash algorithm.
+ * Returns the size of binary message digest for given hash function.
  *
- * @param hash_id the id of hash algorithm
- * @return digest size in bytes
+ * @param hash_id the id of the hash function
+ * @return the size of the message digest in bytes
  */
-RHASH_API int  rhash_get_digest_size(unsigned hash_id); /* size of binary message digest */
+RHASH_API int rhash_get_digest_size(unsigned hash_id);
 
 /**
- * Returns length of digest hash string in default output format.
+ * Returns the length of message digest string in its default output format.
  *
- * @param hash_id the id of hash algorithm
- * @return the length of hash string
+ * @param hash_id the id of the hash function
+ * @return the length of the message digest
  */
-RHASH_API int  rhash_get_hash_length(unsigned hash_id); /* length of formatted hash string */
+RHASH_API int rhash_get_hash_length(unsigned hash_id);
 
 /**
- * Detect default digest output format for given hash algorithm.
+ * Detect default message digest output format for the given hash algorithm.
  *
  * @param hash_id the id of hash algorithm
  * @return 1 for base32 format, 0 for hexadecimal
  */
-RHASH_API int  rhash_is_base32(unsigned hash_id); /* default digest output format */
+RHASH_API int rhash_is_base32(unsigned hash_id);
 
 /**
- * Returns a name of given hash algorithm.
+ * Returns the name of the given hash function.
  *
- * @param hash_id the id of hash algorithm
- * @return algorithm name
+ * @param hash_id id of the hash function
+ * @return hash function name
  */
 RHASH_API const char* rhash_get_name(unsigned hash_id); /* get hash function name */
 
@@ -287,7 +332,7 @@
  * Such magnet_name is used to generate a magnet link of the form
  * urn:&lt;magnet_name&gt;=&lt;hash_value&gt;.
  *
- * @param hash_id the id of hash algorithm
+ * @param hash_id id of the hash algorithm
  * @return name
  */
 RHASH_API const char* rhash_get_magnet_name(unsigned hash_id); /* get name part of magnet urn */
@@ -296,7 +341,7 @@
 
 #if 0
 /**
- * Flags for printing a hash sum.
+ * Flags for printing a message digest.
  */
 enum rhash_print_sum_flags
 {
@@ -326,7 +371,7 @@
 	 */
 	RHPR_UPPERCASE = 0x8,
 	/*
-	 * Reverse hash bytes. Can be used for GOST hash.
+	 * Reverse message digest bytes. Can be used for GOST hash functions.
 	 */
 	RHPR_REVERSE   = 0x10,
 	/*
@@ -346,12 +391,12 @@
 
 
 /**
- * Print a text presentation of a given hash sum to the specified buffer.
+ * Print to the specified buffer the text representation of the given message digest.
  *
- * @param output a buffer to print the hash to
- * @param bytes a hash sum to print
- * @param size a size of hash sum in bytes
- * @param flags  a bit-mask controlling how to format the hash sum,
+ * @param output a buffer to print the message digest to
+ * @param bytes a binary message digest to print
+ * @param size a size of the message digest in bytes
+ * @param flags  a bit-mask controlling how to format the message digest,
  *               can be a mix of the flags: RHPR_RAW, RHPR_HEX, RHPR_BASE32,
  *               RHPR_BASE64, RHPR_URLENCODE, RHPR_UPPERCASE, RHPR_REVERSE
  * @return the number of written characters
@@ -360,33 +405,33 @@
 	const unsigned char* bytes, size_t size, int flags);
 
 /**
- * Print text presentation of a hash sum with given hash_id to the specified
- * output buffer. If the hash_id is zero, then print the hash sum with
- * the lowest id stored in the hash context.
- * The function call fails if the context doesn't include a hash with the
+ * Print to the specified output buffer the text representation of the message digest
+ * with the given hash_id. If the hash_id is zero, then print the message digest with
+ * the lowest hash_id calculated by the hash context.
+ * The function call fails if the context doesn't include the message digest with the
  * given hash_id.
  *
- * @param output a buffer to print the hash to
- * @param ctx    algorithms state
- * @param hash_id id of the hash sum to print or 0 to print the first hash
- *                saved in the context.
- * @param flags  a bitmask controlling how to print the hash. Can contain flags
- *               RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc.
+ * @param output a buffer to print the message digest to
+ * @param ctx     algorithms state
+ * @param hash_id id of the message digest to print or 0 to print the first
+ *                message digest saved in the context.
+ * @param flags  a bitmask controlling how to print the message digest. Can contain
+ *               flags RHPR_UPPERCASE, RHPR_HEX, RHPR_BASE32, RHPR_BASE64, etc.
  * @return the number of written characters on success or 0 on fail
  */
 RHASH_API size_t rhash_print(char* output, rhash ctx, unsigned hash_id,
 	int flags);
 
 /**
- * Print magnet link with given filepath and calculated hash sums into the
- * output buffer. The hash_mask can limit which hash values will be printed.
+ * Print magnet link with given filepath and calculated message digest into the
+ * output buffer. The hash_mask can limit which message digests will be printed.
  * The function returns the size of the required buffer.
  * If output is NULL the .
  *
  * @param output a string buffer to receive the magnet link or NULL
  * @param filepath the file path to be printed or NULL
  * @param context algorithms state
- * @param hash_mask bit mask of the hash sums to add to the link
+ * @param hash_mask bit mask of the message digest to add to the link
  * @param flags   can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET,
  *                RHPR_FILESIZE
  * @return number of written characters, including terminating '\0' on success, 0 on fail
@@ -445,19 +490,20 @@
 #define RMSG_GET_OPENSSL_MASK 11
 #define RMSG_GET_OPENSSL_SUPPORTED_MASK 12
 #define RMSG_GET_OPENSSL_AVAILABLE_MASK 13
+#define RMSG_GET_LIBRHASH_VERSION 20
 
 /* HELPER MACROS */
 
 /**
- * Get a pointer to context of the specified hash function.
+ * Get a pointer to the context of the specified hash function.
  */
 #define rhash_get_context_ptr(ctx, hash_id) RHASH_UPTR2PVOID(rhash_transmit(RMSG_GET_CONTEXT, ctx, hash_id, 0))
 /**
- * Cancel hash calculation of a file.
+ * Cancel file processing.
  */
 #define rhash_cancel(ctx) rhash_transmit(RMSG_CANCEL, ctx, 0, 0)
 /**
- * Return non-zero if hash calculation was canceled, zero otherwise.
+ * Return non-zero if a message digest calculation was canceled, zero otherwise.
  */
 #define rhash_is_canceled(ctx) rhash_transmit(RMSG_IS_CANCELED, ctx, 0, 0)
 /**
@@ -468,7 +514,7 @@
 /**
  * Turn on/off the auto-final flag for the given rhash_context. By default
  * auto-final is on, which means rhash_final is called automatically, if
- * needed when a hash value is retrieved by rhash_print call.
+ * needed when a message digest is retrieved by rhash_print call.
  */
 #define rhash_set_autofinal(ctx, on) rhash_transmit(RMSG_SET_AUTOFINAL, ctx, on, 0)
 
@@ -500,9 +546,13 @@
  */
 #define rhash_get_openssl_available_mask() rhash_transmit(RMSG_GET_OPENSSL_AVAILABLE_MASK, NULL, 0, 0)
 
+/**
+ * Return librhash version.
+ */
+#define rhash_get_version() rhash_transmit(RMSG_GET_LIBRHASH_VERSION, NULL, 0, 0)
 
 /**
- * Return non-zero if LibRHash hash been compiled with OpenSSL support,
+ * Return non-zero if LibRHash has been compiled with OpenSSL support,
  * and zero otherwise.
  */
 #define rhash_is_openssl_supported() (rhash_get_openssl_mask() != RHASH_ERROR)
diff --git a/Utilities/cmlibrhash/librhash/sha1.c b/Utilities/cmlibrhash/librhash/sha1.c
index b226925..cbc2b72 100644
--- a/Utilities/cmlibrhash/librhash/sha1.c
+++ b/Utilities/cmlibrhash/librhash/sha1.c
@@ -20,7 +20,7 @@
 #include "sha1.h"
 
 /**
- * Initialize context before calculaing hash.
+ * Initialize context before calculating hash.
  *
  * @param ctx context to initialize
  */
@@ -36,6 +36,23 @@
 	ctx->hash[4] = 0xc3d2e1f0;
 }
 
+/* constants for SHA1 rounds */
+static const uint32_t K0 = 0x5a827999;
+static const uint32_t K1 = 0x6ed9eba1;
+static const uint32_t K2 = 0x8f1bbcdc;
+static const uint32_t K3 = 0xca62c1d6;
+
+/* round functions for SHA1 */
+#define CHO(X,Y,Z) (((X)&(Y))|((~(X))&(Z)))
+#define PAR(X,Y,Z) ((X)^(Y)^(Z))
+#define MAJ(X,Y,Z) (((X)&(Y))|((X)&(Z))|((Y)&(Z)))
+
+#define ROUND_0(a,b,c,d,e, FF, k, w) e +=              FF(b,       c,           d    )+ROTL32(a,5)+k+w
+#define ROUND_1(a,b,c,d,e, FF, k, w) e +=              FF(b,ROTL32(c,30),       d    )+ROTL32(a,5)+k+w
+#define ROUND_2(a,b,c,d,e, FF, k, w) e +=              FF(b,ROTL32(c,30),ROTL32(d,30))+ROTL32(a,5)+k+w
+#define ROUND(a,b,c,d,e, FF, k, w)   e  = ROTL32(e,30)+FF(b,ROTL32(c,30),ROTL32(d,30))+ROTL32(a,5)+k+w
+
+
 /**
  * The core transformation. Process a 512-bit block.
  * The function has been taken from RFC 3174 with little changes.
@@ -45,21 +62,9 @@
  */
 static void rhash_sha1_process_block(unsigned* hash, const unsigned* block)
 {
-	int           t;                 /* Loop counter */
-	uint32_t      temp;              /* Temporary word value */
 	uint32_t      W[80];             /* Word sequence */
 	uint32_t      A, B, C, D, E;     /* Word buffers */
 
-	/* initialize the first 16 words in the array W */
-	for (t = 0; t < 16; t++) {
-		/* note: it is much faster to apply be2me here, then using be32_copy */
-		W[t] = be2me_32(block[t]);
-	}
-
-	/* initialize the rest */
-	for (t = 16; t < 80; t++) {
-		W[t] = ROTL32(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
-	}
 
 	A = hash[0];
 	B = hash[1];
@@ -67,50 +72,189 @@
 	D = hash[3];
 	E = hash[4];
 
-	for (t = 0; t < 20; t++) {
-		/* the following is faster than ((B & C) | ((~B) & D)) */
-		temp =  ROTL32(A, 5) + (((C ^ D) & B) ^ D)
-			+ E + W[t] + 0x5A827999;
-		E = D;
-		D = C;
-		C = ROTL32(B, 30);
-		B = A;
-		A = temp;
-	}
+	/* 0..19 */
+	W[ 0] = be2me_32(block[ 0]);
+	ROUND_0(A,B,C,D,E, CHO, K0, W[ 0]);
+	W[ 1] = be2me_32(block[ 1]);
+	ROUND_1(E,A,B,C,D, CHO, K0, W[ 1]);
+	W[ 2] = be2me_32(block[ 2]);
+	ROUND_2(D,E,A,B,C, CHO, K0, W[ 2]);
+	W[ 3] = be2me_32(block[ 3]);
+	ROUND(C,D,E,A,B, CHO, K0, W[ 3]);
+	W[ 4] = be2me_32(block[ 4]);
+	ROUND(B,C,D,E,A, CHO, K0, W[ 4]);
 
-	for (t = 20; t < 40; t++) {
-		temp = ROTL32(A, 5) + (B ^ C ^ D) + E + W[t] + 0x6ED9EBA1;
-		E = D;
-		D = C;
-		C = ROTL32(B, 30);
-		B = A;
-		A = temp;
-	}
+	W[ 5] = be2me_32(block[ 5]);
+	ROUND(A,B,C,D,E, CHO, K0, W[ 5]);
+	W[ 6] = be2me_32(block[ 6]);
+	ROUND(E,A,B,C,D, CHO, K0, W[ 6]);
+	W[ 7] = be2me_32(block[ 7]);
+	ROUND(D,E,A,B,C, CHO, K0, W[ 7]);
+	W[ 8] = be2me_32(block[ 8]);
+	ROUND(C,D,E,A,B, CHO, K0, W[ 8]);
+	W[ 9] = be2me_32(block[ 9]);
+	ROUND(B,C,D,E,A, CHO, K0, W[ 9]);
 
-	for (t = 40; t < 60; t++) {
-		temp = ROTL32(A, 5) + ((B & C) | (B & D) | (C & D))
-			+ E + W[t] + 0x8F1BBCDC;
-		E = D;
-		D = C;
-		C = ROTL32(B, 30);
-		B = A;
-		A = temp;
-	}
+	W[10] = be2me_32(block[10]);
+	ROUND(A,B,C,D,E, CHO, K0, W[10]);
+	W[11] = be2me_32(block[11]);
+	ROUND(E,A,B,C,D, CHO, K0, W[11]);
+	W[12] = be2me_32(block[12]);
+	ROUND(D,E,A,B,C, CHO, K0, W[12]);
+	W[13] = be2me_32(block[13]);
+	ROUND(C,D,E,A,B, CHO, K0, W[13]);
+	W[14] = be2me_32(block[14]);
+	ROUND(B,C,D,E,A, CHO, K0, W[14]);
 
-	for (t = 60; t < 80; t++) {
-		temp = ROTL32(A, 5) + (B ^ C ^ D) + E + W[t] + 0xCA62C1D6;
-		E = D;
-		D = C;
-		C = ROTL32(B, 30);
-		B = A;
-		A = temp;
-	}
+	W[15] = be2me_32(block[15]);
+	ROUND(A,B,C,D,E, CHO, K0, W[15]);
+	W[16] = ROTL32(W[13] ^ W[ 8] ^ W[ 2] ^ W[ 0], 1);
+	ROUND(E,A,B,C,D, CHO, K0, W[16]);
+	W[17] = ROTL32(W[14] ^ W[ 9] ^ W[ 3] ^ W[ 1], 1);
+	ROUND(D,E,A,B,C, CHO, K0, W[17]);
+	W[18] = ROTL32(W[15] ^ W[10] ^ W[ 4] ^ W[ 2], 1);
+	ROUND(C,D,E,A,B, CHO, K0, W[18]);
+	W[19] = ROTL32(W[16] ^ W[11] ^ W[ 5] ^ W[ 3], 1);
+	ROUND(B,C,D,E,A, CHO, K0, W[19]);
+	/* 20..39 */
+	W[20] = ROTL32(W[17] ^ W[12] ^ W[ 6] ^ W[ 4], 1);
+	ROUND(A,B,C,D,E, PAR, K1, W[20]);
+	W[21] = ROTL32(W[18] ^ W[13] ^ W[ 7] ^ W[ 5], 1);
+	ROUND(E,A,B,C,D, PAR, K1, W[21]);
+	W[22] = ROTL32(W[19] ^ W[14] ^ W[ 8] ^ W[ 6], 1);
+	ROUND(D,E,A,B,C, PAR, K1, W[22]);
+	W[23] = ROTL32(W[20] ^ W[15] ^ W[ 9] ^ W[ 7], 1);
+	ROUND(C,D,E,A,B, PAR, K1, W[23]);
+	W[24] = ROTL32(W[21] ^ W[16] ^ W[10] ^ W[ 8], 1);
+	ROUND(B,C,D,E,A, PAR, K1, W[24]);
+
+	W[25] = ROTL32(W[22] ^ W[17] ^ W[11] ^ W[ 9], 1);
+	ROUND(A,B,C,D,E, PAR, K1, W[25]);
+	W[26] = ROTL32(W[23] ^ W[18] ^ W[12] ^ W[10], 1);
+	ROUND(E,A,B,C,D, PAR, K1, W[26]);
+	W[27] = ROTL32(W[24] ^ W[19] ^ W[13] ^ W[11], 1);
+	ROUND(D,E,A,B,C, PAR, K1, W[27]);
+	W[28] = ROTL32(W[25] ^ W[20] ^ W[14] ^ W[12], 1);
+	ROUND(C,D,E,A,B, PAR, K1, W[28]);
+	W[29] = ROTL32(W[26] ^ W[21] ^ W[15] ^ W[13], 1);
+	ROUND(B,C,D,E,A, PAR, K1, W[29]);
+
+	W[30] = ROTL32(W[27] ^ W[22] ^ W[16] ^ W[14], 1);
+	ROUND(A,B,C,D,E, PAR, K1, W[30]);
+	W[31] = ROTL32(W[28] ^ W[23] ^ W[17] ^ W[15], 1);
+	ROUND(E,A,B,C,D, PAR, K1, W[31]);
+	W[32] = ROTL32(W[29] ^ W[24] ^ W[18] ^ W[16], 1);
+	ROUND(D,E,A,B,C, PAR, K1, W[32]);
+	W[33] = ROTL32(W[30] ^ W[25] ^ W[19] ^ W[17], 1);
+	ROUND(C,D,E,A,B, PAR, K1, W[33]);
+	W[34] = ROTL32(W[31] ^ W[26] ^ W[20] ^ W[18], 1);
+	ROUND(B,C,D,E,A, PAR, K1, W[34]);
+
+	W[35] = ROTL32(W[32] ^ W[27] ^ W[21] ^ W[19], 1);
+	ROUND(A,B,C,D,E, PAR, K1, W[35]);
+	W[36] = ROTL32(W[33] ^ W[28] ^ W[22] ^ W[20], 1);
+	ROUND(E,A,B,C,D, PAR, K1, W[36]);
+	W[37] = ROTL32(W[34] ^ W[29] ^ W[23] ^ W[21], 1);
+	ROUND(D,E,A,B,C, PAR, K1, W[37]);
+	W[38] = ROTL32(W[35] ^ W[30] ^ W[24] ^ W[22], 1);
+	ROUND(C,D,E,A,B, PAR, K1, W[38]);
+	W[39] = ROTL32(W[36] ^ W[31] ^ W[25] ^ W[23], 1);
+	ROUND(B,C,D,E,A, PAR, K1, W[39]);
+	/* 40..59 */
+	W[40] = ROTL32(W[37] ^ W[32] ^ W[26] ^ W[24], 1);
+	ROUND(A,B,C,D,E, MAJ, K2, W[40]);
+	W[41] = ROTL32(W[38] ^ W[33] ^ W[27] ^ W[25], 1);
+	ROUND(E,A,B,C,D, MAJ, K2, W[41]);
+	W[42] = ROTL32(W[39] ^ W[34] ^ W[28] ^ W[26], 1);
+	ROUND(D,E,A,B,C, MAJ, K2, W[42]);
+	W[43] = ROTL32(W[40] ^ W[35] ^ W[29] ^ W[27], 1);
+	ROUND(C,D,E,A,B, MAJ, K2, W[43]);
+	W[44] = ROTL32(W[41] ^ W[36] ^ W[30] ^ W[28], 1);
+	ROUND(B,C,D,E,A, MAJ, K2, W[44]);
+
+	W[45] = ROTL32(W[42] ^ W[37] ^ W[31] ^ W[29], 1);
+	ROUND(A,B,C,D,E, MAJ, K2, W[45]);
+	W[46] = ROTL32(W[43] ^ W[38] ^ W[32] ^ W[30], 1);
+	ROUND(E,A,B,C,D, MAJ, K2, W[46]);
+	W[47] = ROTL32(W[44] ^ W[39] ^ W[33] ^ W[31], 1);
+	ROUND(D,E,A,B,C, MAJ, K2, W[47]);
+	W[48] = ROTL32(W[45] ^ W[40] ^ W[34] ^ W[32], 1);
+	ROUND(C,D,E,A,B, MAJ, K2, W[48]);
+	W[49] = ROTL32(W[46] ^ W[41] ^ W[35] ^ W[33], 1);
+	ROUND(B,C,D,E,A, MAJ, K2, W[49]);
+
+	W[50] = ROTL32(W[47] ^ W[42] ^ W[36] ^ W[34], 1);
+	ROUND(A,B,C,D,E, MAJ, K2, W[50]);
+	W[51] = ROTL32(W[48] ^ W[43] ^ W[37] ^ W[35], 1);
+	ROUND(E,A,B,C,D, MAJ, K2, W[51]);
+	W[52] = ROTL32(W[49] ^ W[44] ^ W[38] ^ W[36], 1);
+	ROUND(D,E,A,B,C, MAJ, K2, W[52]);
+	W[53] = ROTL32(W[50] ^ W[45] ^ W[39] ^ W[37], 1);
+	ROUND(C,D,E,A,B, MAJ, K2, W[53]);
+	W[54] = ROTL32(W[51] ^ W[46] ^ W[40] ^ W[38], 1);
+	ROUND(B,C,D,E,A, MAJ, K2, W[54]);
+
+	W[55] = ROTL32(W[52] ^ W[47] ^ W[41] ^ W[39], 1);
+	ROUND(A,B,C,D,E, MAJ, K2, W[55]);
+	W[56] = ROTL32(W[53] ^ W[48] ^ W[42] ^ W[40], 1);
+	ROUND(E,A,B,C,D, MAJ, K2, W[56]);
+	W[57] = ROTL32(W[54] ^ W[49] ^ W[43] ^ W[41], 1);
+	ROUND(D,E,A,B,C, MAJ, K2, W[57]);
+	W[58] = ROTL32(W[55] ^ W[50] ^ W[44] ^ W[42], 1);
+	ROUND(C,D,E,A,B, MAJ, K2, W[58]);
+	W[59] = ROTL32(W[56] ^ W[51] ^ W[45] ^ W[43], 1);
+	ROUND(B,C,D,E,A, MAJ, K2, W[59]);
+	/* 60..79 */
+	W[60] = ROTL32(W[57] ^ W[52] ^ W[46] ^ W[44], 1);
+	ROUND(A,B,C,D,E, PAR, K3, W[60]);
+	W[61] = ROTL32(W[58] ^ W[53] ^ W[47] ^ W[45], 1);
+	ROUND(E,A,B,C,D, PAR, K3, W[61]);
+	W[62] = ROTL32(W[59] ^ W[54] ^ W[48] ^ W[46], 1);
+	ROUND(D,E,A,B,C, PAR, K3, W[62]);
+	W[63] = ROTL32(W[60] ^ W[55] ^ W[49] ^ W[47], 1);
+	ROUND(C,D,E,A,B, PAR, K3, W[63]);
+	W[64] = ROTL32(W[61] ^ W[56] ^ W[50] ^ W[48], 1);
+	ROUND(B,C,D,E,A, PAR, K3, W[64]);
+
+	W[65] = ROTL32(W[62] ^ W[57] ^ W[51] ^ W[49], 1);
+	ROUND(A,B,C,D,E, PAR, K3, W[65]);
+	W[66] = ROTL32(W[63] ^ W[58] ^ W[52] ^ W[50], 1);
+	ROUND(E,A,B,C,D, PAR, K3, W[66]);
+	W[67] = ROTL32(W[64] ^ W[59] ^ W[53] ^ W[51], 1);
+	ROUND(D,E,A,B,C, PAR, K3, W[67]);
+	W[68] = ROTL32(W[65] ^ W[60] ^ W[54] ^ W[52], 1);
+	ROUND(C,D,E,A,B, PAR, K3, W[68]);
+	W[69] = ROTL32(W[66] ^ W[61] ^ W[55] ^ W[53], 1);
+	ROUND(B,C,D,E,A, PAR, K3, W[69]);
+
+	W[70] = ROTL32(W[67] ^ W[62] ^ W[56] ^ W[54], 1);
+	ROUND(A,B,C,D,E, PAR, K3, W[70]);
+	W[71] = ROTL32(W[68] ^ W[63] ^ W[57] ^ W[55], 1);
+	ROUND(E,A,B,C,D, PAR, K3, W[71]);
+	W[72] = ROTL32(W[69] ^ W[64] ^ W[58] ^ W[56], 1);
+	ROUND(D,E,A,B,C, PAR, K3, W[72]);
+	W[73] = ROTL32(W[70] ^ W[65] ^ W[59] ^ W[57], 1);
+	ROUND(C,D,E,A,B, PAR, K3, W[73]);
+	W[74] = ROTL32(W[71] ^ W[66] ^ W[60] ^ W[58], 1);
+	ROUND(B,C,D,E,A, PAR, K3, W[74]);
+
+	W[75] = ROTL32(W[72] ^ W[67] ^ W[61] ^ W[59], 1);
+	ROUND(A,B,C,D,E, PAR, K3, W[75]);
+	W[76] = ROTL32(W[73] ^ W[68] ^ W[62] ^ W[60], 1);
+	ROUND(E,A,B,C,D, PAR, K3, W[76]);
+	W[77] = ROTL32(W[74] ^ W[69] ^ W[63] ^ W[61], 1);
+	ROUND(D,E,A,B,C, PAR, K3, W[77]);
+	W[78] = ROTL32(W[75] ^ W[70] ^ W[64] ^ W[62], 1);
+	ROUND(C,D,E,A,B, PAR, K3, W[78]);
+	W[79] = ROTL32(W[76] ^ W[71] ^ W[65] ^ W[63], 1);
+	ROUND(B,C,D,E,A, PAR, K3, W[79]);
+
 
 	hash[0] += A;
 	hash[1] += B;
-	hash[2] += C;
-	hash[3] += D;
-	hash[4] += E;
+	hash[2] += ROTL32(C,30);
+	hash[3] += ROTL32(D,30);
+	hash[4] += ROTL32(E,30);
 }
 
 /**
diff --git a/Utilities/cmlibrhash/librhash/sha256.c b/Utilities/cmlibrhash/librhash/sha256.c
index 21a69aa..69d28ce 100644
--- a/Utilities/cmlibrhash/librhash/sha256.c
+++ b/Utilities/cmlibrhash/librhash/sha256.c
@@ -61,7 +61,7 @@
 	ROUND(a,b,c,d,e,f,g,h, k[n], RECALCULATE_W(W, n))
 
 /**
- * Initialize context before calculaing hash.
+ * Initialize context before calculating hash.
  *
  * @param ctx context to initialize
  */
@@ -74,7 +74,7 @@
 		0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
 		0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
 	};
-
+	memset(ctx->message, 0, sizeof(ctx->message));
 	ctx->length = 0;
 	ctx->digest_length = sha256_hash_size;
 
@@ -83,7 +83,7 @@
 }
 
 /**
- * Initialize context before calculaing hash.
+ * Initialize context before calculating hash.
  *
  * @param ctx context to initialize
  */
@@ -96,7 +96,7 @@
 		0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
 		0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
 	};
-
+	memset(ctx->message, 0, sizeof(ctx->message));
 	ctx->length = 0;
 	ctx->digest_length = sha224_hash_size;
 
diff --git a/Utilities/cmlibrhash/librhash/sha256.h b/Utilities/cmlibrhash/librhash/sha256.h
index 3625cfe..33ce9d9 100644
--- a/Utilities/cmlibrhash/librhash/sha256.h
+++ b/Utilities/cmlibrhash/librhash/sha256.h
@@ -23,7 +23,7 @@
 void rhash_sha224_init(sha256_ctx* ctx);
 void rhash_sha256_init(sha256_ctx* ctx);
 void rhash_sha256_update(sha256_ctx* ctx, const unsigned char* data, size_t length);
-void rhash_sha256_final(sha256_ctx* ctx, unsigned char result[32]);
+void rhash_sha256_final(sha256_ctx* ctx, unsigned char* result);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/Utilities/cmlibrhash/librhash/sha512.c b/Utilities/cmlibrhash/librhash/sha512.c
index 555e6ef..a9901dd 100644
--- a/Utilities/cmlibrhash/librhash/sha512.c
+++ b/Utilities/cmlibrhash/librhash/sha512.c
@@ -91,7 +91,7 @@
 		I64(0xa54ff53a5f1d36f1), I64(0x510e527fade682d1), I64(0x9b05688c2b3e6c1f),
 		I64(0x1f83d9abfb41bd6b), I64(0x5be0cd19137e2179)
 	};
-
+	memset(ctx->message, 0, sizeof(ctx->message));
 	ctx->length = 0;
 	ctx->digest_length = sha512_hash_size;
 
@@ -100,7 +100,7 @@
 }
 
 /**
- * Initialize context before calculaing hash.
+ * Initialize context before calculating hash.
  *
  * @param ctx context to initialize
  */
@@ -114,7 +114,7 @@
 		I64(0x152fecd8f70e5939), I64(0x67332667ffc00b31), I64(0x8eb44a8768581511),
 		I64(0xdb0c2e0d64f98fa7), I64(0x47b5481dbefa4fa4)
 	};
-
+	memset(ctx->message, 0, sizeof(ctx->message));
 	ctx->length = 0;
 	ctx->digest_length = sha384_hash_size;
 
diff --git a/Utilities/cmlibrhash/librhash/util.c b/Utilities/cmlibrhash/librhash/util.c
new file mode 100644
index 0000000..8266460
--- /dev/null
+++ b/Utilities/cmlibrhash/librhash/util.c
@@ -0,0 +1,61 @@
+/* util.c - memory functions.
+ *
+ * Copyright (c) 2020, Aleksey Kravchenko <rhash.admin@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE  INCLUDING ALL IMPLIED WARRANTIES OF  MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT,  OR CONSEQUENTIAL DAMAGES  OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION OF CONTRACT,  NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION,  ARISING OUT OF  OR IN CONNECTION  WITH THE USE  OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "util.h"
+
+#if defined(HAS_POSIX_ALIGNED_ALLOC)
+
+#include <errno.h>
+
+void* rhash_px_aalloc(size_t alignment, size_t size)
+{
+	void* ptr;
+	if ((errno = posix_memalign(&ptr, alignment, size)) != 0)
+		return NULL;
+	return ptr;
+}
+
+#elif defined(HAS_GENERIC_ALIGNED_ALLOC)
+
+#include <assert.h>
+#include <stdlib.h>
+
+void* rhash_aligned_alloc(size_t alignment, size_t size)
+{
+	unsigned char* block = (unsigned char*)malloc(size + alignment);
+	assert((alignment & (alignment - 1)) == 0);
+	assert(alignment >= sizeof(void*));
+	if (block) {
+		const size_t alignment_mask = (alignment - 1);
+		unsigned char* basement = block + sizeof(void*);
+		size_t offset = ((unsigned char*)0 - basement) & alignment_mask;
+		void** result = (void**)(basement + offset);
+		assert((((unsigned char*)result - (unsigned char*)0) % alignment) == 0);
+		result[-1] = block; /* store original pointer */
+		return result;
+	}
+	return NULL;
+}
+
+void rhash_aligned_free(void* ptr)
+{
+	void** pfree = (void**)ptr;
+	if (ptr)
+		free(pfree[-1]);
+}
+
+#else
+typedef int dummy_declaration_required_by_strict_iso_c;
+#endif /* HAS_POSIX_ALIGNED_ALLOC / HAS_GENERIC_ALIGNED_ALLOC */
diff --git a/Utilities/cmlibrhash/librhash/util.h b/Utilities/cmlibrhash/librhash/util.h
index 57cae9b..a0a0674 100644
--- a/Utilities/cmlibrhash/librhash/util.h
+++ b/Utilities/cmlibrhash/librhash/util.h
@@ -2,10 +2,15 @@
 #ifndef UTIL_H
 #define UTIL_H
 
+#include <stdlib.h> /* for aligned_alloc and __GLIBC__ version macros */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* compile-time assert */
+#define RHASH_ASSERT(cond) (void)sizeof(char[1 - 2 * !(cond)])
+
 #if (defined(__GNUC__) && __GNUC__ >= 4 && (__GNUC__ > 4 || __GNUC_MINOR__ >= 1) \
 	&& defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) \
 	|| (defined(__INTEL_COMPILER) && !defined(_WIN32))
@@ -24,6 +29,50 @@
 # define NO_ATOMIC_BUILTINS
 #endif
 
+/* alignment macros */
+#define DEFAULT_ALIGNMENT 64
+#define ALIGN_SIZE_BY(size, align) (((size) + ((align) - 1)) & ~((align) - 1))
+#define IS_SIZE_ALIGNED_BY(size, align) (((size) & ((align) - 1)) == 0)
+#define IS_PTR_ALIGNED_BY(ptr, align) IS_SIZE_ALIGNED_BY((uintptr_t)(ptr), (align))
+
+/* define rhash_aligned_alloc() and rhash_aligned_free() */
+#if !defined(NO_WIN32_ALIGNED_ALLOC) && defined(_WIN32)
+
+# define HAS_WIN32_ALIGNED_ALLOC
+# include <malloc.h>
+# define rhash_aligned_alloc(alignment, size) _aligned_malloc((size), (alignment))
+# define rhash_aligned_free(ptr) _aligned_free(ptr)
+
+#elif !defined(NO_STDC_ALIGNED_ALLOC) && (__STDC_VERSION__ >= 201112L || defined(_ISOC11_SOURCE)) \
+	&& !(defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 15))) \
+	&& !(defined(__ibmxl__) && defined(__clang__) && defined(__linux__)) \
+	&& !defined(__APPLE__) && !defined(__HAIKU__) && !defined(__sun) \
+	&& (!defined(__ANDROID_API__) || __ANDROID_API__ >= 28)
+
+# define HAS_STDC_ALIGNED_ALLOC
+# define rhash_aligned_alloc(alignment, size) aligned_alloc((alignment), ALIGN_SIZE_BY(size, alignment))
+# define rhash_aligned_free(ptr) free(ptr)
+
+#else /* defined(_WIN32) ... */
+
+# include "ustd.h" /* for _POSIX_VERSION macro */
+
+# if !defined(NO_POSIX_ALIGNED_ALLOC) && (_POSIX_VERSION >= 200112L || _XOPEN_SOURCE >= 600)
+
+#  define HAS_POSIX_ALIGNED_ALLOC
+#  define rhash_aligned_alloc(alignment, size) rhash_px_aalloc((alignment), ALIGN_SIZE_BY(size, sizeof(void*)))
+#  define rhash_aligned_free(ptr) free(ptr)
+void* rhash_px_aalloc(size_t size, size_t alignment);
+
+# else
+
+#  define HAS_GENERIC_ALIGNED_ALLOC
+void* rhash_aligned_alloc(size_t alignment, size_t size);
+void rhash_aligned_free(void* ptr);
+
+# endif /* !defined(NO_POSIX_ALIGNED_ALLOC) ... */
+#endif /* defined(_WIN32) ... */
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* __cplusplus */
diff --git a/Utilities/cmzstd/LICENSE b/Utilities/cmzstd/LICENSE
index a793a80..7580028 100644
--- a/Utilities/cmzstd/LICENSE
+++ b/Utilities/cmzstd/LICENSE
@@ -2,7 +2,7 @@
 
 For Zstandard software
 
-Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
+Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:
@@ -14,9 +14,9 @@
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
 
- * Neither the name Facebook nor the names of its contributors may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
+ * Neither the name Facebook, nor Meta, nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
diff --git a/Utilities/cmzstd/README.md b/Utilities/cmzstd/README.md
index dcca766..f91e68f 100644
--- a/Utilities/cmzstd/README.md
+++ b/Utilities/cmzstd/README.md
@@ -4,23 +4,21 @@
 targeting real-time compression scenarios at zlib-level and better compression ratios.
 It's backed by a very fast entropy stage, provided by [Huff0 and FSE library](https://github.com/Cyan4973/FiniteStateEntropy).
 
-The project is provided as an open-source dual [BSD](LICENSE) and [GPLv2](COPYING) licensed **C** library,
+Zstandard's format is stable and documented in [RFC8878](https://datatracker.ietf.org/doc/html/rfc8878). Multiple independent implementations are already available.
+This repository represents the reference implementation, provided as an open-source dual [BSD](LICENSE) and [GPLv2](COPYING) licensed **C** library,
 and a command line utility producing and decoding `.zst`, `.gz`, `.xz` and `.lz4` files.
 Should your project require another programming language,
-a list of known ports and bindings is provided on [Zstandard homepage](http://www.zstd.net/#other-languages).
+a list of known ports and bindings is provided on [Zstandard homepage](https://facebook.github.io/zstd/#other-languages).
 
 **Development branch status:**
 
 [![Build Status][travisDevBadge]][travisLink]
-[![Build status][AppveyorDevBadge]][AppveyorLink]
 [![Build status][CircleDevBadge]][CircleLink]
 [![Build status][CirrusDevBadge]][CirrusLink]
 [![Fuzzing Status][OSSFuzzBadge]][OSSFuzzLink]
 
-[travisDevBadge]: https://travis-ci.org/facebook/zstd.svg?branch=dev "Continuous Integration test suite"
-[travisLink]: https://travis-ci.org/facebook/zstd
-[AppveyorDevBadge]: https://ci.appveyor.com/api/projects/status/xt38wbdxjk5mrbem/branch/dev?svg=true "Windows test suite"
-[AppveyorLink]: https://ci.appveyor.com/project/YannCollet/zstd-p0yf0
+[travisDevBadge]: https://api.travis-ci.com/facebook/zstd.svg?branch=dev "Continuous Integration test suite"
+[travisLink]: https://travis-ci.com/facebook/zstd
 [CircleDevBadge]: https://circleci.com/gh/facebook/zstd/tree/dev.svg?style=shield "Short test suite"
 [CircleLink]: https://circleci.com/gh/facebook/zstd
 [CirrusDevBadge]: https://api.cirrus-ci.com/github/facebook/zstd.svg?branch=dev
@@ -31,37 +29,36 @@
 ## Benchmarks
 
 For reference, several fast compression algorithms were tested and compared
-on a server running Arch Linux (`Linux version 5.5.11-arch1-1`),
-with a Core i9-9900K CPU @ 5.0GHz,
+on a desktop running Ubuntu 20.04 (`Linux 5.11.0-41-generic`),
+with a Core i7-9700K CPU @ 4.9GHz,
 using [lzbench], an open-source in-memory benchmark by @inikep
 compiled with [gcc] 9.3.0,
 on the [Silesia compression corpus].
 
 [lzbench]: https://github.com/inikep/lzbench
-[Silesia compression corpus]: http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia
+[Silesia compression corpus]: https://sun.aei.polsl.pl//~sdeor/index.php?page=silesia
 [gcc]: https://gcc.gnu.org/
 
 | Compressor name         | Ratio | Compression| Decompress.|
 | ---------------         | ------| -----------| ---------- |
-| **zstd 1.4.5 -1**       | 2.884 |   500 MB/s |  1660 MB/s |
-| zlib 1.2.11 -1          | 2.743 |    90 MB/s |   400 MB/s |
-| brotli 1.0.7 -0         | 2.703 |   400 MB/s |   450 MB/s |
-| **zstd 1.4.5 --fast=1** | 2.434 |   570 MB/s |  2200 MB/s |
-| **zstd 1.4.5 --fast=3** | 2.312 |   640 MB/s |  2300 MB/s |
-| quicklz 1.5.0 -1        | 2.238 |   560 MB/s |   710 MB/s |
-| **zstd 1.4.5 --fast=5** | 2.178 |   700 MB/s |  2420 MB/s |
-| lzo1x 2.10 -1           | 2.106 |   690 MB/s |   820 MB/s |
-| lz4 1.9.2               | 2.101 |   740 MB/s |  4530 MB/s |
-| **zstd 1.4.5 --fast=7** | 2.096 |   750 MB/s |  2480 MB/s |
-| lzf 3.6 -1              | 2.077 |   410 MB/s |   860 MB/s |
-| snappy 1.1.8            | 2.073 |   560 MB/s |  1790 MB/s |
+| **zstd 1.5.1 -1**       | 2.887 |   530 MB/s |  1700 MB/s |
+| [zlib] 1.2.11 -1        | 2.743 |    95 MB/s |   400 MB/s |
+| brotli 1.0.9 -0         | 2.702 |   395 MB/s |   450 MB/s |
+| **zstd 1.5.1 --fast=1** | 2.437 |   600 MB/s |  2150 MB/s |
+| **zstd 1.5.1 --fast=3** | 2.239 |   670 MB/s |  2250 MB/s |
+| quicklz 1.5.0 -1        | 2.238 |   540 MB/s |   760 MB/s |
+| **zstd 1.5.1 --fast=4** | 2.148 |   710 MB/s |  2300 MB/s |
+| lzo1x 2.10 -1           | 2.106 |   660 MB/s |   845 MB/s |
+| [lz4] 1.9.3             | 2.101 |   740 MB/s |  4500 MB/s |
+| lzf 3.6 -1              | 2.077 |   410 MB/s |   830 MB/s |
+| snappy 1.1.9            | 2.073 |   550 MB/s |  1750 MB/s |
 
-[zlib]: http://www.zlib.net/
-[LZ4]: http://www.lz4.org/
+[zlib]: https://www.zlib.net/
+[lz4]: https://lz4.github.io/lz4/
 
 The negative compression levels, specified with `--fast=#`,
-offer faster compression and decompression speed in exchange for some loss in
-compression ratio compared to level 1, as seen in the table above.
+offer faster compression and decompression speed
+at the cost of compression ratio (compared to level 1).
 
 Zstd can also offer stronger compression ratios at the cost of compression speed.
 Speed vs Compression trade-off is configurable by small increments.
@@ -124,14 +121,27 @@
 
 ## Build instructions
 
+`make` is the officially maintained build system of this project.
+All other build systems are "compatible" and 3rd-party maintained,
+they may feature small differences in advanced options.
+When your system allows it, prefer using `make` to build `zstd` and `libzstd`.
+
 ### Makefile
 
 If your system is compatible with standard `make` (or `gmake`),
 invoking `make` in root directory will generate `zstd` cli in root directory.
+It will also create `libzstd` into `lib/`.
 
 Other available options include:
 - `make install` : create and install zstd cli, library and man pages
-- `make check` : create and run `zstd`, tests its behavior on local platform
+- `make check` : create and run `zstd`, test its behavior on local platform
+
+The `Makefile` follows the [GNU Standard Makefile conventions](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html),
+allowing staged install, standard flags, directory variables and command variables.
+
+For advanced use cases, specialized compilation flags which control binary generation
+are documented in [`lib/README.md`](lib/README.md#modular-build) for the `libzstd` library
+and in [`programs/README.md`](programs/README.md#compilation-variables) for the `zstd` CLI.
 
 ### cmake
 
@@ -141,6 +151,18 @@
 
 By default, `CMAKE_BUILD_TYPE` is set to `Release`.
 
+#### Support for Fat (Universal2) Output
+
+`zstd` can be built and installed with support for both Apple Silicon (M1/M2) as well as Intel by using CMake's Universal2 support.
+To perform a Fat/Universal2 build and install use the following commands:
+
+```bash
+cmake -B build-cmake-debug -S build/cmake -G Ninja -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h;arm64"
+cd build-cmake-debug
+ninja
+sudo ninja install
+```
+
 ### Meson
 
 A Meson project is provided within [`build/meson`](build/meson). Follow
@@ -178,13 +200,15 @@
 
 ## Testing
 
-You can run quick local smoke tests by executing the `playTest.sh` script from the `src/tests` directory.
-Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the zstd and datagen binary.
-For information on CI testing, please refer to TESTING.md
+You can run quick local smoke tests by running `make check`.
+If you can't use `make`, execute the `playTest.sh` script from the `src/tests` directory.
+Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the `zstd` and `datagen` binary.
+For information on CI testing, please refer to `TESTING.md`.
 
 ## Status
 
-Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases.
+Zstandard is currently deployed within Facebook and many other large cloud infrastructures.
+It is run continuously to compress large amounts of data in multiple formats and use cases.
 Zstandard is considered safe for production environments.
 
 ## License
diff --git a/Utilities/cmzstd/lib/common/allocations.h b/Utilities/cmzstd/lib/common/allocations.h
new file mode 100644
index 0000000..a3153c4
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/allocations.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* This file provides custom allocation primitives
+ */
+
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"   /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */
+
+#include "mem.h" /* MEM_STATIC */
+#define ZSTD_STATIC_LINKING_ONLY
+#include "../zstd.h" /* ZSTD_customMem */
+
+#ifndef ZSTD_ALLOCATIONS_H
+#define ZSTD_ALLOCATIONS_H
+
+/* custom memory allocation functions */
+
+MEM_STATIC void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem)
+{
+    if (customMem.customAlloc)
+        return customMem.customAlloc(customMem.opaque, size);
+    return ZSTD_malloc(size);
+}
+
+MEM_STATIC void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem)
+{
+    if (customMem.customAlloc) {
+        /* calloc implemented as malloc+memset;
+         * not as efficient as calloc, but next best guess for custom malloc */
+        void* const ptr = customMem.customAlloc(customMem.opaque, size);
+        ZSTD_memset(ptr, 0, size);
+        return ptr;
+    }
+    return ZSTD_calloc(1, size);
+}
+
+MEM_STATIC void ZSTD_customFree(void* ptr, ZSTD_customMem customMem)
+{
+    if (ptr!=NULL) {
+        if (customMem.customFree)
+            customMem.customFree(customMem.opaque, ptr);
+        else
+            ZSTD_free(ptr);
+    }
+}
+
+#endif /* ZSTD_ALLOCATIONS_H */
diff --git a/Utilities/cmzstd/lib/common/bits.h b/Utilities/cmzstd/lib/common/bits.h
new file mode 100644
index 0000000..def56c4
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/bits.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_BITS_H
+#define ZSTD_BITS_H
+
+#include "mem.h"
+
+MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val)
+{
+    assert(val != 0);
+    {
+        static const U32 DeBruijnBytePos[32] = {0, 1, 28, 2, 29, 14, 24, 3,
+                                                30, 22, 20, 15, 25, 17, 4, 8,
+                                                31, 27, 13, 23, 21, 19, 16, 7,
+                                                26, 12, 18, 6, 11, 5, 10, 9};
+        return DeBruijnBytePos[((U32) ((val & -(S32) val) * 0x077CB531U)) >> 27];
+    }
+}
+
+MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
+{
+    assert(val != 0);
+#   if defined(_MSC_VER)
+#       if STATIC_BMI2 == 1
+            return (unsigned)_tzcnt_u32(val);
+#       else
+            if (val != 0) {
+                unsigned long r;
+                _BitScanForward(&r, val);
+                return (unsigned)r;
+            } else {
+                /* Should not reach this code path */
+                __assume(0);
+            }
+#       endif
+#   elif defined(__GNUC__) && (__GNUC__ >= 4)
+        return (unsigned)__builtin_ctz(val);
+#   else
+        return ZSTD_countTrailingZeros32_fallback(val);
+#   endif
+}
+
+MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) {
+    assert(val != 0);
+    {
+        static const U32 DeBruijnClz[32] = {0, 9, 1, 10, 13, 21, 2, 29,
+                                            11, 14, 16, 18, 22, 25, 3, 30,
+                                            8, 12, 20, 28, 15, 17, 24, 7,
+                                            19, 27, 23, 6, 26, 5, 4, 31};
+        val |= val >> 1;
+        val |= val >> 2;
+        val |= val >> 4;
+        val |= val >> 8;
+        val |= val >> 16;
+        return 31 - DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
+    }
+}
+
+MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val)
+{
+    assert(val != 0);
+#   if defined(_MSC_VER)
+#       if STATIC_BMI2 == 1
+            return (unsigned)_lzcnt_u32(val);
+#       else
+            if (val != 0) {
+                unsigned long r;
+                _BitScanReverse(&r, val);
+                return (unsigned)(31 - r);
+            } else {
+                /* Should not reach this code path */
+                __assume(0);
+            }
+#       endif
+#   elif defined(__GNUC__) && (__GNUC__ >= 4)
+        return (unsigned)__builtin_clz(val);
+#   else
+        return ZSTD_countLeadingZeros32_fallback(val);
+#   endif
+}
+
+MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
+{
+    assert(val != 0);
+#   if defined(_MSC_VER) && defined(_WIN64)
+#       if STATIC_BMI2 == 1
+            return (unsigned)_tzcnt_u64(val);
+#       else
+            if (val != 0) {
+                unsigned long r;
+                _BitScanForward64(&r, val);
+                return (unsigned)r;
+            } else {
+                /* Should not reach this code path */
+                __assume(0);
+            }
+#       endif
+#   elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(__LP64__)
+        return (unsigned)__builtin_ctzll(val);
+#   else
+        {
+            U32 mostSignificantWord = (U32)(val >> 32);
+            U32 leastSignificantWord = (U32)val;
+            if (leastSignificantWord == 0) {
+                return 32 + ZSTD_countTrailingZeros32(mostSignificantWord);
+            } else {
+                return ZSTD_countTrailingZeros32(leastSignificantWord);
+            }
+        }
+#   endif
+}
+
+MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val)
+{
+    assert(val != 0);
+#   if defined(_MSC_VER) && defined(_WIN64)
+#       if STATIC_BMI2 == 1
+            return (unsigned)_lzcnt_u64(val);
+#       else
+            if (val != 0) {
+                unsigned long r;
+                _BitScanReverse64(&r, val);
+                return (unsigned)(63 - r);
+            } else {
+                /* Should not reach this code path */
+                __assume(0);
+            }
+#       endif
+#   elif defined(__GNUC__) && (__GNUC__ >= 4)
+        return (unsigned)(__builtin_clzll(val));
+#   else
+        {
+            U32 mostSignificantWord = (U32)(val >> 32);
+            U32 leastSignificantWord = (U32)val;
+            if (mostSignificantWord == 0) {
+                return 32 + ZSTD_countLeadingZeros32(leastSignificantWord);
+            } else {
+                return ZSTD_countLeadingZeros32(mostSignificantWord);
+            }
+        }
+#   endif
+}
+
+MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
+{
+    if (MEM_isLittleEndian()) {
+        if (MEM_64bits()) {
+            return ZSTD_countTrailingZeros64((U64)val) >> 3;
+        } else {
+            return ZSTD_countTrailingZeros32((U32)val) >> 3;
+        }
+    } else {  /* Big Endian CPU */
+        if (MEM_64bits()) {
+            return ZSTD_countLeadingZeros64((U64)val) >> 3;
+        } else {
+            return ZSTD_countLeadingZeros32((U32)val) >> 3;
+        }
+    }
+}
+
+MEM_STATIC unsigned ZSTD_highbit32(U32 val)   /* compress, dictBuilder, decodeCorpus */
+{
+    assert(val != 0);
+    return 31 - ZSTD_countLeadingZeros32(val);
+}
+
+/* ZSTD_rotateRight_*():
+ * Rotates a bitfield to the right by "count" bits.
+ * https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts
+ */
+MEM_STATIC
+U64 ZSTD_rotateRight_U64(U64 const value, U32 count) {
+    assert(count < 64);
+    count &= 0x3F; /* for fickle pattern recognition */
+    return (value >> count) | (U64)(value << ((0U - count) & 0x3F));
+}
+
+MEM_STATIC
+U32 ZSTD_rotateRight_U32(U32 const value, U32 count) {
+    assert(count < 32);
+    count &= 0x1F; /* for fickle pattern recognition */
+    return (value >> count) | (U32)(value << ((0U - count) & 0x1F));
+}
+
+MEM_STATIC
+U16 ZSTD_rotateRight_U16(U16 const value, U32 count) {
+    assert(count < 16);
+    count &= 0x0F; /* for fickle pattern recognition */
+    return (value >> count) | (U16)(value << ((0U - count) & 0x0F));
+}
+
+#endif /* ZSTD_BITS_H */
diff --git a/Utilities/cmzstd/lib/common/bitstream.h b/Utilities/cmzstd/lib/common/bitstream.h
index 136a188..31feb39 100644
--- a/Utilities/cmzstd/lib/common/bitstream.h
+++ b/Utilities/cmzstd/lib/common/bitstream.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * bitstream
  * Part of FSE library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -32,14 +32,15 @@
 #include "compiler.h"       /* UNLIKELY() */
 #include "debug.h"          /* assert(), DEBUGLOG(), RAWLOG() */
 #include "error_private.h"  /* error codes and messages */
+#include "bits.h"           /* ZSTD_highbit32 */
 
 
 /*=========================================
 *  Target specific
 =========================================*/
 #ifndef ZSTD_NO_INTRINSICS
-#  if defined(__BMI__) && defined(__GNUC__)
-#    include <immintrin.h>   /* support for bextr (experimental) */
+#  if (defined(__BMI__) || defined(__BMI2__)) && defined(__GNUC__)
+#    include <immintrin.h>   /* support for bextr (experimental)/bzhi */
 #  elif defined(__ICCARM__)
 #    include <intrinsics.h>
 #  endif
@@ -134,42 +135,6 @@
 MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
 /* faster, but works only if nbBits >= 1 */
 
-
-
-/*-**************************************************************
-*  Internal functions
-****************************************************************/
-MEM_STATIC unsigned BIT_highbit32 (U32 val)
-{
-    assert(val != 0);
-    {
-#   if defined(_MSC_VER)   /* Visual */
-#       if STATIC_BMI2 == 1
-		return _lzcnt_u32(val) ^ 31;
-#       else
-		unsigned long r = 0;
-		return _BitScanReverse(&r, val) ? (unsigned)r : 0;
-#       endif
-#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* Use GCC Intrinsic */
-        return __builtin_clz (val) ^ 31;
-#   elif defined(__ICCARM__)    /* IAR Intrinsic */
-        return 31 - __CLZ(val);
-#   else   /* Software version */
-        static const unsigned DeBruijnClz[32] = { 0,  9,  1, 10, 13, 21,  2, 29,
-                                                 11, 14, 16, 18, 22, 25,  3, 30,
-                                                  8, 12, 20, 28, 15, 17, 24,  7,
-                                                 19, 27, 23,  6, 26,  5,  4, 31 };
-        U32 v = val;
-        v |= v >> 1;
-        v |= v >> 2;
-        v |= v >> 4;
-        v |= v >> 8;
-        v |= v >> 16;
-        return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27];
-#   endif
-    }
-}
-
 /*=====    Local Constants   =====*/
 static const unsigned BIT_mask[] = {
     0,          1,         3,         7,         0xF,       0x1F,
@@ -199,6 +164,16 @@
     return 0;
 }
 
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
+{
+#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 && !defined(ZSTD_NO_INTRINSICS)
+    return  _bzhi_u64(bitContainer, nbBits);
+#else
+    assert(nbBits < BIT_MASK_SIZE);
+    return bitContainer & BIT_mask[nbBits];
+#endif
+}
+
 /*! BIT_addBits() :
  *  can add up to 31 bits into `bitC`.
  *  Note : does not check for register overflow ! */
@@ -208,7 +183,7 @@
     DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32);
     assert(nbBits < BIT_MASK_SIZE);
     assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
-    bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
+    bitC->bitContainer |= BIT_getLowerBits(value, nbBits) << bitC->bitPos;
     bitC->bitPos += nbBits;
 }
 
@@ -287,7 +262,7 @@
         bitD->ptr   = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer);
         bitD->bitContainer = MEM_readLEST(bitD->ptr);
         { BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
-          bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;  /* ensures bitsConsumed is always set */
+          bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;  /* ensures bitsConsumed is always set */
           if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
     } else {
         bitD->ptr   = bitD->start;
@@ -295,27 +270,27 @@
         switch(srcSize)
         {
         case 7: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[6]) << (sizeof(bitD->bitContainer)*8 - 16);
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         case 6: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[5]) << (sizeof(bitD->bitContainer)*8 - 24);
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         case 5: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[4]) << (sizeof(bitD->bitContainer)*8 - 32);
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         case 4: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[3]) << 24;
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         case 3: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[2]) << 16;
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         case 2: bitD->bitContainer += (size_t)(((const BYTE*)(srcBuffer))[1]) <<  8;
-                /* fall-through */
+                ZSTD_FALLTHROUGH;
 
         default: break;
         }
         {   BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
-            bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
+            bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;
             if (lastByte == 0) return ERROR(corruption_detected);  /* endMark not present */
         }
         bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
@@ -334,16 +309,15 @@
     U32 const regMask = sizeof(bitContainer)*8 - 1;
     /* if start > regMask, bitstream is corrupted, and result is undefined */
     assert(nbBits < BIT_MASK_SIZE);
-    return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
-}
-
-MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
-{
-#if defined(STATIC_BMI2) && STATIC_BMI2 == 1
-	return  _bzhi_u64(bitContainer, nbBits);
+    /* x86 transform & ((1 << nbBits) - 1) to bzhi instruction, it is better
+     * than accessing memory. When bmi2 instruction is not present, we consider
+     * such cpus old (pre-Haswell, 2013) and their performance is not of that
+     * importance.
+     */
+#if defined(__x86_64__) || defined(_M_X86)
+    return (bitContainer >> (start & regMask)) & ((((U64)1) << nbBits) - 1);
 #else
-    assert(nbBits < BIT_MASK_SIZE);
-    return bitContainer & BIT_mask[nbBits];
+    return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
 #endif
 }
 
@@ -393,7 +367,7 @@
 }
 
 /*! BIT_readBitsFast() :
- *  unsafe version; only works only if nbBits >= 1 */
+ *  unsafe version; only works if nbBits >= 1 */
 MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits)
 {
     size_t const value = BIT_lookBitsFast(bitD, nbBits);
@@ -424,7 +398,7 @@
  *  This function is safe, it guarantees it will not read beyond src buffer.
  * @return : status of `BIT_DStream_t` internal register.
  *           when status == BIT_DStream_unfinished, internal register is filled with at least 25 or 57 bits */
-MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
+MEM_STATIC FORCE_INLINE_ATTR BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD)
 {
     if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8))  /* overflow detected, like end of stream */
         return BIT_DStream_overflow;
diff --git a/Utilities/cmzstd/lib/common/compiler.h b/Utilities/cmzstd/lib/common/compiler.h
index f717855..d07e189 100644
--- a/Utilities/cmzstd/lib/common/compiler.h
+++ b/Utilities/cmzstd/lib/common/compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,6 +11,8 @@
 #ifndef ZSTD_COMPILER_H
 #define ZSTD_COMPILER_H
 
+#include "portability_macros.h"
+
 /*-*******************************************************
 *  Compiler specifics
 *********************************************************/
@@ -40,7 +42,7 @@
 
 /**
   On MSVC qsort requires that functions passed into it use the __cdecl calling conversion(CC).
-  This explictly marks such functions as __cdecl so that the code will still compile
+  This explicitly marks such functions as __cdecl so that the code will still compile
   if a CC other than __cdecl has been made the default.
 */
 #if  defined(_MSC_VER)
@@ -92,29 +94,17 @@
 
 
 /* target attribute */
-#ifndef __has_attribute
-  #define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
-#endif
 #if defined(__GNUC__) || defined(__ICCARM__)
 #  define TARGET_ATTRIBUTE(target) __attribute__((__target__(target)))
 #else
 #  define TARGET_ATTRIBUTE(target)
 #endif
 
-/* Enable runtime BMI2 dispatch based on the CPU.
- * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
+/* Target attribute for BMI2 dynamic dispatch.
+ * Enable lzcnt, bmi, and bmi2.
+ * We test for bmi1 & bmi2. lzcnt is included in bmi1.
  */
-#ifndef DYNAMIC_BMI2
-  #if ((defined(__clang__) && __has_attribute(__target__)) \
-      || (defined(__GNUC__) \
-          && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \
-      && (defined(__x86_64__) || defined(_M_X86)) \
-      && !defined(__BMI2__)
-  #  define DYNAMIC_BMI2 1
-  #else
-  #  define DYNAMIC_BMI2 0
-  #endif
-#endif
+#define BMI2_TARGET_ATTRIBUTE TARGET_ATTRIBUTE("lzcnt,bmi,bmi2")
 
 /* prefetch
  * can be disabled, by declaring NO_PREFETCH build macro */
@@ -150,8 +140,9 @@
 }
 
 /* vectorization
- * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax */
-#if !defined(__INTEL_COMPILER) && !defined(__clang__) && !defined(__LCC__) && defined(__GNUC__)
+ * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax,
+ * and some compilers, like Intel ICC and MCST LCC, do not support it at all. */
+#if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__) && !defined(__LCC__)
 #  if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5)
 #    define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize")))
 #  else
@@ -174,6 +165,12 @@
 #define UNLIKELY(x) (x)
 #endif
 
+#if __has_builtin(__builtin_unreachable) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)))
+#  define ZSTD_UNREACHABLE { assert(0), __builtin_unreachable(); }
+#else
+#  define ZSTD_UNREACHABLE { assert(0); }
+#endif
+
 /* disable warnings */
 #ifdef _MSC_VER    /* Visual Studio */
 #  include <intrin.h>                    /* For Visual 2005 */
@@ -190,6 +187,8 @@
 #    ifdef __AVX2__  //MSVC does not have a BMI2 specific flag, but every CPU that supports AVX2 also supports BMI2
 #       define STATIC_BMI2 1
 #    endif
+#  elif defined(__BMI2__) && defined(__x86_64__) && defined(__GNUC__)
+#    define STATIC_BMI2 1
 #  endif
 #endif
 
@@ -197,26 +196,107 @@
     #define STATIC_BMI2 0
 #endif
 
-/* compat. with non-clang compilers */
-#ifndef __has_builtin
-#  define __has_builtin(x) 0
-#endif
-
-/* compat. with non-clang compilers */
-#ifndef __has_feature
-#  define __has_feature(x) 0
-#endif
-
-/* detects whether we are being compiled under msan */
-#ifndef ZSTD_MEMORY_SANITIZER
-#  if __has_feature(memory_sanitizer)
-#    define ZSTD_MEMORY_SANITIZER 1
-#  else
-#    define ZSTD_MEMORY_SANITIZER 0
+/* compile time determination of SIMD support */
+#if !defined(ZSTD_NO_INTRINSICS)
+#  if defined(__SSE2__) || defined(_M_AMD64) || (defined (_M_IX86) && defined(_M_IX86_FP) && (_M_IX86_FP >= 2))
+#    define ZSTD_ARCH_X86_SSE2
+#  endif
+#  if defined(__ARM_NEON) || defined(_M_ARM64)
+#    define ZSTD_ARCH_ARM_NEON
+#  endif
+#
+#  if defined(ZSTD_ARCH_X86_SSE2)
+#    include <emmintrin.h>
+#  elif defined(ZSTD_ARCH_ARM_NEON)
+#    include <arm_neon.h>
 #  endif
 #endif
 
-#if ZSTD_MEMORY_SANITIZER
+/* C-language Attributes are added in C23. */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute)
+# define ZSTD_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)
+#else
+# define ZSTD_HAS_C_ATTRIBUTE(x) 0
+#endif
+
+/* Only use C++ attributes in C++. Some compilers report support for C++
+ * attributes when compiling with C.
+ */
+#if defined(__cplusplus) && defined(__has_cpp_attribute)
+# define ZSTD_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+# define ZSTD_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+/* Define ZSTD_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute.
+ * - C23: https://en.cppreference.com/w/c/language/attributes/fallthrough
+ * - CPP17: https://en.cppreference.com/w/cpp/language/attributes/fallthrough
+ * - Else: __attribute__((__fallthrough__))
+ */
+#ifndef ZSTD_FALLTHROUGH
+# if ZSTD_HAS_C_ATTRIBUTE(fallthrough)
+#  define ZSTD_FALLTHROUGH [[fallthrough]]
+# elif ZSTD_HAS_CPP_ATTRIBUTE(fallthrough)
+#  define ZSTD_FALLTHROUGH [[fallthrough]]
+# elif __has_attribute(__fallthrough__)
+/* Leading semicolon is to satisfy gcc-11 with -pedantic. Without the semicolon
+ * gcc complains about: a label can only be part of a statement and a declaration is not a statement.
+ */
+#  define ZSTD_FALLTHROUGH ; __attribute__((__fallthrough__))
+# else
+#  define ZSTD_FALLTHROUGH
+# endif
+#endif
+
+/*-**************************************************************
+*  Alignment check
+*****************************************************************/
+
+/* this test was initially positioned in mem.h,
+ * but this file is removed (or replaced) for linux kernel
+ * so it's now hosted in compiler.h,
+ * which remains valid for both user & kernel spaces.
+ */
+
+#ifndef ZSTD_ALIGNOF
+# if defined(__GNUC__) || defined(_MSC_VER)
+/* covers gcc, clang & MSVC */
+/* note : this section must come first, before C11,
+ * due to a limitation in the kernel source generator */
+#  define ZSTD_ALIGNOF(T) __alignof(T)
+
+# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+/* Oracle Studio */
+#  define ZSTD_ALIGNOF(T) _Alignof(T)
+
+# elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
+/* C11 support */
+#  include <stdalign.h>
+#  define ZSTD_ALIGNOF(T) alignof(T)
+
+# else
+/* No known support for alignof() - imperfect backup */
+#  define ZSTD_ALIGNOF(T) (sizeof(void*) < sizeof(T) ? sizeof(void*) : sizeof(T))
+
+# endif
+#endif /* ZSTD_ALIGNOF */
+
+/*-**************************************************************
+*  Sanitizer
+*****************************************************************/
+
+/* Issue #3240 reports an ASAN failure on an llvm-mingw build. Out of an
+ * abundance of caution, disable our custom poisoning on mingw. */
+#ifdef __MINGW32__
+#ifndef ZSTD_ASAN_DONT_POISON_WORKSPACE
+#define ZSTD_ASAN_DONT_POISON_WORKSPACE 1
+#endif
+#ifndef ZSTD_MSAN_DONT_POISON_WORKSPACE
+#define ZSTD_MSAN_DONT_POISON_WORKSPACE 1
+#endif
+#endif
+
+#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE)
 /* Not all platforms that support msan provide sanitizers/msan_interface.h.
  * We therefore declare the functions we need ourselves, rather than trying to
  * include the header file... */
@@ -235,20 +315,13 @@
 /* Returns the offset of the first (at least partially) poisoned byte in the
    memory range, or -1 if the whole range is good. */
 intptr_t __msan_test_shadow(const volatile void *x, size_t size);
+
+/* Print shadow and origin for the memory range to stderr in a human-readable
+   format. */
+void __msan_print_shadow(const volatile void *x, size_t size);
 #endif
 
-/* detects whether we are being compiled under asan */
-#ifndef ZSTD_ADDRESS_SANITIZER
-#  if __has_feature(address_sanitizer)
-#    define ZSTD_ADDRESS_SANITIZER 1
-#  elif defined(__SANITIZE_ADDRESS__)
-#    define ZSTD_ADDRESS_SANITIZER 1
-#  else
-#    define ZSTD_ADDRESS_SANITIZER 0
-#  endif
-#endif
-
-#if ZSTD_ADDRESS_SANITIZER
+#if ZSTD_ADDRESS_SANITIZER && !defined(ZSTD_ASAN_DONT_POISON_WORKSPACE)
 /* Not all platforms that support asan provide sanitizers/asan_interface.h.
  * We therefore declare the functions we need ourselves, rather than trying to
  * include the header file... */
diff --git a/Utilities/cmzstd/lib/common/cpu.h b/Utilities/cmzstd/lib/common/cpu.h
index 8acd33b..8bc34a3 100644
--- a/Utilities/cmzstd/lib/common/cpu.h
+++ b/Utilities/cmzstd/lib/common/cpu.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/common/debug.c b/Utilities/cmzstd/lib/common/debug.c
index bb863c9..ebf7bfc 100644
--- a/Utilities/cmzstd/lib/common/debug.c
+++ b/Utilities/cmzstd/lib/common/debug.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * debug
  * Part of FSE library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/common/debug.h b/Utilities/cmzstd/lib/common/debug.h
index 3b2a320..0e9817e 100644
--- a/Utilities/cmzstd/lib/common/debug.h
+++ b/Utilities/cmzstd/lib/common/debug.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * debug
  * Part of FSE library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/common/entropy_common.c b/Utilities/cmzstd/lib/common/entropy_common.c
index 41cd695..e2173af 100644
--- a/Utilities/cmzstd/lib/common/entropy_common.c
+++ b/Utilities/cmzstd/lib/common/entropy_common.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * Common functions of New Generation Entropy library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -19,8 +19,8 @@
 #include "error_private.h"       /* ERR_*, ERROR */
 #define FSE_STATIC_LINKING_ONLY  /* FSE_MIN_TABLELOG */
 #include "fse.h"
-#define HUF_STATIC_LINKING_ONLY  /* HUF_TABLELOG_ABSOLUTEMAX */
 #include "huf.h"
+#include "bits.h"                /* ZSDT_highbit32, ZSTD_countTrailingZeros32 */
 
 
 /*===   Version   ===*/
@@ -38,28 +38,6 @@
 /*-**************************************************************
 *  FSE NCount encoding-decoding
 ****************************************************************/
-static U32 FSE_ctz(U32 val)
-{
-    assert(val != 0);
-    {
-#   if defined(_MSC_VER)   /* Visual */
-        unsigned long r=0;
-        return _BitScanForward(&r, val) ? (unsigned)r : 0;
-#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* GCC Intrinsic */
-        return __builtin_ctz(val);
-#   elif defined(__ICCARM__)    /* IAR Intrinsic */
-        return __CTZ(val);
-#   else   /* Software version */
-        U32 count = 0;
-        while ((val & 1) == 0) {
-            val >>= 1;
-            ++count;
-        }
-        return count;
-#   endif
-    }
-}
-
 FORCE_INLINE_TEMPLATE
 size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
                            const void* headerBuffer, size_t hbSize)
@@ -107,7 +85,7 @@
              * repeat.
              * Avoid UB by setting the high bit to 1.
              */
-            int repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
+            int repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1;
             while (repeats >= 12) {
                 charnum += 3 * 12;
                 if (LIKELY(ip <= iend-7)) {
@@ -118,7 +96,7 @@
                     ip = iend - 4;
                 }
                 bitStream = MEM_readLE32(ip) >> bitCount;
-                repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
+                repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1;
             }
             charnum += 3 * repeats;
             bitStream >>= 2 * repeats;
@@ -183,7 +161,7 @@
                  * know that threshold > 1.
                  */
                 if (remaining <= 1) break;
-                nbBits = BIT_highbit32(remaining) + 1;
+                nbBits = ZSTD_highbit32(remaining) + 1;
                 threshold = 1 << (nbBits - 1);
             }
             if (charnum >= maxSV1) break;
@@ -217,7 +195,7 @@
 }
 
 #if DYNAMIC_BMI2
-TARGET_ATTRIBUTE("bmi2") static size_t FSE_readNCount_body_bmi2(
+BMI2_TARGET_ATTRIBUTE static size_t FSE_readNCount_body_bmi2(
         short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
         const void* headerBuffer, size_t hbSize)
 {
@@ -258,7 +236,7 @@
                      const void* src, size_t srcSize)
 {
     U32 wksp[HUF_READ_STATS_WORKSPACE_SIZE_U32];
-    return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* bmi2 */ 0);
+    return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* flags */ 0);
 }
 
 FORCE_INLINE_TEMPLATE size_t
@@ -299,21 +277,21 @@
     ZSTD_memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
     weightTotal = 0;
     {   U32 n; for (n=0; n<oSize; n++) {
-            if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected);
+            if (huffWeight[n] > HUF_TABLELOG_MAX) return ERROR(corruption_detected);
             rankStats[huffWeight[n]]++;
             weightTotal += (1 << huffWeight[n]) >> 1;
     }   }
     if (weightTotal == 0) return ERROR(corruption_detected);
 
     /* get last non-null symbol weight (implied, total must be 2^n) */
-    {   U32 const tableLog = BIT_highbit32(weightTotal) + 1;
+    {   U32 const tableLog = ZSTD_highbit32(weightTotal) + 1;
         if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected);
         *tableLogPtr = tableLog;
         /* determine last weight */
         {   U32 const total = 1 << tableLog;
             U32 const rest = total - weightTotal;
-            U32 const verif = 1 << BIT_highbit32(rest);
-            U32 const lastWeight = BIT_highbit32(rest) + 1;
+            U32 const verif = 1 << ZSTD_highbit32(rest);
+            U32 const lastWeight = ZSTD_highbit32(rest) + 1;
             if (verif != rest) return ERROR(corruption_detected);    /* last value must be a clean power of 2 */
             huffWeight[oSize] = (BYTE)lastWeight;
             rankStats[lastWeight]++;
@@ -337,7 +315,7 @@
 }
 
 #if DYNAMIC_BMI2
-static TARGET_ATTRIBUTE("bmi2") size_t HUF_readStats_body_bmi2(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+static BMI2_TARGET_ATTRIBUTE size_t HUF_readStats_body_bmi2(BYTE* huffWeight, size_t hwSize, U32* rankStats,
                      U32* nbSymbolsPtr, U32* tableLogPtr,
                      const void* src, size_t srcSize,
                      void* workSpace, size_t wkspSize)
@@ -350,13 +328,13 @@
                      U32* nbSymbolsPtr, U32* tableLogPtr,
                      const void* src, size_t srcSize,
                      void* workSpace, size_t wkspSize,
-                     int bmi2)
+                     int flags)
 {
 #if DYNAMIC_BMI2
-    if (bmi2) {
+    if (flags & HUF_flags_bmi2) {
         return HUF_readStats_body_bmi2(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize);
     }
 #endif
-    (void)bmi2;
+    (void)flags;
     return HUF_readStats_body_default(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize);
 }
diff --git a/Utilities/cmzstd/lib/common/error_private.c b/Utilities/cmzstd/lib/common/error_private.c
index 6d1135f..075fc5e 100644
--- a/Utilities/cmzstd/lib/common/error_private.c
+++ b/Utilities/cmzstd/lib/common/error_private.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -27,9 +27,11 @@
     case PREFIX(version_unsupported): return "Version not supported";
     case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
     case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
-    case PREFIX(corruption_detected): return "Corrupted block detected";
+    case PREFIX(corruption_detected): return "Data corruption detected";
     case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
+    case PREFIX(literals_headerWrong): return "Header of Literals' block doesn't respect format specification";
     case PREFIX(parameter_unsupported): return "Unsupported parameter";
+    case PREFIX(parameter_combination_unsupported): return "Unsupported combination of parameters";
     case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
     case PREFIX(init_missing): return "Context should be init first";
     case PREFIX(memory_allocation): return "Allocation error : not enough memory";
@@ -38,17 +40,22 @@
     case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
     case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
     case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
+    case PREFIX(stabilityCondition_notRespected): return "pledged buffer stability condition is not respected";
     case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
     case PREFIX(dictionary_wrong): return "Dictionary mismatch";
     case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples";
     case PREFIX(dstSize_tooSmall): return "Destination buffer is too small";
     case PREFIX(srcSize_wrong): return "Src size is incorrect";
     case PREFIX(dstBuffer_null): return "Operation on NULL destination buffer";
+    case PREFIX(noForwardProgress_destFull): return "Operation made no progress over multiple calls, due to output buffer being full";
+    case PREFIX(noForwardProgress_inputEmpty): return "Operation made no progress over multiple calls, due to input being empty";
         /* following error codes are not stable and may be removed or changed in a future version */
     case PREFIX(frameIndex_tooLarge): return "Frame index is too large";
     case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking";
     case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong";
     case PREFIX(srcBuffer_wrong): return "Source buffer is wrong";
+    case PREFIX(sequenceProducer_failed): return "Block-level external sequence producer returned an error code";
+    case PREFIX(externalSequences_invalid): return "External sequences are not valid";
     case PREFIX(maxCode):
     default: return notErrorCode;
     }
diff --git a/Utilities/cmzstd/lib/common/error_private.h b/Utilities/cmzstd/lib/common/error_private.h
index 6d8b9f7..325daad 100644
--- a/Utilities/cmzstd/lib/common/error_private.h
+++ b/Utilities/cmzstd/lib/common/error_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -22,6 +22,8 @@
 *  Dependencies
 ******************************************/
 #include "../zstd_errors.h"  /* enum list */
+#include "compiler.h"
+#include "debug.h"
 #include "zstd_deps.h"       /* size_t */
 
 
@@ -73,6 +75,83 @@
     return ERR_getErrorString(ERR_getErrorCode(code));
 }
 
+/**
+ * Ignore: this is an internal helper.
+ *
+ * This is a helper function to help force C99-correctness during compilation.
+ * Under strict compilation modes, variadic macro arguments can't be empty.
+ * However, variadic function arguments can be. Using a function therefore lets
+ * us statically check that at least one (string) argument was passed,
+ * independent of the compilation flags.
+ */
+static INLINE_KEYWORD UNUSED_ATTR
+void _force_has_format_string(const char *format, ...) {
+  (void)format;
+}
+
+/**
+ * Ignore: this is an internal helper.
+ *
+ * We want to force this function invocation to be syntactically correct, but
+ * we don't want to force runtime evaluation of its arguments.
+ */
+#define _FORCE_HAS_FORMAT_STRING(...) \
+  if (0) { \
+    _force_has_format_string(__VA_ARGS__); \
+  }
+
+#define ERR_QUOTE(str) #str
+
+/**
+ * Return the specified error if the condition evaluates to true.
+ *
+ * In debug modes, prints additional information.
+ * In order to do that (particularly, printing the conditional that failed),
+ * this can't just wrap RETURN_ERROR().
+ */
+#define RETURN_ERROR_IF(cond, err, ...) \
+  if (cond) { \
+    RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \
+           __FILE__, __LINE__, ERR_QUOTE(cond), ERR_QUOTE(ERROR(err))); \
+    _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
+    RAWLOG(3, ": " __VA_ARGS__); \
+    RAWLOG(3, "\n"); \
+    return ERROR(err); \
+  }
+
+/**
+ * Unconditionally return the specified error.
+ *
+ * In debug modes, prints additional information.
+ */
+#define RETURN_ERROR(err, ...) \
+  do { \
+    RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \
+           __FILE__, __LINE__, ERR_QUOTE(ERROR(err))); \
+    _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
+    RAWLOG(3, ": " __VA_ARGS__); \
+    RAWLOG(3, "\n"); \
+    return ERROR(err); \
+  } while(0);
+
+/**
+ * If the provided expression evaluates to an error code, returns that error code.
+ *
+ * In debug modes, prints additional information.
+ */
+#define FORWARD_IF_ERROR(err, ...) \
+  do { \
+    size_t const err_code = (err); \
+    if (ERR_isError(err_code)) { \
+      RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \
+             __FILE__, __LINE__, ERR_QUOTE(err), ERR_getErrorName(err_code)); \
+      _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
+      RAWLOG(3, ": " __VA_ARGS__); \
+      RAWLOG(3, "\n"); \
+      return err_code; \
+    } \
+  } while(0);
+
 #if defined (__cplusplus)
 }
 #endif
diff --git a/Utilities/cmzstd/lib/common/fse.h b/Utilities/cmzstd/lib/common/fse.h
index 19dd4fe..02a1f0b 100644
--- a/Utilities/cmzstd/lib/common/fse.h
+++ b/Utilities/cmzstd/lib/common/fse.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * FSE : Finite State Entropy codec
  * Public Prototypes declaration
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -53,34 +53,6 @@
 FSE_PUBLIC_API unsigned FSE_versionNumber(void);   /**< library version number; to be used when checking dll version */
 
 
-/*-****************************************
-*  FSE simple functions
-******************************************/
-/*! FSE_compress() :
-    Compress content of buffer 'src', of size 'srcSize', into destination buffer 'dst'.
-    'dst' buffer must be already allocated. Compression runs faster is dstCapacity >= FSE_compressBound(srcSize).
-    @return : size of compressed data (<= dstCapacity).
-    Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
-                     if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression instead.
-                     if FSE_isError(return), compression failed (more details using FSE_getErrorName())
-*/
-FSE_PUBLIC_API size_t FSE_compress(void* dst, size_t dstCapacity,
-                             const void* src, size_t srcSize);
-
-/*! FSE_decompress():
-    Decompress FSE data from buffer 'cSrc', of size 'cSrcSize',
-    into already allocated destination buffer 'dst', of size 'dstCapacity'.
-    @return : size of regenerated data (<= maxDstSize),
-              or an error code, which can be tested using FSE_isError() .
-
-    ** Important ** : FSE_decompress() does not decompress non-compressible nor RLE data !!!
-    Why ? : making this distinction requires a header.
-    Header management is intentionally delegated to the user layer, which can better manage special cases.
-*/
-FSE_PUBLIC_API size_t FSE_decompress(void* dst,  size_t dstCapacity,
-                               const void* cSrc, size_t cSrcSize);
-
-
 /*-*****************************************
 *  Tool functions
 ******************************************/
@@ -92,20 +64,6 @@
 
 
 /*-*****************************************
-*  FSE advanced functions
-******************************************/
-/*! FSE_compress2() :
-    Same as FSE_compress(), but allows the selection of 'maxSymbolValue' and 'tableLog'
-    Both parameters can be defined as '0' to mean : use default value
-    @return : size of compressed data
-    Special values : if return == 0, srcData is not compressible => Nothing is stored within cSrc !!!
-                     if return == 1, srcData is a single byte symbol * srcSize times. Use RLE compression.
-                     if FSE_isError(return), it's an error code.
-*/
-FSE_PUBLIC_API size_t FSE_compress2 (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
-
-
-/*-*****************************************
 *  FSE detailed API
 ******************************************/
 /*!
@@ -164,8 +122,6 @@
 /*! Constructor and Destructor of FSE_CTable.
     Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */
 typedef unsigned FSE_CTable;   /* don't allocate that. It's only meant to be more restrictive than void* */
-FSE_PUBLIC_API FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog);
-FSE_PUBLIC_API void        FSE_freeCTable (FSE_CTable* ct);
 
 /*! FSE_buildCTable():
     Builds `ct`, which must be already allocated, using FSE_createCTable().
@@ -241,23 +197,7 @@
                            unsigned* maxSymbolValuePtr, unsigned* tableLogPtr,
                            const void* rBuffer, size_t rBuffSize, int bmi2);
 
-/*! Constructor and Destructor of FSE_DTable.
-    Note that its size depends on 'tableLog' */
 typedef unsigned FSE_DTable;   /* don't allocate that. It's just a way to be more restrictive than void* */
-FSE_PUBLIC_API FSE_DTable* FSE_createDTable(unsigned tableLog);
-FSE_PUBLIC_API void        FSE_freeDTable(FSE_DTable* dt);
-
-/*! FSE_buildDTable():
-    Builds 'dt', which must be already allocated, using FSE_createDTable().
-    return : 0, or an errorCode, which can be tested using FSE_isError() */
-FSE_PUBLIC_API size_t FSE_buildDTable (FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog);
-
-/*! FSE_decompress_usingDTable():
-    Decompress compressed source `cSrc` of size `cSrcSize` using `dt`
-    into `dst` which must be already allocated.
-    @return : size of regenerated data (necessarily <= `dstCapacity`),
-              or an errorCode, which can be tested using FSE_isError() */
-FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, const FSE_DTable* dt);
 
 /*!
 Tutorial :
@@ -320,24 +260,15 @@
 unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus);
 /**< same as FSE_optimalTableLog(), which used `minus==2` */
 
-/* FSE_compress_wksp() :
- * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
- * FSE_COMPRESS_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable.
- */
-#define FSE_COMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue)   ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) )
-size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
-
-size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits);
-/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */
-
 size_t FSE_buildCTable_rle (FSE_CTable* ct, unsigned char symbolValue);
 /**< build a fake FSE_CTable, designed to compress always the same symbolValue */
 
 /* FSE_buildCTable_wksp() :
  * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
  * `wkspSize` must be >= `FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog)` of `unsigned`.
+ * See FSE_buildCTable_wksp() for breakdown of workspace usage.
  */
-#define FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog) (maxSymbolValue + 2 + (1ull << (tableLog - 2)))
+#define FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog) (((maxSymbolValue + 2) + (1ull << (tableLog)))/2 + sizeof(U64)/sizeof(U32) /* additional 8 bytes for potential table overwrite */)
 #define FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) (sizeof(unsigned) * FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog))
 size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
 
@@ -346,19 +277,11 @@
 FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
 /**< Same as FSE_buildDTable(), using an externally allocated `workspace` produced with `FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxSymbolValue)` */
 
-size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits);
-/**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */
-
-size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
-/**< build a fake FSE_DTable, designed to always generate the same symbolValue */
-
-#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1)
+#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + 1 + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1)
 #define FSE_DECOMPRESS_WKSP_SIZE(maxTableLog, maxSymbolValue) (FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(unsigned))
-size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize);
-/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)` */
-
 size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize, int bmi2);
-/**< Same as FSE_decompress_wksp() but with dynamic BMI2 support. Pass 1 if your CPU supports BMI2 or 0 if it doesn't. */
+/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)`.
+ * Set bmi2 to 1 if your CPU supports BMI2 or 0 if it doesn't */
 
 typedef enum {
    FSE_repeat_none,  /**< Cannot use the previous table */
@@ -554,7 +477,7 @@
 
 /* FSE_getMaxNbBits() :
  * Approximate maximum cost of a symbol, in bits.
- * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
+ * Fractional get rounded up (i.e. a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
  * note 1 : assume symbolValue is valid (<= maxSymbolValue)
  * note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
 MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue)
diff --git a/Utilities/cmzstd/lib/common/fse_decompress.c b/Utilities/cmzstd/lib/common/fse_decompress.c
index f4ff58f..1e1c9f9 100644
--- a/Utilities/cmzstd/lib/common/fse_decompress.c
+++ b/Utilities/cmzstd/lib/common/fse_decompress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * FSE : Finite State Entropy decoder
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -24,6 +24,7 @@
 #include "error_private.h"
 #define ZSTD_DEPS_NEED_MALLOC
 #include "zstd_deps.h"
+#include "bits.h"       /* ZSTD_highbit32 */
 
 
 /* **************************************************************
@@ -55,19 +56,6 @@
 #define FSE_FUNCTION_NAME(X,Y) FSE_CAT(X,Y)
 #define FSE_TYPE_NAME(X,Y) FSE_CAT(X,Y)
 
-
-/* Function templates */
-FSE_DTable* FSE_createDTable (unsigned tableLog)
-{
-    if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
-    return (FSE_DTable*)ZSTD_malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) );
-}
-
-void FSE_freeDTable (FSE_DTable* dt)
-{
-    ZSTD_free(dt);
-}
-
 static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize)
 {
     void* const tdPtr = dt+1;   /* because *dt is unsigned, 32-bits aligned on 32-bits */
@@ -127,10 +115,10 @@
             }
         }
         /* Now we spread those positions across the table.
-         * The benefit of doing it in two stages is that we avoid the the
+         * The benefit of doing it in two stages is that we avoid the
          * variable size inner loop, which caused lots of branch misses.
          * Now we can run through all the positions without any branch misses.
-         * We unroll the loop twice, since that is what emperically worked best.
+         * We unroll the loop twice, since that is what empirically worked best.
          */
         {
             size_t position = 0;
@@ -166,7 +154,7 @@
         for (u=0; u<tableSize; u++) {
             FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol);
             U32 const nextState = symbolNext[symbol]++;
-            tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
+            tableDecode[u].nbBits = (BYTE) (tableLog - ZSTD_highbit32(nextState) );
             tableDecode[u].newState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
     }   }
 
@@ -184,49 +172,6 @@
 /*-*******************************************************
 *  Decompression (Byte symbols)
 *********************************************************/
-size_t FSE_buildDTable_rle (FSE_DTable* dt, BYTE symbolValue)
-{
-    void* ptr = dt;
-    FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr;
-    void* dPtr = dt + 1;
-    FSE_decode_t* const cell = (FSE_decode_t*)dPtr;
-
-    DTableH->tableLog = 0;
-    DTableH->fastMode = 0;
-
-    cell->newState = 0;
-    cell->symbol = symbolValue;
-    cell->nbBits = 0;
-
-    return 0;
-}
-
-
-size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits)
-{
-    void* ptr = dt;
-    FSE_DTableHeader* const DTableH = (FSE_DTableHeader*)ptr;
-    void* dPtr = dt + 1;
-    FSE_decode_t* const dinfo = (FSE_decode_t*)dPtr;
-    const unsigned tableSize = 1 << nbBits;
-    const unsigned tableMask = tableSize - 1;
-    const unsigned maxSV1 = tableMask+1;
-    unsigned s;
-
-    /* Sanity checks */
-    if (nbBits < 1) return ERROR(GENERIC);         /* min size */
-
-    /* Build Decoding Table */
-    DTableH->tableLog = (U16)nbBits;
-    DTableH->fastMode = 1;
-    for (s=0; s<maxSV1; s++) {
-        dinfo[s].newState = 0;
-        dinfo[s].symbol = (BYTE)s;
-        dinfo[s].nbBits = (BYTE)nbBits;
-    }
-
-    return 0;
-}
 
 FORCE_INLINE_TEMPLATE size_t FSE_decompress_usingDTable_generic(
           void* dst, size_t maxDstSize,
@@ -290,26 +235,6 @@
     return op-ostart;
 }
 
-
-size_t FSE_decompress_usingDTable(void* dst, size_t originalSize,
-                            const void* cSrc, size_t cSrcSize,
-                            const FSE_DTable* dt)
-{
-    const void* ptr = dt;
-    const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr;
-    const U32 fastMode = DTableH->fastMode;
-
-    /* select fast mode (static) */
-    if (fastMode) return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1);
-    return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0);
-}
-
-
-size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
-{
-    return FSE_decompress_wksp_bmi2(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, /* bmi2 */ 0);
-}
-
 typedef struct {
     short ncount[FSE_MAX_SYMBOL_VALUE + 1];
     FSE_DTable dtable[1]; /* Dynamically sized */
@@ -342,7 +267,8 @@
     }
 
     if (FSE_DECOMPRESS_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(tableLog_tooLarge);
-    workSpace = wksp->dtable + FSE_DTABLE_SIZE_U32(tableLog);
+    assert(sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog) <= wkspSize);
+    workSpace = (BYTE*)workSpace + sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog);
     wkspSize -= sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog);
 
     CHECK_F( FSE_buildDTable_internal(wksp->dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize) );
@@ -365,7 +291,7 @@
 }
 
 #if DYNAMIC_BMI2
-TARGET_ATTRIBUTE("bmi2") static size_t FSE_decompress_wksp_body_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
+BMI2_TARGET_ATTRIBUTE static size_t FSE_decompress_wksp_body_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
 {
     return FSE_decompress_wksp_body(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, 1);
 }
@@ -382,22 +308,4 @@
     return FSE_decompress_wksp_body_default(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize);
 }
 
-
-typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)];
-
-#ifndef ZSTD_NO_UNUSED_FUNCTIONS
-size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) {
-    U32 wksp[FSE_BUILD_DTABLE_WKSP_SIZE_U32(FSE_TABLELOG_ABSOLUTE_MAX, FSE_MAX_SYMBOL_VALUE)];
-    return FSE_buildDTable_wksp(dt, normalizedCounter, maxSymbolValue, tableLog, wksp, sizeof(wksp));
-}
-
-size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize)
-{
-    /* Static analyzer seems unable to understand this table will be properly initialized later */
-    U32 wksp[FSE_DECOMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)];
-    return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, FSE_MAX_TABLELOG, wksp, sizeof(wksp));
-}
-#endif
-
-
 #endif   /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/common/huf.h b/Utilities/cmzstd/lib/common/huf.h
index 3d47ced..73d1ee5 100644
--- a/Utilities/cmzstd/lib/common/huf.h
+++ b/Utilities/cmzstd/lib/common/huf.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * huff0 huffman codec,
  * part of Finite State Entropy library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -21,106 +21,29 @@
 
 /* *** Dependencies *** */
 #include "zstd_deps.h"    /* size_t */
-
-
-/* *** library symbols visibility *** */
-/* Note : when linking with -fvisibility=hidden on gcc, or by default on Visual,
- *        HUF symbols remain "private" (internal symbols for library only).
- *        Set macro FSE_DLL_EXPORT to 1 if you want HUF symbols visible on DLL interface */
-#if defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1) && defined(__GNUC__) && (__GNUC__ >= 4)
-#  define HUF_PUBLIC_API __attribute__ ((visibility ("default")))
-#elif defined(FSE_DLL_EXPORT) && (FSE_DLL_EXPORT==1)   /* Visual expected */
-#  define HUF_PUBLIC_API __declspec(dllexport)
-#elif defined(FSE_DLL_IMPORT) && (FSE_DLL_IMPORT==1)
-#  define HUF_PUBLIC_API __declspec(dllimport)  /* not required, just to generate faster code (saves a function pointer load from IAT and an indirect jump) */
-#else
-#  define HUF_PUBLIC_API
-#endif
-
-
-/* ========================== */
-/* ***  simple functions  *** */
-/* ========================== */
-
-/** HUF_compress() :
- *  Compress content from buffer 'src', of size 'srcSize', into buffer 'dst'.
- * 'dst' buffer must be already allocated.
- *  Compression runs faster if `dstCapacity` >= HUF_compressBound(srcSize).
- * `srcSize` must be <= `HUF_BLOCKSIZE_MAX` == 128 KB.
- * @return : size of compressed data (<= `dstCapacity`).
- *  Special values : if return == 0, srcData is not compressible => Nothing is stored within dst !!!
- *                   if HUF_isError(return), compression failed (more details using HUF_getErrorName())
- */
-HUF_PUBLIC_API size_t HUF_compress(void* dst, size_t dstCapacity,
-                             const void* src, size_t srcSize);
-
-/** HUF_decompress() :
- *  Decompress HUF data from buffer 'cSrc', of size 'cSrcSize',
- *  into already allocated buffer 'dst', of minimum size 'dstSize'.
- * `originalSize` : **must** be the ***exact*** size of original (uncompressed) data.
- *  Note : in contrast with FSE, HUF_decompress can regenerate
- *         RLE (cSrcSize==1) and uncompressed (cSrcSize==dstSize) data,
- *         because it knows size to regenerate (originalSize).
- * @return : size of regenerated data (== originalSize),
- *           or an error code, which can be tested using HUF_isError()
- */
-HUF_PUBLIC_API size_t HUF_decompress(void* dst,  size_t originalSize,
-                               const void* cSrc, size_t cSrcSize);
-
-
-/* ***   Tool functions *** */
-#define HUF_BLOCKSIZE_MAX (128 * 1024)                  /**< maximum input size for a single block compressed with HUF_compress */
-HUF_PUBLIC_API size_t HUF_compressBound(size_t size);   /**< maximum compressed size (worst case) */
-
-/* Error Management */
-HUF_PUBLIC_API unsigned    HUF_isError(size_t code);       /**< tells if a return value is an error code */
-HUF_PUBLIC_API const char* HUF_getErrorName(size_t code);  /**< provides error code string (useful for debugging) */
-
-
-/* ***   Advanced function   *** */
-
-/** HUF_compress2() :
- *  Same as HUF_compress(), but offers control over `maxSymbolValue` and `tableLog`.
- * `maxSymbolValue` must be <= HUF_SYMBOLVALUE_MAX .
- * `tableLog` must be `<= HUF_TABLELOG_MAX` . */
-HUF_PUBLIC_API size_t HUF_compress2 (void* dst, size_t dstCapacity,
-                               const void* src, size_t srcSize,
-                               unsigned maxSymbolValue, unsigned tableLog);
-
-/** HUF_compress4X_wksp() :
- *  Same as HUF_compress2(), but uses externally allocated `workSpace`.
- * `workspace` must have minimum alignment of 4, and be at least as large as HUF_WORKSPACE_SIZE */
-#define HUF_WORKSPACE_SIZE ((6 << 10) + 256)
-#define HUF_WORKSPACE_SIZE_U32 (HUF_WORKSPACE_SIZE / sizeof(U32))
-HUF_PUBLIC_API size_t HUF_compress4X_wksp (void* dst, size_t dstCapacity,
-                                     const void* src, size_t srcSize,
-                                     unsigned maxSymbolValue, unsigned tableLog,
-                                     void* workSpace, size_t wkspSize);
-
-#endif   /* HUF_H_298734234 */
-
-/* ******************************************************************
- *  WARNING !!
- *  The following section contains advanced and experimental definitions
- *  which shall never be used in the context of a dynamic library,
- *  because they are not guaranteed to remain stable in the future.
- *  Only consider them in association with static linking.
- * *****************************************************************/
-#if defined(HUF_STATIC_LINKING_ONLY) && !defined(HUF_H_HUF_STATIC_LINKING_ONLY)
-#define HUF_H_HUF_STATIC_LINKING_ONLY
-
-/* *** Dependencies *** */
-#include "mem.h"   /* U32 */
+#include "mem.h"          /* U32 */
 #define FSE_STATIC_LINKING_ONLY
 #include "fse.h"
 
 
+/* ***   Tool functions *** */
+#define HUF_BLOCKSIZE_MAX (128 * 1024)   /**< maximum input size for a single block compressed with HUF_compress */
+size_t HUF_compressBound(size_t size);   /**< maximum compressed size (worst case) */
+
+/* Error Management */
+unsigned    HUF_isError(size_t code);       /**< tells if a return value is an error code */
+const char* HUF_getErrorName(size_t code);  /**< provides error code string (useful for debugging) */
+
+
+#define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */)
+#define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64))
+
 /* *** Constants *** */
-#define HUF_TABLELOG_MAX      12      /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */
+#define HUF_TABLELOG_MAX      12      /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_TABLELOG_ABSOLUTEMAX */
 #define HUF_TABLELOG_DEFAULT  11      /* default tableLog value when none specified */
 #define HUF_SYMBOLVALUE_MAX  255
 
-#define HUF_TABLELOG_ABSOLUTEMAX  15  /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
+#define HUF_TABLELOG_ABSOLUTEMAX  12  /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
 #if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX)
 #  error "HUF_TABLELOG_MAX is too large !"
 #endif
@@ -136,15 +59,11 @@
 
 /* static allocation of HUF's Compression Table */
 /* this is a private definition, just exposed for allocation and strict aliasing purpose. never EVER access its members directly */
-struct HUF_CElt_s {
-  U16  val;
-  BYTE nbBits;
-};   /* typedef'd to HUF_CElt */
-typedef struct HUF_CElt_s HUF_CElt;   /* consider it an incomplete type */
-#define HUF_CTABLE_SIZE_U32(maxSymbolValue)   ((maxSymbolValue)+1)   /* Use tables of U32, for proper alignment */
-#define HUF_CTABLE_SIZE(maxSymbolValue)       (HUF_CTABLE_SIZE_U32(maxSymbolValue) * sizeof(U32))
+typedef size_t HUF_CElt;   /* consider it an incomplete type */
+#define HUF_CTABLE_SIZE_ST(maxSymbolValue)   ((maxSymbolValue)+2)   /* Use tables of size_t, for proper alignment */
+#define HUF_CTABLE_SIZE(maxSymbolValue)       (HUF_CTABLE_SIZE_ST(maxSymbolValue) * sizeof(size_t))
 #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \
-    HUF_CElt name[HUF_CTABLE_SIZE_U32(maxSymbolValue)] /* no final ; */
+    HUF_CElt name[HUF_CTABLE_SIZE_ST(maxSymbolValue)] /* no final ; */
 
 /* static allocation of HUF's DTable */
 typedef U32 HUF_DTable;
@@ -158,25 +77,49 @@
 /* ****************************************
 *  Advanced decompression functions
 ******************************************/
-size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< single-symbol decoder */
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< double-symbols decoder */
-#endif
 
-size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< decodes RLE and uncompressed */
-size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize); /**< considers RLE and uncompressed as errors */
-size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize); /**< considers RLE and uncompressed as errors */
-size_t HUF_decompress4X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< single-symbol decoder */
-size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);   /**< single-symbol decoder */
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< double-symbols decoder */
-size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);   /**< double-symbols decoder */
-#endif
+/**
+ * Huffman flags bitset.
+ * For all flags, 0 is the default value.
+ */
+typedef enum {
+    /**
+     * If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime.
+     * Otherwise: Ignored.
+     */
+    HUF_flags_bmi2 = (1 << 0),
+    /**
+     * If set: Test possible table depths to find the one that produces the smallest header + encoded size.
+     * If unset: Use heuristic to find the table depth.
+     */
+    HUF_flags_optimalDepth = (1 << 1),
+    /**
+     * If set: If the previous table can encode the input, always reuse the previous table.
+     * If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output.
+     */
+    HUF_flags_preferRepeat = (1 << 2),
+    /**
+     * If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress.
+     * If unset: Always histogram the entire input.
+     */
+    HUF_flags_suspectUncompressible = (1 << 3),
+    /**
+     * If set: Don't use assembly implementations
+     * If unset: Allow using assembly implementations
+     */
+    HUF_flags_disableAsm = (1 << 4),
+    /**
+     * If set: Don't use the fast decoding loop, always use the fallback decoding loop.
+     * If unset: Use the fast decoding loop when possible.
+     */
+    HUF_flags_disableFast = (1 << 5)
+} HUF_flags_e;
 
 
 /* ****************************************
  *  HUF detailed API
  * ****************************************/
+#define HUF_OPTIMAL_DEPTH_THRESHOLD ZSTD_btultra
 
 /*! HUF_compress() does the following:
  *  1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h")
@@ -189,11 +132,12 @@
  *  For example, it's possible to compress several blocks using the same 'CTable',
  *  or to save and regenerate 'CTable' using external methods.
  */
-unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
-size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits);   /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */
-size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog);
+unsigned HUF_minTableLog(unsigned symbolCardinality);
+unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue);
+unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, void* workSpace,
+ size_t wkspSize, HUF_CElt* table, const unsigned* count, int flags); /* table is used as scratch space for building and testing tables, not a return value */
 size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize);
-size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
+size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags);
 size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
 int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
 
@@ -202,22 +146,24 @@
    HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */
    HUF_repeat_valid  /**< Can use the previous table and it is assumed to be valid */
  } HUF_repeat;
+
 /** HUF_compress4X_repeat() :
  *  Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
  *  If it uses hufTable it does not modify hufTable or repeat.
  *  If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
- *  If preferRepeat then the old table will always be used if valid. */
+ *  If preferRepeat then the old table will always be used if valid.
+ *  If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */
 size_t HUF_compress4X_repeat(void* dst, size_t dstSize,
                        const void* src, size_t srcSize,
                        unsigned maxSymbolValue, unsigned tableLog,
                        void* workSpace, size_t wkspSize,    /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
-                       HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
+                       HUF_CElt* hufTable, HUF_repeat* repeat, int flags);
 
 /** HUF_buildCTable_wksp() :
  *  Same as HUF_buildCTable(), but using externally allocated scratch buffer.
  * `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE.
  */
-#define HUF_CTABLE_WORKSPACE_SIZE_U32 (2*HUF_SYMBOLVALUE_MAX +1 +1)
+#define HUF_CTABLE_WORKSPACE_SIZE_U32 ((4 * (HUF_SYMBOLVALUE_MAX + 1)) + 192)
 #define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned))
 size_t HUF_buildCTable_wksp (HUF_CElt* tree,
                        const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
@@ -243,17 +189,16 @@
                           U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
                           const void* src, size_t srcSize,
                           void* workspace, size_t wkspSize,
-                          int bmi2);
+                          int flags);
 
 /** HUF_readCTable() :
  *  Loading a CTable saved with HUF_writeCTable() */
 size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights);
 
-/** HUF_getNbBits() :
+/** HUF_getNbBitsFromCTable() :
  *  Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX
- *  Note 1 : is not inlined, as HUF_CElt definition is private
- *  Note 2 : const void* used, so that it can provide a statically allocated table as argument (which uses type U32) */
-U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue);
+ *  Note 1 : is not inlined, as HUF_CElt definition is private */
+U32 HUF_getNbBitsFromCTable(const HUF_CElt* symbolTable, U32 symbolValue);
 
 /*
  * HUF_decompress() does the following:
@@ -282,80 +227,46 @@
 #define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9))
 #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
 
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_readDTableX1 (HUF_DTable* DTable, const void* src, size_t srcSize);
-size_t HUF_readDTableX1_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
-#endif
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_readDTableX2 (HUF_DTable* DTable, const void* src, size_t srcSize);
-size_t HUF_readDTableX2_wksp (HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize);
-#endif
-
-size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress4X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
-#endif
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress4X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
-#endif
-
 
 /* ====================== */
 /* single stream variants */
 /* ====================== */
 
-size_t HUF_compress1X (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog);
-size_t HUF_compress1X_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);  /**< `workSpace` must be a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
-size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
+size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags);
 /** HUF_compress1X_repeat() :
  *  Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
  *  If it uses hufTable it does not modify hufTable or repeat.
  *  If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
- *  If preferRepeat then the old table will always be used if valid. */
+ *  If preferRepeat then the old table will always be used if valid.
+ *  If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */
 size_t HUF_compress1X_repeat(void* dst, size_t dstSize,
                        const void* src, size_t srcSize,
                        unsigned maxSymbolValue, unsigned tableLog,
                        void* workSpace, size_t wkspSize,   /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
-                       HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2);
+                       HUF_CElt* hufTable, HUF_repeat* repeat, int flags);
 
-size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /* single-symbol decoder */
+size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
 #ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /* double-symbol decoder */
-#endif
-
-size_t HUF_decompress1X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
-size_t HUF_decompress1X_DCtx_wksp (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress1X1_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< single-symbol decoder */
-size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);   /**< single-symbol decoder */
-#endif
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress1X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);   /**< double-symbols decoder */
-size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize);   /**< double-symbols decoder */
-#endif
-
-size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);   /**< automatic selection of sing or double symbol decoder, based on DTable */
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress1X1_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
-#endif
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress1X2_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable);
+size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);   /**< double-symbols decoder */
 #endif
 
 /* BMI2 variants.
  * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
  */
-size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
+size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags);
 #ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
 #endif
-size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
-size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags);
+size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
 #ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2);
+size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags);
+#endif
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags);
 #endif
 
-#endif /* HUF_STATIC_LINKING_ONLY */
+#endif   /* HUF_H_298734234 */
 
 #if defined (__cplusplus)
 }
diff --git a/Utilities/cmzstd/lib/common/mem.h b/Utilities/cmzstd/lib/common/mem.h
index 9f3b81a..98dd47a 100644
--- a/Utilities/cmzstd/lib/common/mem.h
+++ b/Utilities/cmzstd/lib/common/mem.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -51,6 +51,8 @@
 #    include <stdint.h> /* intptr_t */
 #  endif
   typedef   uint8_t BYTE;
+  typedef   uint8_t U8;
+  typedef    int8_t S8;
   typedef  uint16_t U16;
   typedef   int16_t S16;
   typedef  uint32_t U32;
@@ -63,6 +65,8 @@
 #  error "this implementation requires char to be exactly 8-bit type"
 #endif
   typedef unsigned char      BYTE;
+  typedef unsigned char      U8;
+  typedef   signed char      S8;
 #if USHRT_MAX != 65535
 #  error "this implementation requires short to be exactly 16-bit type"
 #endif
@@ -129,21 +133,15 @@
 /*-**************************************************************
 *  Memory I/O Implementation
 *****************************************************************/
-/* MEM_FORCE_MEMORY_ACCESS :
- * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
- * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
- * The below switch allow to select different access method for improved performance.
- * Method 0 (default) : use `memcpy()`. Safe and portable.
- * Method 1 : `__packed` statement. It depends on compiler extension (i.e., not portable).
- *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+/* MEM_FORCE_MEMORY_ACCESS : For accessing unaligned memory:
+ * Method 0 : always use `memcpy()`. Safe and portable.
+ * Method 1 : Use compiler extension to set unaligned access.
  * Method 2 : direct access. This method is portable but violate C standard.
  *            It can generate buggy code on targets depending on alignment.
- *            In some circumstances, it's the only known way to get the most performance (i.e. GCC + ARMv6)
- * See http://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
- * Prefer these methods in priority order (0 > 1 > 2)
+ * Default  : method 1 if supported, else method 0
  */
 #ifndef MEM_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
-#  if defined(__INTEL_COMPILER) || defined(__GNUC__) || defined(__ICCARM__)
+#  ifdef __GNUC__
 #    define MEM_FORCE_MEMORY_ACCESS 1
 #  endif
 #endif
@@ -153,8 +151,22 @@
 
 MEM_STATIC unsigned MEM_isLittleEndian(void)
 {
+#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+    return 1;
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+    return 0;
+#elif defined(__clang__) && __LITTLE_ENDIAN__
+    return 1;
+#elif defined(__clang__) && __BIG_ENDIAN__
+    return 0;
+#elif defined(_MSC_VER) && (_M_AMD64 || _M_IX86)
+    return 1;
+#elif defined(__DMC__) && defined(_M_IX86)
+    return 1;
+#else
     const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental  */
     return one.c[0];
+#endif
 }
 
 #if defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==2)
@@ -172,30 +184,19 @@
 
 #elif defined(MEM_FORCE_MEMORY_ACCESS) && (MEM_FORCE_MEMORY_ACCESS==1)
 
-/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
-/* currently only defined for gcc and icc */
-#if defined(_MSC_VER) || (defined(__INTEL_COMPILER) && defined(WIN32))
-    __pragma( pack(push, 1) )
-    typedef struct { U16 v; } unalign16;
-    typedef struct { U32 v; } unalign32;
-    typedef struct { U64 v; } unalign64;
-    typedef struct { size_t v; } unalignArch;
-    __pragma( pack(pop) )
-#else
-    typedef struct { U16 v; } __attribute__((packed)) unalign16;
-    typedef struct { U32 v; } __attribute__((packed)) unalign32;
-    typedef struct { U64 v; } __attribute__((packed)) unalign64;
-    typedef struct { size_t v; } __attribute__((packed)) unalignArch;
-#endif
+typedef __attribute__((aligned(1))) U16 unalign16;
+typedef __attribute__((aligned(1))) U32 unalign32;
+typedef __attribute__((aligned(1))) U64 unalign64;
+typedef __attribute__((aligned(1))) size_t unalignArch;
 
-MEM_STATIC U16 MEM_read16(const void* ptr) { return ((const unalign16*)ptr)->v; }
-MEM_STATIC U32 MEM_read32(const void* ptr) { return ((const unalign32*)ptr)->v; }
-MEM_STATIC U64 MEM_read64(const void* ptr) { return ((const unalign64*)ptr)->v; }
-MEM_STATIC size_t MEM_readST(const void* ptr) { return ((const unalignArch*)ptr)->v; }
+MEM_STATIC U16 MEM_read16(const void* ptr) { return *(const unalign16*)ptr; }
+MEM_STATIC U32 MEM_read32(const void* ptr) { return *(const unalign32*)ptr; }
+MEM_STATIC U64 MEM_read64(const void* ptr) { return *(const unalign64*)ptr; }
+MEM_STATIC size_t MEM_readST(const void* ptr) { return *(const unalignArch*)ptr; }
 
-MEM_STATIC void MEM_write16(void* memPtr, U16 value) { ((unalign16*)memPtr)->v = value; }
-MEM_STATIC void MEM_write32(void* memPtr, U32 value) { ((unalign32*)memPtr)->v = value; }
-MEM_STATIC void MEM_write64(void* memPtr, U64 value) { ((unalign64*)memPtr)->v = value; }
+MEM_STATIC void MEM_write16(void* memPtr, U16 value) { *(unalign16*)memPtr = value; }
+MEM_STATIC void MEM_write32(void* memPtr, U32 value) { *(unalign32*)memPtr = value; }
+MEM_STATIC void MEM_write64(void* memPtr, U64 value) { *(unalign64*)memPtr = value; }
 
 #else
 
@@ -239,6 +240,14 @@
 
 #endif /* MEM_FORCE_MEMORY_ACCESS */
 
+MEM_STATIC U32 MEM_swap32_fallback(U32 in)
+{
+    return  ((in << 24) & 0xff000000 ) |
+            ((in <<  8) & 0x00ff0000 ) |
+            ((in >>  8) & 0x0000ff00 ) |
+            ((in >> 24) & 0x000000ff );
+}
+
 MEM_STATIC U32 MEM_swap32(U32 in)
 {
 #if defined(_MSC_VER)     /* Visual Studio */
@@ -247,13 +256,22 @@
   || (defined(__clang__) && __has_builtin(__builtin_bswap32))
     return __builtin_bswap32(in);
 #else
-    return  ((in << 24) & 0xff000000 ) |
-            ((in <<  8) & 0x00ff0000 ) |
-            ((in >>  8) & 0x0000ff00 ) |
-            ((in >> 24) & 0x000000ff );
+    return MEM_swap32_fallback(in);
 #endif
 }
 
+MEM_STATIC U64 MEM_swap64_fallback(U64 in)
+{
+     return  ((in << 56) & 0xff00000000000000ULL) |
+            ((in << 40) & 0x00ff000000000000ULL) |
+            ((in << 24) & 0x0000ff0000000000ULL) |
+            ((in << 8)  & 0x000000ff00000000ULL) |
+            ((in >> 8)  & 0x00000000ff000000ULL) |
+            ((in >> 24) & 0x0000000000ff0000ULL) |
+            ((in >> 40) & 0x000000000000ff00ULL) |
+            ((in >> 56) & 0x00000000000000ffULL);
+}
+
 MEM_STATIC U64 MEM_swap64(U64 in)
 {
 #if defined(_MSC_VER)     /* Visual Studio */
@@ -262,14 +280,7 @@
   || (defined(__clang__) && __has_builtin(__builtin_bswap64))
     return __builtin_bswap64(in);
 #else
-    return  ((in << 56) & 0xff00000000000000ULL) |
-            ((in << 40) & 0x00ff000000000000ULL) |
-            ((in << 24) & 0x0000ff0000000000ULL) |
-            ((in << 8)  & 0x000000ff00000000ULL) |
-            ((in >> 8)  & 0x00000000ff000000ULL) |
-            ((in >> 24) & 0x0000000000ff0000ULL) |
-            ((in >> 40) & 0x000000000000ff00ULL) |
-            ((in >> 56) & 0x00000000000000ffULL);
+    return MEM_swap64_fallback(in);
 #endif
 }
 
diff --git a/Utilities/cmzstd/lib/common/pool.c b/Utilities/cmzstd/lib/common/pool.c
index ea70b8b..d5ca5a7 100644
--- a/Utilities/cmzstd/lib/common/pool.c
+++ b/Utilities/cmzstd/lib/common/pool.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -10,9 +10,9 @@
 
 
 /* ======   Dependencies   ======= */
+#include "../common/allocations.h"  /* ZSTD_customCalloc, ZSTD_customFree */
 #include "zstd_deps.h" /* size_t */
 #include "debug.h"     /* assert */
-#include "zstd_internal.h"  /* ZSTD_customMalloc, ZSTD_customFree */
 #include "pool.h"
 
 /* ======   Compiler specifics   ====== */
@@ -86,7 +86,7 @@
         {   POOL_job const job = ctx->queue[ctx->queueHead];
             ctx->queueHead = (ctx->queueHead + 1) % ctx->queueSize;
             ctx->numThreadsBusy++;
-            ctx->queueEmpty = ctx->queueHead == ctx->queueTail;
+            ctx->queueEmpty = (ctx->queueHead == ctx->queueTail);
             /* Unlock the mutex, signal a pusher, and run the job */
             ZSTD_pthread_cond_signal(&ctx->queuePushCond);
             ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
@@ -96,15 +96,14 @@
             /* If the intended queue size was 0, signal after finishing job */
             ZSTD_pthread_mutex_lock(&ctx->queueMutex);
             ctx->numThreadsBusy--;
-            if (ctx->queueSize == 1) {
-                ZSTD_pthread_cond_signal(&ctx->queuePushCond);
-            }
+            ZSTD_pthread_cond_signal(&ctx->queuePushCond);
             ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
         }
     }  /* for (;;) */
     assert(0);  /* Unreachable */
 }
 
+/* ZSTD_createThreadPool() : public access point */
 POOL_ctx* ZSTD_createThreadPool(size_t numThreads) {
     return POOL_create (numThreads, 0);
 }
@@ -114,7 +113,8 @@
 }
 
 POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize,
-                               ZSTD_customMem customMem) {
+                               ZSTD_customMem customMem)
+{
     POOL_ctx* ctx;
     /* Check parameters */
     if (!numThreads) { return NULL; }
@@ -126,7 +126,7 @@
      * empty and full queues.
      */
     ctx->queueSize = queueSize + 1;
-    ctx->queue = (POOL_job*)ZSTD_customMalloc(ctx->queueSize * sizeof(POOL_job), customMem);
+    ctx->queue = (POOL_job*)ZSTD_customCalloc(ctx->queueSize * sizeof(POOL_job), customMem);
     ctx->queueHead = 0;
     ctx->queueTail = 0;
     ctx->numThreadsBusy = 0;
@@ -140,7 +140,7 @@
     }
     ctx->shutdown = 0;
     /* Allocate space for the thread handles */
-    ctx->threads = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
+    ctx->threads = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
     ctx->threadCapacity = 0;
     ctx->customMem = customMem;
     /* Check for errors */
@@ -173,7 +173,7 @@
     /* Join all of the threads */
     {   size_t i;
         for (i = 0; i < ctx->threadCapacity; ++i) {
-            ZSTD_pthread_join(ctx->threads[i], NULL);  /* note : could fail */
+            ZSTD_pthread_join(ctx->threads[i]);  /* note : could fail */
     }   }
 }
 
@@ -188,11 +188,22 @@
     ZSTD_customFree(ctx, ctx->customMem);
 }
 
+/*! POOL_joinJobs() :
+ *  Waits for all queued jobs to finish executing.
+ */
+void POOL_joinJobs(POOL_ctx* ctx) {
+    ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+    while(!ctx->queueEmpty || ctx->numThreadsBusy > 0) {
+        ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
+    }
+    ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+}
+
 void ZSTD_freeThreadPool (ZSTD_threadPool* pool) {
   POOL_free (pool);
 }
 
-size_t POOL_sizeof(POOL_ctx *ctx) {
+size_t POOL_sizeof(const POOL_ctx* ctx) {
     if (ctx==NULL) return 0;  /* supports sizeof NULL */
     return sizeof(*ctx)
         + ctx->queueSize * sizeof(POOL_job)
@@ -209,7 +220,7 @@
         return 0;
     }
     /* numThreads > threadCapacity */
-    {   ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);
+    {   ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customCalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);
         if (!threadPool) return 1;
         /* replace existing thread pool */
         ZSTD_memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool));
@@ -257,9 +268,12 @@
 }
 
 
-static void POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)
+static void
+POOL_add_internal(POOL_ctx* ctx, POOL_function function, void *opaque)
 {
-    POOL_job const job = {function, opaque};
+    POOL_job job;
+    job.function = function;
+    job.opaque = opaque;
     assert(ctx != NULL);
     if (ctx->shutdown) return;
 
@@ -313,7 +327,9 @@
     return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
 }
 
-POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem) {
+POOL_ctx*
+POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customMem customMem)
+{
     (void)numThreads;
     (void)queueSize;
     (void)customMem;
@@ -325,6 +341,11 @@
     (void)ctx;
 }
 
+void POOL_joinJobs(POOL_ctx* ctx){
+    assert(!ctx || ctx == &g_poolCtx);
+    (void)ctx;
+}
+
 int POOL_resize(POOL_ctx* ctx, size_t numThreads) {
     (void)ctx; (void)numThreads;
     return 0;
@@ -341,7 +362,7 @@
     return 1;
 }
 
-size_t POOL_sizeof(POOL_ctx* ctx) {
+size_t POOL_sizeof(const POOL_ctx* ctx) {
     if (ctx==NULL) return 0;  /* supports sizeof NULL */
     assert(ctx == &g_poolCtx);
     return sizeof(*ctx);
diff --git a/Utilities/cmzstd/lib/common/pool.h b/Utilities/cmzstd/lib/common/pool.h
index e18aa07..eb22ff5 100644
--- a/Utilities/cmzstd/lib/common/pool.h
+++ b/Utilities/cmzstd/lib/common/pool.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -38,6 +38,12 @@
  */
 void POOL_free(POOL_ctx* ctx);
 
+
+/*! POOL_joinJobs() :
+ *  Waits for all queued jobs to finish executing.
+ */
+void POOL_joinJobs(POOL_ctx* ctx);
+
 /*! POOL_resize() :
  *  Expands or shrinks pool's number of threads.
  *  This is more efficient than releasing + creating a new context,
@@ -53,7 +59,7 @@
  * @return threadpool memory usage
  *  note : compatible with NULL (returns 0 in this case)
  */
-size_t POOL_sizeof(POOL_ctx* ctx);
+size_t POOL_sizeof(const POOL_ctx* ctx);
 
 /*! POOL_function :
  *  The function type that can be added to a thread pool.
@@ -70,7 +76,7 @@
 
 
 /*! POOL_tryAdd() :
- *  Add the job `function(opaque)` to thread pool _if_ a worker is available.
+ *  Add the job `function(opaque)` to thread pool _if_ a queue slot is available.
  *  Returns immediately even if not (does not block).
  * @return : 1 if successful, 0 if not.
  */
diff --git a/Utilities/cmzstd/lib/common/portability_macros.h b/Utilities/cmzstd/lib/common/portability_macros.h
new file mode 100644
index 0000000..8fd6ea8
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/portability_macros.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_PORTABILITY_MACROS_H
+#define ZSTD_PORTABILITY_MACROS_H
+
+/**
+ * This header file contains macro definitions to support portability.
+ * This header is shared between C and ASM code, so it MUST only
+ * contain macro definitions. It MUST not contain any C code.
+ *
+ * This header ONLY defines macros to detect platforms/feature support.
+ *
+ */
+
+
+/* compat. with non-clang compilers */
+#ifndef __has_attribute
+  #define __has_attribute(x) 0
+#endif
+
+/* compat. with non-clang compilers */
+#ifndef __has_builtin
+#  define __has_builtin(x) 0
+#endif
+
+/* compat. with non-clang compilers */
+#ifndef __has_feature
+#  define __has_feature(x) 0
+#endif
+
+/* detects whether we are being compiled under msan */
+#ifndef ZSTD_MEMORY_SANITIZER
+#  if __has_feature(memory_sanitizer)
+#    define ZSTD_MEMORY_SANITIZER 1
+#  else
+#    define ZSTD_MEMORY_SANITIZER 0
+#  endif
+#endif
+
+/* detects whether we are being compiled under asan */
+#ifndef ZSTD_ADDRESS_SANITIZER
+#  if __has_feature(address_sanitizer)
+#    define ZSTD_ADDRESS_SANITIZER 1
+#  elif defined(__SANITIZE_ADDRESS__)
+#    define ZSTD_ADDRESS_SANITIZER 1
+#  else
+#    define ZSTD_ADDRESS_SANITIZER 0
+#  endif
+#endif
+
+/* detects whether we are being compiled under dfsan */
+#ifndef ZSTD_DATAFLOW_SANITIZER
+# if __has_feature(dataflow_sanitizer)
+#  define ZSTD_DATAFLOW_SANITIZER 1
+# else
+#  define ZSTD_DATAFLOW_SANITIZER 0
+# endif
+#endif
+
+/* Mark the internal assembly functions as hidden  */
+#ifdef __ELF__
+# define ZSTD_HIDE_ASM_FUNCTION(func) .hidden func
+#else
+# define ZSTD_HIDE_ASM_FUNCTION(func)
+#endif
+
+/* Enable runtime BMI2 dispatch based on the CPU.
+ * Enabled for clang & gcc >=4.8 on x86 when BMI2 isn't enabled by default.
+ */
+#ifndef DYNAMIC_BMI2
+  #if ((defined(__clang__) && __has_attribute(__target__)) \
+      || (defined(__GNUC__) \
+          && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))) \
+      && (defined(__x86_64__) || defined(_M_X64)) \
+      && !defined(__BMI2__)
+  #  define DYNAMIC_BMI2 1
+  #else
+  #  define DYNAMIC_BMI2 0
+  #endif
+#endif
+
+/**
+ * Only enable assembly for GNUC compatible compilers,
+ * because other platforms may not support GAS assembly syntax.
+ *
+ * Only enable assembly for Linux / MacOS, other platforms may
+ * work, but they haven't been tested. This could likely be
+ * extended to BSD systems.
+ *
+ * Disable assembly when MSAN is enabled, because MSAN requires
+ * 100% of code to be instrumented to work.
+ */
+#if defined(__GNUC__)
+#  if defined(__linux__) || defined(__linux) || defined(__APPLE__)
+#    if ZSTD_MEMORY_SANITIZER
+#      define ZSTD_ASM_SUPPORTED 0
+#    elif ZSTD_DATAFLOW_SANITIZER
+#      define ZSTD_ASM_SUPPORTED 0
+#    else
+#      define ZSTD_ASM_SUPPORTED 1
+#    endif
+#  else
+#    define ZSTD_ASM_SUPPORTED 0
+#  endif
+#else
+#  define ZSTD_ASM_SUPPORTED 0
+#endif
+
+/**
+ * Determines whether we should enable assembly for x86-64
+ * with BMI2.
+ *
+ * Enable if all of the following conditions hold:
+ * - ASM hasn't been explicitly disabled by defining ZSTD_DISABLE_ASM
+ * - Assembly is supported
+ * - We are compiling for x86-64 and either:
+ *   - DYNAMIC_BMI2 is enabled
+ *   - BMI2 is supported at compile time
+ */
+#if !defined(ZSTD_DISABLE_ASM) &&                                 \
+    ZSTD_ASM_SUPPORTED &&                                         \
+    defined(__x86_64__) &&                                        \
+    (DYNAMIC_BMI2 || defined(__BMI2__))
+# define ZSTD_ENABLE_ASM_X86_64_BMI2 1
+#else
+# define ZSTD_ENABLE_ASM_X86_64_BMI2 0
+#endif
+
+/*
+ * For x86 ELF targets, add .note.gnu.property section for Intel CET in
+ * assembly sources when CET is enabled.
+ *
+ * Additionally, any function that may be called indirectly must begin
+ * with ZSTD_CET_ENDBRANCH.
+ */
+#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) \
+    && defined(__has_include)
+# if __has_include(<cet.h>)
+#  include <cet.h>
+#  define ZSTD_CET_ENDBRANCH _CET_ENDBR
+# endif
+#endif
+
+#ifndef ZSTD_CET_ENDBRANCH
+# define ZSTD_CET_ENDBRANCH
+#endif
+
+#endif /* ZSTD_PORTABILITY_MACROS_H */
diff --git a/Utilities/cmzstd/lib/common/threading.c b/Utilities/cmzstd/lib/common/threading.c
index 92cf57c..ca155b9 100644
--- a/Utilities/cmzstd/lib/common/threading.c
+++ b/Utilities/cmzstd/lib/common/threading.c
@@ -23,8 +23,7 @@
 #if defined(ZSTD_MULTITHREAD) && defined(_WIN32)
 
 /**
- * Windows minimalist Pthread Wrapper, based on :
- * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ * Windows minimalist Pthread Wrapper
  */
 
 
@@ -35,37 +34,92 @@
 
 /* ===  Implementation  === */
 
+typedef struct {
+    void* (*start_routine)(void*);
+    void* arg;
+    int initialized;
+    ZSTD_pthread_cond_t initialized_cond;
+    ZSTD_pthread_mutex_t initialized_mutex;
+} ZSTD_thread_params_t;
+
 static unsigned __stdcall worker(void *arg)
 {
-    ZSTD_pthread_t* const thread = (ZSTD_pthread_t*) arg;
-    thread->arg = thread->start_routine(thread->arg);
+    void* (*start_routine)(void*);
+    void* thread_arg;
+
+    /* Initialized thread_arg and start_routine and signal main thread that we don't need it
+     * to wait any longer.
+     */
+    {
+        ZSTD_thread_params_t*  thread_param = (ZSTD_thread_params_t*)arg;
+        thread_arg = thread_param->arg;
+        start_routine = thread_param->start_routine;
+
+        /* Signal main thread that we are running and do not depend on its memory anymore */
+        ZSTD_pthread_mutex_lock(&thread_param->initialized_mutex);
+        thread_param->initialized = 1;
+        ZSTD_pthread_cond_signal(&thread_param->initialized_cond);
+        ZSTD_pthread_mutex_unlock(&thread_param->initialized_mutex);
+    }
+
+    start_routine(thread_arg);
+
     return 0;
 }
 
 int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused,
             void* (*start_routine) (void*), void* arg)
 {
+    ZSTD_thread_params_t thread_param;
     (void)unused;
-    thread->arg = arg;
-    thread->start_routine = start_routine;
-    thread->handle = (HANDLE) _beginthreadex(NULL, 0, worker, thread, 0, NULL);
 
-    if (!thread->handle)
+    thread_param.start_routine = start_routine;
+    thread_param.arg = arg;
+    thread_param.initialized = 0;
+    *thread = NULL;
+
+    /* Setup thread initialization synchronization */
+    if(ZSTD_pthread_cond_init(&thread_param.initialized_cond, NULL)) {
+        /* Should never happen on Windows */
+        return -1;
+    }
+    if(ZSTD_pthread_mutex_init(&thread_param.initialized_mutex, NULL)) {
+        /* Should never happen on Windows */
+        ZSTD_pthread_cond_destroy(&thread_param.initialized_cond);
+        return -1;
+    }
+
+    /* Spawn thread */
+    *thread = (HANDLE)_beginthreadex(NULL, 0, worker, &thread_param, 0, NULL);
+    if (!thread) {
+        ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex);
+        ZSTD_pthread_cond_destroy(&thread_param.initialized_cond);
         return errno;
-    else
-        return 0;
+    }
+
+    /* Wait for thread to be initialized */
+    ZSTD_pthread_mutex_lock(&thread_param.initialized_mutex);
+    while(!thread_param.initialized) {
+        ZSTD_pthread_cond_wait(&thread_param.initialized_cond, &thread_param.initialized_mutex);
+    }
+    ZSTD_pthread_mutex_unlock(&thread_param.initialized_mutex);
+    ZSTD_pthread_mutex_destroy(&thread_param.initialized_mutex);
+    ZSTD_pthread_cond_destroy(&thread_param.initialized_cond);
+
+    return 0;
 }
 
-int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr)
+int ZSTD_pthread_join(ZSTD_pthread_t thread)
 {
     DWORD result;
 
-    if (!thread.handle) return 0;
+    if (!thread) return 0;
 
-    result = WaitForSingleObject(thread.handle, INFINITE);
+    result = WaitForSingleObject(thread, INFINITE);
+    CloseHandle(thread);
+
     switch (result) {
     case WAIT_OBJECT_0:
-        if (value_ptr) *value_ptr = thread.arg;
         return 0;
     case WAIT_ABANDONED:
         return EINVAL;
diff --git a/Utilities/cmzstd/lib/common/threading.h b/Utilities/cmzstd/lib/common/threading.h
index fd0060d..fb5c1c8 100644
--- a/Utilities/cmzstd/lib/common/threading.h
+++ b/Utilities/cmzstd/lib/common/threading.h
@@ -23,8 +23,7 @@
 #if defined(ZSTD_MULTITHREAD) && defined(_WIN32)
 
 /**
- * Windows minimalist Pthread Wrapper, based on :
- * http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ * Windows minimalist Pthread Wrapper
  */
 #ifdef WINVER
 #  undef WINVER
@@ -62,16 +61,12 @@
 #define ZSTD_pthread_cond_broadcast(a)  WakeAllConditionVariable((a))
 
 /* ZSTD_pthread_create() and ZSTD_pthread_join() */
-typedef struct {
-    HANDLE handle;
-    void* (*start_routine)(void*);
-    void* arg;
-} ZSTD_pthread_t;
+typedef HANDLE ZSTD_pthread_t;
 
 int ZSTD_pthread_create(ZSTD_pthread_t* thread, const void* unused,
                    void* (*start_routine) (void*), void* arg);
 
-int ZSTD_pthread_join(ZSTD_pthread_t thread, void** value_ptr);
+int ZSTD_pthread_join(ZSTD_pthread_t thread);
 
 /**
  * add here more wrappers as required
@@ -99,7 +94,7 @@
 
 #define ZSTD_pthread_t                  pthread_t
 #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d))
-#define ZSTD_pthread_join(a, b)         pthread_join((a),(b))
+#define ZSTD_pthread_join(a)         pthread_join((a),NULL)
 
 #else /* DEBUGLEVEL >= 1 */
 
@@ -124,7 +119,7 @@
 
 #define ZSTD_pthread_t                  pthread_t
 #define ZSTD_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d))
-#define ZSTD_pthread_join(a, b)         pthread_join((a),(b))
+#define ZSTD_pthread_join(a)         pthread_join((a),NULL)
 
 #endif
 
diff --git a/Utilities/cmzstd/lib/common/xxhash.c b/Utilities/cmzstd/lib/common/xxhash.c
index 926b336..fd237c9 100644
--- a/Utilities/cmzstd/lib/common/xxhash.c
+++ b/Utilities/cmzstd/lib/common/xxhash.c
@@ -1,11 +1,11 @@
 /*
  *  xxHash - Fast Hash algorithm
- *  Copyright (c) Yann Collet, Facebook, Inc.
+ *  Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
- *  - xxHash homepage: http://www.xxhash.com
+ *  - xxHash homepage: https://cyan4973.github.io/xxHash/
  *  - xxHash source repository : https://github.com/Cyan4973/xxHash
- * 
+ *
  * This source code is licensed under both the BSD-style license (found in the
  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  * in the COPYING file in the root directory of this source tree).
@@ -13,812 +13,12 @@
 */
 
 
-/* *************************************
-*  Tuning parameters
-***************************************/
-/*!XXH_FORCE_MEMORY_ACCESS :
- * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
- * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
- * The below switch allow to select different access method for improved performance.
- * Method 0 (default) : use `memcpy()`. Safe and portable.
- * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
- *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
- * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
- *            It can generate buggy code on targets which do not support unaligned memory accesses.
- *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
- * See http://stackoverflow.com/a/32095106/646947 for details.
- * Prefer these methods in priority order (0 > 1 > 2)
+
+/*
+ * xxhash.c instantiates functions defined in xxhash.h
  */
-#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
-#  if (defined(__INTEL_COMPILER) && !defined(WIN32)) || \
-  (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) )) || \
-  defined(__ICCARM__)
-#    define XXH_FORCE_MEMORY_ACCESS 1
-#  endif
-#endif
 
-/*!XXH_ACCEPT_NULL_INPUT_POINTER :
- * If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
- * When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
- * By default, this option is disabled. To enable it, uncomment below define :
- */
-/* #define XXH_ACCEPT_NULL_INPUT_POINTER 1 */
+#define XXH_STATIC_LINKING_ONLY   /* access advanced declarations */
+#define XXH_IMPLEMENTATION   /* access definitions */
 
-/*!XXH_FORCE_NATIVE_FORMAT :
- * By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
- * Results are therefore identical for little-endian and big-endian CPU.
- * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
- * Should endian-independence be of no importance for your application, you may set the #define below to 1,
- * to improve speed for Big-endian CPU.
- * This option has no impact on Little_Endian CPU.
- */
-#ifndef XXH_FORCE_NATIVE_FORMAT   /* can be defined externally */
-#  define XXH_FORCE_NATIVE_FORMAT 0
-#endif
-
-/*!XXH_FORCE_ALIGN_CHECK :
- * This is a minor performance trick, only useful with lots of very small keys.
- * It means : check for aligned/unaligned input.
- * The check costs one initial branch per hash; set to 0 when the input data
- * is guaranteed to be aligned.
- */
-#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
-#  if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
-#    define XXH_FORCE_ALIGN_CHECK 0
-#  else
-#    define XXH_FORCE_ALIGN_CHECK 1
-#  endif
-#endif
-
-
-/* *************************************
-*  Includes & Memory related functions
-***************************************/
-/* Modify the local functions below should you wish to use some other memory routines */
-/* for ZSTD_malloc(), ZSTD_free() */
-#define ZSTD_DEPS_NEED_MALLOC
-#include "zstd_deps.h"  /* size_t, ZSTD_malloc, ZSTD_free, ZSTD_memcpy */
-static void* XXH_malloc(size_t s) { return ZSTD_malloc(s); }
-static void  XXH_free  (void* p)  { ZSTD_free(p); }
-static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_memcpy(dest,src,size); }
-
-#ifndef XXH_STATIC_LINKING_ONLY
-#  define XXH_STATIC_LINKING_ONLY
-#endif
 #include "xxhash.h"
-
-
-/* *************************************
-*  Compiler Specific Options
-***************************************/
-#include "compiler.h"
-
-
-/* *************************************
-*  Basic Types
-***************************************/
-#include "mem.h"  /* BYTE, U32, U64, size_t */
-
-#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
-
-/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
-static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
-static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
-
-#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
-
-/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
-/* currently only defined for gcc and icc */
-typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign;
-
-static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
-static U64 XXH_read64(const void* ptr) { return ((const unalign*)ptr)->u64; }
-
-#else
-
-/* portable and safe solution. Generally efficient.
- * see : http://stackoverflow.com/a/32095106/646947
- */
-
-static U32 XXH_read32(const void* memPtr)
-{
-    U32 val;
-    ZSTD_memcpy(&val, memPtr, sizeof(val));
-    return val;
-}
-
-static U64 XXH_read64(const void* memPtr)
-{
-    U64 val;
-    ZSTD_memcpy(&val, memPtr, sizeof(val));
-    return val;
-}
-
-#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
-
-
-/* ****************************************
-*  Compiler-specific Functions and Macros
-******************************************/
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-
-/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
-#if defined(_MSC_VER)
-#  define XXH_rotl32(x,r) _rotl(x,r)
-#  define XXH_rotl64(x,r) _rotl64(x,r)
-#else
-#if defined(__ICCARM__)
-#  include <intrinsics.h>
-#  define XXH_rotl32(x,r) __ROR(x,(32 - r))
-#else
-#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
-#endif
-#  define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))
-#endif
-
-#if defined(_MSC_VER)     /* Visual Studio */
-#  define XXH_swap32 _byteswap_ulong
-#  define XXH_swap64 _byteswap_uint64
-#elif GCC_VERSION >= 403
-#  define XXH_swap32 __builtin_bswap32
-#  define XXH_swap64 __builtin_bswap64
-#else
-static U32 XXH_swap32 (U32 x)
-{
-    return  ((x << 24) & 0xff000000 ) |
-            ((x <<  8) & 0x00ff0000 ) |
-            ((x >>  8) & 0x0000ff00 ) |
-            ((x >> 24) & 0x000000ff );
-}
-static U64 XXH_swap64 (U64 x)
-{
-    return  ((x << 56) & 0xff00000000000000ULL) |
-            ((x << 40) & 0x00ff000000000000ULL) |
-            ((x << 24) & 0x0000ff0000000000ULL) |
-            ((x << 8)  & 0x000000ff00000000ULL) |
-            ((x >> 8)  & 0x00000000ff000000ULL) |
-            ((x >> 24) & 0x0000000000ff0000ULL) |
-            ((x >> 40) & 0x000000000000ff00ULL) |
-            ((x >> 56) & 0x00000000000000ffULL);
-}
-#endif
-
-
-/* *************************************
-*  Architecture Macros
-***************************************/
-typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
-
-/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
-#ifndef XXH_CPU_LITTLE_ENDIAN
-    static const int g_one = 1;
-#   define XXH_CPU_LITTLE_ENDIAN   (*(const char*)(&g_one))
-#endif
-
-
-/* ***************************
-*  Memory reads
-*****************************/
-typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
-
-FORCE_INLINE_TEMPLATE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
-{
-    if (align==XXH_unaligned)
-        return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
-    else
-        return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
-}
-
-FORCE_INLINE_TEMPLATE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)
-{
-    return XXH_readLE32_align(ptr, endian, XXH_unaligned);
-}
-
-static U32 XXH_readBE32(const void* ptr)
-{
-    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
-}
-
-FORCE_INLINE_TEMPLATE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)
-{
-    if (align==XXH_unaligned)
-        return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
-    else
-        return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
-}
-
-FORCE_INLINE_TEMPLATE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)
-{
-    return XXH_readLE64_align(ptr, endian, XXH_unaligned);
-}
-
-static U64 XXH_readBE64(const void* ptr)
-{
-    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
-}
-
-
-/* *************************************
-*  Macros
-***************************************/
-#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(int)(!!(c)) }; }    /* use only *after* variable declarations */
-
-
-/* *************************************
-*  Constants
-***************************************/
-static const U32 PRIME32_1 = 2654435761U;
-static const U32 PRIME32_2 = 2246822519U;
-static const U32 PRIME32_3 = 3266489917U;
-static const U32 PRIME32_4 =  668265263U;
-static const U32 PRIME32_5 =  374761393U;
-
-static const U64 PRIME64_1 = 11400714785074694791ULL;
-static const U64 PRIME64_2 = 14029467366897019727ULL;
-static const U64 PRIME64_3 =  1609587929392839161ULL;
-static const U64 PRIME64_4 =  9650029242287828579ULL;
-static const U64 PRIME64_5 =  2870177450012600261ULL;
-
-XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
-
-
-/* **************************
-*  Utils
-****************************/
-XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dstState, const XXH32_state_t* restrict srcState)
-{
-    ZSTD_memcpy(dstState, srcState, sizeof(*dstState));
-}
-
-XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dstState, const XXH64_state_t* restrict srcState)
-{
-    ZSTD_memcpy(dstState, srcState, sizeof(*dstState));
-}
-
-
-/* ***************************
-*  Simple Hash Functions
-*****************************/
-
-static U32 XXH32_round(U32 seed, U32 input)
-{
-    seed += input * PRIME32_2;
-    seed  = XXH_rotl32(seed, 13);
-    seed *= PRIME32_1;
-    return seed;
-}
-
-FORCE_INLINE_TEMPLATE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align)
-{
-    const BYTE* p = (const BYTE*)input;
-    const BYTE* bEnd = p + len;
-    U32 h32;
-#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)
-
-#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
-    if (p==NULL) {
-        len=0;
-        bEnd=p=(const BYTE*)(size_t)16;
-    }
-#endif
-
-    if (len>=16) {
-        const BYTE* const limit = bEnd - 16;
-        U32 v1 = seed + PRIME32_1 + PRIME32_2;
-        U32 v2 = seed + PRIME32_2;
-        U32 v3 = seed + 0;
-        U32 v4 = seed - PRIME32_1;
-
-        do {
-            v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
-            v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
-            v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
-            v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
-        } while (p<=limit);
-
-        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
-    } else {
-        h32  = seed + PRIME32_5;
-    }
-
-    h32 += (U32) len;
-
-    while (p+4<=bEnd) {
-        h32 += XXH_get32bits(p) * PRIME32_3;
-        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;
-        p+=4;
-    }
-
-    while (p<bEnd) {
-        h32 += (*p) * PRIME32_5;
-        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
-        p++;
-    }
-
-    h32 ^= h32 >> 15;
-    h32 *= PRIME32_2;
-    h32 ^= h32 >> 13;
-    h32 *= PRIME32_3;
-    h32 ^= h32 >> 16;
-
-    return h32;
-}
-
-
-XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)
-{
-#if 0
-    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
-    XXH32_CREATESTATE_STATIC(state);
-    XXH32_reset(state, seed);
-    XXH32_update(state, input, len);
-    return XXH32_digest(state);
-#else
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if (XXH_FORCE_ALIGN_CHECK) {
-        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */
-            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-                return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
-            else
-                return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
-    }   }
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
-    else
-        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
-#endif
-}
-
-
-static U64 XXH64_round(U64 acc, U64 input)
-{
-    acc += input * PRIME64_2;
-    acc  = XXH_rotl64(acc, 31);
-    acc *= PRIME64_1;
-    return acc;
-}
-
-static U64 XXH64_mergeRound(U64 acc, U64 val)
-{
-    val  = XXH64_round(0, val);
-    acc ^= val;
-    acc  = acc * PRIME64_1 + PRIME64_4;
-    return acc;
-}
-
-FORCE_INLINE_TEMPLATE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align)
-{
-    const BYTE* p = (const BYTE*)input;
-    const BYTE* const bEnd = p + len;
-    U64 h64;
-#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)
-
-#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
-    if (p==NULL) {
-        len=0;
-        bEnd=p=(const BYTE*)(size_t)32;
-    }
-#endif
-
-    if (len>=32) {
-        const BYTE* const limit = bEnd - 32;
-        U64 v1 = seed + PRIME64_1 + PRIME64_2;
-        U64 v2 = seed + PRIME64_2;
-        U64 v3 = seed + 0;
-        U64 v4 = seed - PRIME64_1;
-
-        do {
-            v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
-            v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
-            v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
-            v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
-        } while (p<=limit);
-
-        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
-        h64 = XXH64_mergeRound(h64, v1);
-        h64 = XXH64_mergeRound(h64, v2);
-        h64 = XXH64_mergeRound(h64, v3);
-        h64 = XXH64_mergeRound(h64, v4);
-
-    } else {
-        h64  = seed + PRIME64_5;
-    }
-
-    h64 += (U64) len;
-
-    while (p+8<=bEnd) {
-        U64 const k1 = XXH64_round(0, XXH_get64bits(p));
-        h64 ^= k1;
-        h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
-        p+=8;
-    }
-
-    if (p+4<=bEnd) {
-        h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1;
-        h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
-        p+=4;
-    }
-
-    while (p<bEnd) {
-        h64 ^= (*p) * PRIME64_5;
-        h64 = XXH_rotl64(h64, 11) * PRIME64_1;
-        p++;
-    }
-
-    h64 ^= h64 >> 33;
-    h64 *= PRIME64_2;
-    h64 ^= h64 >> 29;
-    h64 *= PRIME64_3;
-    h64 ^= h64 >> 32;
-
-    return h64;
-}
-
-
-XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)
-{
-#if 0
-    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
-    XXH64_CREATESTATE_STATIC(state);
-    XXH64_reset(state, seed);
-    XXH64_update(state, input, len);
-    return XXH64_digest(state);
-#else
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if (XXH_FORCE_ALIGN_CHECK) {
-        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */
-            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-                return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
-            else
-                return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
-    }   }
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
-    else
-        return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
-#endif
-}
-
-
-/* **************************************************
-*  Advanced Hash Functions
-****************************************************/
-
-XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
-{
-    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
-}
-XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
-{
-    XXH_free(statePtr);
-    return XXH_OK;
-}
-
-XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
-{
-    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
-}
-XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
-{
-    XXH_free(statePtr);
-    return XXH_OK;
-}
-
-
-/*** Hash feed ***/
-
-XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
-{
-    XXH32_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
-    ZSTD_memset(&state, 0, sizeof(state)-4);   /* do not write into reserved, for future removal */
-    state.v1 = seed + PRIME32_1 + PRIME32_2;
-    state.v2 = seed + PRIME32_2;
-    state.v3 = seed + 0;
-    state.v4 = seed - PRIME32_1;
-    ZSTD_memcpy(statePtr, &state, sizeof(state));
-    return XXH_OK;
-}
-
-
-XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
-{
-    XXH64_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
-    ZSTD_memset(&state, 0, sizeof(state)-8);   /* do not write into reserved, for future removal */
-    state.v1 = seed + PRIME64_1 + PRIME64_2;
-    state.v2 = seed + PRIME64_2;
-    state.v3 = seed + 0;
-    state.v4 = seed - PRIME64_1;
-    ZSTD_memcpy(statePtr, &state, sizeof(state));
-    return XXH_OK;
-}
-
-
-FORCE_INLINE_TEMPLATE XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)
-{
-    const BYTE* p = (const BYTE*)input;
-    const BYTE* const bEnd = p + len;
-
-#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
-    if (input==NULL) return XXH_ERROR;
-#endif
-
-    state->total_len_32 += (unsigned)len;
-    state->large_len |= (len>=16) | (state->total_len_32>=16);
-
-    if (state->memsize + len < 16)  {   /* fill in tmp buffer */
-        XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
-        state->memsize += (unsigned)len;
-        return XXH_OK;
-    }
-
-    if (state->memsize) {   /* some data left from previous update */
-        XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
-        {   const U32* p32 = state->mem32;
-            state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;
-            state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;
-            state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;
-            state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); p32++;
-        }
-        p += 16-state->memsize;
-        state->memsize = 0;
-    }
-
-    if (p <= bEnd-16) {
-        const BYTE* const limit = bEnd - 16;
-        U32 v1 = state->v1;
-        U32 v2 = state->v2;
-        U32 v3 = state->v3;
-        U32 v4 = state->v4;
-
-        do {
-            v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;
-            v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;
-            v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;
-            v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;
-        } while (p<=limit);
-
-        state->v1 = v1;
-        state->v2 = v2;
-        state->v3 = v3;
-        state->v4 = v4;
-    }
-
-    if (p < bEnd) {
-        XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
-        state->memsize = (unsigned)(bEnd-p);
-    }
-
-    return XXH_OK;
-}
-
-XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)
-{
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
-    else
-        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
-}
-
-
-
-FORCE_INLINE_TEMPLATE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)
-{
-    const BYTE * p = (const BYTE*)state->mem32;
-    const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize;
-    U32 h32;
-
-    if (state->large_len) {
-        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
-    } else {
-        h32 = state->v3 /* == seed */ + PRIME32_5;
-    }
-
-    h32 += state->total_len_32;
-
-    while (p+4<=bEnd) {
-        h32 += XXH_readLE32(p, endian) * PRIME32_3;
-        h32  = XXH_rotl32(h32, 17) * PRIME32_4;
-        p+=4;
-    }
-
-    while (p<bEnd) {
-        h32 += (*p) * PRIME32_5;
-        h32  = XXH_rotl32(h32, 11) * PRIME32_1;
-        p++;
-    }
-
-    h32 ^= h32 >> 15;
-    h32 *= PRIME32_2;
-    h32 ^= h32 >> 13;
-    h32 *= PRIME32_3;
-    h32 ^= h32 >> 16;
-
-    return h32;
-}
-
-
-XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)
-{
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH32_digest_endian(state_in, XXH_littleEndian);
-    else
-        return XXH32_digest_endian(state_in, XXH_bigEndian);
-}
-
-
-
-/* **** XXH64 **** */
-
-FORCE_INLINE_TEMPLATE XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)
-{
-    const BYTE* p = (const BYTE*)input;
-    const BYTE* const bEnd = p + len;
-
-#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
-    if (input==NULL) return XXH_ERROR;
-#endif
-
-    state->total_len += len;
-
-    if (state->memsize + len < 32) {  /* fill in tmp buffer */
-        if (input != NULL) {
-            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
-        }
-        state->memsize += (U32)len;
-        return XXH_OK;
-    }
-
-    if (state->memsize) {   /* tmp buffer is full */
-        XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
-        state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));
-        state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));
-        state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));
-        state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));
-        p += 32-state->memsize;
-        state->memsize = 0;
-    }
-
-    if (p+32 <= bEnd) {
-        const BYTE* const limit = bEnd - 32;
-        U64 v1 = state->v1;
-        U64 v2 = state->v2;
-        U64 v3 = state->v3;
-        U64 v4 = state->v4;
-
-        do {
-            v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;
-            v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;
-            v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;
-            v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;
-        } while (p<=limit);
-
-        state->v1 = v1;
-        state->v2 = v2;
-        state->v3 = v3;
-        state->v4 = v4;
-    }
-
-    if (p < bEnd) {
-        XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
-        state->memsize = (unsigned)(bEnd-p);
-    }
-
-    return XXH_OK;
-}
-
-XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)
-{
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH64_update_endian(state_in, input, len, XXH_littleEndian);
-    else
-        return XXH64_update_endian(state_in, input, len, XXH_bigEndian);
-}
-
-
-
-FORCE_INLINE_TEMPLATE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)
-{
-    const BYTE * p = (const BYTE*)state->mem64;
-    const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize;
-    U64 h64;
-
-    if (state->total_len >= 32) {
-        U64 const v1 = state->v1;
-        U64 const v2 = state->v2;
-        U64 const v3 = state->v3;
-        U64 const v4 = state->v4;
-
-        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
-        h64 = XXH64_mergeRound(h64, v1);
-        h64 = XXH64_mergeRound(h64, v2);
-        h64 = XXH64_mergeRound(h64, v3);
-        h64 = XXH64_mergeRound(h64, v4);
-    } else {
-        h64  = state->v3 + PRIME64_5;
-    }
-
-    h64 += (U64) state->total_len;
-
-    while (p+8<=bEnd) {
-        U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian));
-        h64 ^= k1;
-        h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4;
-        p+=8;
-    }
-
-    if (p+4<=bEnd) {
-        h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1;
-        h64  = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
-        p+=4;
-    }
-
-    while (p<bEnd) {
-        h64 ^= (*p) * PRIME64_5;
-        h64  = XXH_rotl64(h64, 11) * PRIME64_1;
-        p++;
-    }
-
-    h64 ^= h64 >> 33;
-    h64 *= PRIME64_2;
-    h64 ^= h64 >> 29;
-    h64 *= PRIME64_3;
-    h64 ^= h64 >> 32;
-
-    return h64;
-}
-
-
-XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)
-{
-    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
-
-    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
-        return XXH64_digest_endian(state_in, XXH_littleEndian);
-    else
-        return XXH64_digest_endian(state_in, XXH_bigEndian);
-}
-
-
-/* **************************
-*  Canonical representation
-****************************/
-
-/*! Default XXH result types are basic unsigned 32 and 64 bits.
-*   The canonical representation follows human-readable write convention, aka big-endian (large digits first).
-*   These functions allow transformation of hash result into and from its canonical format.
-*   This way, hash values can be written into a file or buffer, and remain comparable across different systems and programs.
-*/
-
-XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
-{
-    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
-    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
-    ZSTD_memcpy(dst, &hash, sizeof(*dst));
-}
-
-XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
-{
-    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
-    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
-    ZSTD_memcpy(dst, &hash, sizeof(*dst));
-}
-
-XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
-{
-    return XXH_readBE32(src);
-}
-
-XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
-{
-    return XXH_readBE64(src);
-}
diff --git a/Utilities/cmzstd/lib/common/xxhash.h b/Utilities/cmzstd/lib/common/xxhash.h
index 16c1f16..b8b7329 100644
--- a/Utilities/cmzstd/lib/common/xxhash.h
+++ b/Utilities/cmzstd/lib/common/xxhash.h
@@ -1,20 +1,36 @@
 /*
- * xxHash - Extremely Fast Hash algorithm
- * Header File
- * Copyright (c) Yann Collet, Facebook, Inc.
+ *  xxHash - Fast Hash algorithm
+ *  Copyright (c) Meta Platforms, Inc. and affiliates.
  *
- * You can contact the author at :
- * - xxHash source repository : https://github.com/Cyan4973/xxHash
- * 
+ *  You can contact the author at :
+ *  - xxHash homepage: https://cyan4973.github.io/xxHash/
+ *  - xxHash source repository : https://github.com/Cyan4973/xxHash
+ *
  * This source code is licensed under both the BSD-style license (found in the
  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
  * in the COPYING file in the root directory of this source tree).
  * You may select, at your option, one of the above-listed licenses.
 */
 
-/* Notice extracted from xxHash homepage :
 
-xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+#ifndef XXH_NO_XXH3
+# define XXH_NO_XXH3
+#endif
+
+#ifndef XXH_NAMESPACE
+# define XXH_NAMESPACE ZSTD_
+#endif
+
+/*!
+ * @mainpage xxHash
+ *
+ * @file xxhash.h
+ * xxHash prototypes and implementation
+ */
+/* TODO: update */
+/* Notice extracted from xxHash homepage:
+
+xxHash is an extremely fast hash algorithm, running at RAM speed limits.
 It also successfully passes all tests from the SMHasher suite.
 
 Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
@@ -22,7 +38,7 @@
 Name            Speed       Q.Score   Author
 xxHash          5.4 GB/s     10
 CrapWow         3.2 GB/s      2       Andrew
-MumurHash 3a    2.7 GB/s     10       Austin Appleby
+MurmurHash 3a   2.7 GB/s     10       Austin Appleby
 SpookyHash      2.0 GB/s     10       Bob Jenkins
 SBox            1.4 GB/s      9       Bret Mulvey
 Lookup3         1.2 GB/s      9       Bob Jenkins
@@ -37,8 +53,13 @@
 It depends on successfully passing SMHasher test set.
 10 is a perfect score.
 
-A 64-bits version, named XXH64, is available since r35.
-It offers much better speed, but for 64-bits applications only.
+Note: SMHasher's CRC32 implementation is not the fastest one.
+Other speed-oriented implementations can be faster,
+especially in combination with PCLMUL instruction:
+https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735
+
+A 64-bit version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bit applications only.
 Name     Speed on 64 bits    Speed on 32 bits
 XXH64       13.8 GB/s            1.9 GB/s
 XXH32        6.8 GB/s            6.0 GB/s
@@ -48,33 +69,34 @@
 extern "C" {
 #endif
 
-#ifndef XXHASH_H_5627135585666179
-#define XXHASH_H_5627135585666179 1
-
-
 /* ****************************
-*  Definitions
-******************************/
-#include "zstd_deps.h"
-typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
-
-
-/* ****************************
-*  API modifier
-******************************/
-/** XXH_PRIVATE_API
-*   This is useful if you want to include xxhash functions in `static` mode
-*   in order to inline them, and remove their symbol from the public list.
-*   Methodology :
-*     #define XXH_PRIVATE_API
-*     #include "xxhash.h"
-*   `xxhash.c` is automatically included.
-*   It's not useful to compile and link it as a separate module anymore.
-*/
-#ifdef XXH_PRIVATE_API
-#  ifndef XXH_STATIC_LINKING_ONLY
-#    define XXH_STATIC_LINKING_ONLY
-#  endif
+ *  INLINE mode
+ ******************************/
+/*!
+ * XXH_INLINE_ALL (and XXH_PRIVATE_API)
+ * Use these build macros to inline xxhash into the target unit.
+ * Inlining improves performance on small inputs, especially when the length is
+ * expressed as a compile-time constant:
+ *
+ *      https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html
+ *
+ * It also keeps xxHash symbols private to the unit, so they are not exported.
+ *
+ * Usage:
+ *     #define XXH_INLINE_ALL
+ *     #include "xxhash.h"
+ *
+ * Do not compile and link xxhash.o as a separate object, as it is not useful.
+ */
+#if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \
+    && !defined(XXH_INLINE_ALL_31684351384)
+   /* this section should be traversed only once */
+#  define XXH_INLINE_ALL_31684351384
+   /* give access to the advanced API, required to compile implementations */
+#  undef XXH_STATIC_LINKING_ONLY   /* avoid macro redef */
+#  define XXH_STATIC_LINKING_ONLY
+   /* make all functions private */
+#  undef XXH_PUBLIC_API
 #  if defined(__GNUC__)
 #    define XXH_PUBLIC_API static __inline __attribute__((unused))
 #  elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
@@ -82,45 +104,205 @@
 #  elif defined(_MSC_VER)
 #    define XXH_PUBLIC_API static __inline
 #  else
-#    define XXH_PUBLIC_API static   /* this version may generate warnings for unused static functions; disable the relevant warning */
+     /* note: this version may generate warnings for unused static functions */
+#    define XXH_PUBLIC_API static
 #  endif
-#else
-#  define XXH_PUBLIC_API   /* do nothing */
-#endif /* XXH_PRIVATE_API */
 
-/*!XXH_NAMESPACE, aka Namespace Emulation :
+   /*
+    * This part deals with the special case where a unit wants to inline xxHash,
+    * but "xxhash.h" has previously been included without XXH_INLINE_ALL,
+    * such as part of some previously included *.h header file.
+    * Without further action, the new include would just be ignored,
+    * and functions would effectively _not_ be inlined (silent failure).
+    * The following macros solve this situation by prefixing all inlined names,
+    * avoiding naming collision with previous inclusions.
+    */
+   /* Before that, we unconditionally #undef all symbols,
+    * in case they were already defined with XXH_NAMESPACE.
+    * They will then be redefined for XXH_INLINE_ALL
+    */
+#  undef XXH_versionNumber
+    /* XXH32 */
+#  undef XXH32
+#  undef XXH32_createState
+#  undef XXH32_freeState
+#  undef XXH32_reset
+#  undef XXH32_update
+#  undef XXH32_digest
+#  undef XXH32_copyState
+#  undef XXH32_canonicalFromHash
+#  undef XXH32_hashFromCanonical
+    /* XXH64 */
+#  undef XXH64
+#  undef XXH64_createState
+#  undef XXH64_freeState
+#  undef XXH64_reset
+#  undef XXH64_update
+#  undef XXH64_digest
+#  undef XXH64_copyState
+#  undef XXH64_canonicalFromHash
+#  undef XXH64_hashFromCanonical
+    /* XXH3_64bits */
+#  undef XXH3_64bits
+#  undef XXH3_64bits_withSecret
+#  undef XXH3_64bits_withSeed
+#  undef XXH3_64bits_withSecretandSeed
+#  undef XXH3_createState
+#  undef XXH3_freeState
+#  undef XXH3_copyState
+#  undef XXH3_64bits_reset
+#  undef XXH3_64bits_reset_withSeed
+#  undef XXH3_64bits_reset_withSecret
+#  undef XXH3_64bits_update
+#  undef XXH3_64bits_digest
+#  undef XXH3_generateSecret
+    /* XXH3_128bits */
+#  undef XXH128
+#  undef XXH3_128bits
+#  undef XXH3_128bits_withSeed
+#  undef XXH3_128bits_withSecret
+#  undef XXH3_128bits_reset
+#  undef XXH3_128bits_reset_withSeed
+#  undef XXH3_128bits_reset_withSecret
+#  undef XXH3_128bits_reset_withSecretandSeed
+#  undef XXH3_128bits_update
+#  undef XXH3_128bits_digest
+#  undef XXH128_isEqual
+#  undef XXH128_cmp
+#  undef XXH128_canonicalFromHash
+#  undef XXH128_hashFromCanonical
+    /* Finally, free the namespace itself */
+#  undef XXH_NAMESPACE
 
-If you want to include _and expose_ xxHash functions from within your own library,
-but also want to avoid symbol collisions with another library which also includes xxHash,
+    /* employ the namespace for XXH_INLINE_ALL */
+#  define XXH_NAMESPACE XXH_INLINE_
+   /*
+    * Some identifiers (enums, type names) are not symbols,
+    * but they must nonetheless be renamed to avoid redeclaration.
+    * Alternative solution: do not redeclare them.
+    * However, this requires some #ifdefs, and has a more dispersed impact.
+    * Meanwhile, renaming can be achieved in a single place.
+    */
+#  define XXH_IPREF(Id)   XXH_NAMESPACE ## Id
+#  define XXH_OK XXH_IPREF(XXH_OK)
+#  define XXH_ERROR XXH_IPREF(XXH_ERROR)
+#  define XXH_errorcode XXH_IPREF(XXH_errorcode)
+#  define XXH32_canonical_t  XXH_IPREF(XXH32_canonical_t)
+#  define XXH64_canonical_t  XXH_IPREF(XXH64_canonical_t)
+#  define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t)
+#  define XXH32_state_s XXH_IPREF(XXH32_state_s)
+#  define XXH32_state_t XXH_IPREF(XXH32_state_t)
+#  define XXH64_state_s XXH_IPREF(XXH64_state_s)
+#  define XXH64_state_t XXH_IPREF(XXH64_state_t)
+#  define XXH3_state_s  XXH_IPREF(XXH3_state_s)
+#  define XXH3_state_t  XXH_IPREF(XXH3_state_t)
+#  define XXH128_hash_t XXH_IPREF(XXH128_hash_t)
+   /* Ensure the header is parsed again, even if it was previously included */
+#  undef XXHASH_H_5627135585666179
+#  undef XXHASH_H_STATIC_13879238742
+#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */
 
-you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
-with the value of XXH_NAMESPACE (so avoid to keep it NULL and avoid numeric values).
 
-Note that no change is required within the calling program as long as it includes `xxhash.h` :
-regular symbol name will be automatically translated by this header.
-*/
+
+/* ****************************************************************
+ *  Stable API
+ *****************************************************************/
+#ifndef XXHASH_H_5627135585666179
+#define XXHASH_H_5627135585666179 1
+
+
+/*!
+ * @defgroup public Public API
+ * Contains details on the public xxHash functions.
+ * @{
+ */
+/* specific declaration modes for Windows */
+#if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API)
+#  if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))
+#    ifdef XXH_EXPORT
+#      define XXH_PUBLIC_API __declspec(dllexport)
+#    elif XXH_IMPORT
+#      define XXH_PUBLIC_API __declspec(dllimport)
+#    endif
+#  else
+#    define XXH_PUBLIC_API   /* do nothing */
+#  endif
+#endif
+
+#ifdef XXH_DOXYGEN
+/*!
+ * @brief Emulate a namespace by transparently prefixing all symbols.
+ *
+ * If you want to include _and expose_ xxHash functions from within your own
+ * library, but also want to avoid symbol collisions with other libraries which
+ * may also include xxHash, you can use XXH_NAMESPACE to automatically prefix
+ * any public symbol from xxhash library with the value of XXH_NAMESPACE
+ * (therefore, avoid empty or numeric values).
+ *
+ * Note that no change is required within the calling program as long as it
+ * includes `xxhash.h`: Regular symbol names will be automatically translated
+ * by this header.
+ */
+#  define XXH_NAMESPACE /* YOUR NAME HERE */
+#  undef XXH_NAMESPACE
+#endif
+
 #ifdef XXH_NAMESPACE
 #  define XXH_CAT(A,B) A##B
 #  define XXH_NAME2(A,B) XXH_CAT(A,B)
-#  define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
-#  define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
 #  define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
+/* XXH32 */
+#  define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
 #  define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
-#  define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
 #  define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
-#  define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
 #  define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
-#  define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
 #  define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
-#  define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
 #  define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
-#  define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
 #  define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
-#  define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
 #  define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
-#  define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
 #  define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
+/* XXH64 */
+#  define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+#  define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+#  define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+#  define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+#  define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+#  define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+#  define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
+#  define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
 #  define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
+/* XXH3_64bits */
+#  define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits)
+#  define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret)
+#  define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed)
+#  define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed)
+#  define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState)
+#  define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState)
+#  define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState)
+#  define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset)
+#  define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed)
+#  define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret)
+#  define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed)
+#  define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update)
+#  define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest)
+#  define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret)
+#  define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed)
+/* XXH3_128bits */
+#  define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128)
+#  define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits)
+#  define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed)
+#  define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret)
+#  define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed)
+#  define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset)
+#  define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed)
+#  define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret)
+#  define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed)
+#  define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update)
+#  define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest)
+#  define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual)
+#  define XXH128_cmp     XXH_NAME2(XXH_NAMESPACE, XXH128_cmp)
+#  define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash)
+#  define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical)
 #endif
 
 
@@ -128,156 +310,5375 @@
 *  Version
 ***************************************/
 #define XXH_VERSION_MAJOR    0
-#define XXH_VERSION_MINOR    6
-#define XXH_VERSION_RELEASE  2
+#define XXH_VERSION_MINOR    8
+#define XXH_VERSION_RELEASE  1
 #define XXH_VERSION_NUMBER  (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
+
+/*!
+ * @brief Obtains the xxHash version.
+ *
+ * This is mostly useful when xxHash is compiled as a shared library,
+ * since the returned value comes from the library, as opposed to header file.
+ *
+ * @return `XXH_VERSION_NUMBER` of the invoked library.
+ */
 XXH_PUBLIC_API unsigned XXH_versionNumber (void);
 
 
 /* ****************************
-*  Simple Hash Functions
+*  Common basic types
 ******************************/
-typedef unsigned int       XXH32_hash_t;
-typedef unsigned long long XXH64_hash_t;
+#include <stddef.h>   /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
 
-XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
-XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
+
+/*-**********************************************************************
+*  32-bit hash
+************************************************************************/
+#if defined(XXH_DOXYGEN) /* Don't show <stdint.h> include */
+/*!
+ * @brief An unsigned 32-bit integer.
+ *
+ * Not necessarily defined to `uint32_t` but functionally equivalent.
+ */
+typedef uint32_t XXH32_hash_t;
+
+#elif !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+#   include <stdint.h>
+    typedef uint32_t XXH32_hash_t;
+
+#else
+#   include <limits.h>
+#   if UINT_MAX == 0xFFFFFFFFUL
+      typedef unsigned int XXH32_hash_t;
+#   else
+#     if ULONG_MAX == 0xFFFFFFFFUL
+        typedef unsigned long XXH32_hash_t;
+#     else
+#       error "unsupported platform: need a 32-bit type"
+#     endif
+#   endif
+#endif
 
 /*!
-XXH32() :
-    Calculate the 32-bits hash of sequence "length" bytes stored at memory address "input".
-    The memory between input & input+length must be valid (allocated and read-accessible).
-    "seed" can be used to alter the result predictably.
-    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s
-XXH64() :
-    Calculate the 64-bits hash of sequence of length "len" stored at memory address "input".
-    "seed" can be used to alter the result predictably.
-    This function runs 2x faster on 64-bits systems, but slower on 32-bits systems (see benchmark).
-*/
+ * @}
+ *
+ * @defgroup xxh32_family XXH32 family
+ * @ingroup public
+ * Contains functions used in the classic 32-bit xxHash algorithm.
+ *
+ * @note
+ *   XXH32 is useful for older platforms, with no or poor 64-bit performance.
+ *   Note that @ref xxh3_family provides competitive speed
+ *   for both 32-bit and 64-bit systems, and offers true 64/128 bit hash results.
+ *
+ * @see @ref xxh64_family, @ref xxh3_family : Other xxHash families
+ * @see @ref xxh32_impl for implementation details
+ * @{
+ */
 
+/*!
+ * @brief Calculates the 32-bit hash of @p input using xxHash32.
+ *
+ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s
+ *
+ * @param input The block of data to be hashed, at least @p length bytes in size.
+ * @param length The length of @p input, in bytes.
+ * @param seed The 32-bit seed to alter the hash's output predictably.
+ *
+ * @pre
+ *   The memory between @p input and @p input + @p length must be valid,
+ *   readable, contiguous memory. However, if @p length is `0`, @p input may be
+ *   `NULL`. In C++, this also must be *TriviallyCopyable*.
+ *
+ * @return The calculated 32-bit hash value.
+ *
+ * @see
+ *    XXH64(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128():
+ *    Direct equivalents for the other variants of xxHash.
+ * @see
+ *    XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version.
+ */
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed);
 
-/* ****************************
-*  Streaming Hash Functions
-******************************/
-typedef struct XXH32_state_s XXH32_state_t;   /* incomplete type */
-typedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */
+/*!
+ * Streaming functions generate the xxHash value from an incremental input.
+ * This method is slower than single-call functions, due to state management.
+ * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.
+ *
+ * An XXH state must first be allocated using `XXH*_createState()`.
+ *
+ * Start a new hash by initializing the state with a seed using `XXH*_reset()`.
+ *
+ * Then, feed the hash state by calling `XXH*_update()` as many times as necessary.
+ *
+ * The function returns an error code, with 0 meaning OK, and any other value
+ * meaning there is an error.
+ *
+ * Finally, a hash value can be produced anytime, by using `XXH*_digest()`.
+ * This function returns the nn-bits hash as an int or long long.
+ *
+ * It's still possible to continue inserting input into the hash state after a
+ * digest, and generate new hash values later on by invoking `XXH*_digest()`.
+ *
+ * When done, release the state using `XXH*_freeState()`.
+ *
+ * Example code for incrementally hashing a file:
+ * @code{.c}
+ *    #include <stdio.h>
+ *    #include <xxhash.h>
+ *    #define BUFFER_SIZE 256
+ *
+ *    // Note: XXH64 and XXH3 use the same interface.
+ *    XXH32_hash_t
+ *    hashFile(FILE* stream)
+ *    {
+ *        XXH32_state_t* state;
+ *        unsigned char buf[BUFFER_SIZE];
+ *        size_t amt;
+ *        XXH32_hash_t hash;
+ *
+ *        state = XXH32_createState();       // Create a state
+ *        assert(state != NULL);             // Error check here
+ *        XXH32_reset(state, 0xbaad5eed);    // Reset state with our seed
+ *        while ((amt = fread(buf, 1, sizeof(buf), stream)) != 0) {
+ *            XXH32_update(state, buf, amt); // Hash the file in chunks
+ *        }
+ *        hash = XXH32_digest(state);        // Finalize the hash
+ *        XXH32_freeState(state);            // Clean up
+ *        return hash;
+ *    }
+ * @endcode
+ */
 
-/*! State allocation, compatible with dynamic libraries */
+/*!
+ * @typedef struct XXH32_state_s XXH32_state_t
+ * @brief The opaque state struct for the XXH32 streaming API.
+ *
+ * @see XXH32_state_s for details.
+ */
+typedef struct XXH32_state_s XXH32_state_t;
 
+/*!
+ * @brief Allocates an @ref XXH32_state_t.
+ *
+ * Must be freed with XXH32_freeState().
+ * @return An allocated XXH32_state_t on success, `NULL` on failure.
+ */
 XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
+/*!
+ * @brief Frees an @ref XXH32_state_t.
+ *
+ * Must be allocated with XXH32_createState().
+ * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState().
+ * @return XXH_OK.
+ */
 XXH_PUBLIC_API XXH_errorcode  XXH32_freeState(XXH32_state_t* statePtr);
+/*!
+ * @brief Copies one @ref XXH32_state_t to another.
+ *
+ * @param dst_state The state to copy to.
+ * @param src_state The state to copy from.
+ * @pre
+ *   @p dst_state and @p src_state must not be `NULL` and must not overlap.
+ */
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
 
-XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
-XXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);
+/*!
+ * @brief Resets an @ref XXH32_state_t to begin a new hash.
+ *
+ * This function resets and seeds a state. Call it before @ref XXH32_update().
+ *
+ * @param statePtr The state struct to reset.
+ * @param seed The 32-bit seed to alter the hash result predictably.
+ *
+ * @pre
+ *   @p statePtr must not be `NULL`.
+ *
+ * @return @ref XXH_OK on success, @ref XXH_ERROR on failure.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, XXH32_hash_t seed);
 
-
-/* hash streaming */
-
-XXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, unsigned int seed);
+/*!
+ * @brief Consumes a block of @p input to an @ref XXH32_state_t.
+ *
+ * Call this to incrementally consume blocks of data.
+ *
+ * @param statePtr The state struct to update.
+ * @param input The block of data to be hashed, at least @p length bytes in size.
+ * @param length The length of @p input, in bytes.
+ *
+ * @pre
+ *   @p statePtr must not be `NULL`.
+ * @pre
+ *   The memory between @p input and @p input + @p length must be valid,
+ *   readable, contiguous memory. However, if @p length is `0`, @p input may be
+ *   `NULL`. In C++, this also must be *TriviallyCopyable*.
+ *
+ * @return @ref XXH_OK on success, @ref XXH_ERROR on failure.
+ */
 XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+
+/*!
+ * @brief Returns the calculated hash value from an @ref XXH32_state_t.
+ *
+ * @note
+ *   Calling XXH32_digest() will not affect @p statePtr, so you can update,
+ *   digest, and update again.
+ *
+ * @param statePtr The state struct to calculate the hash from.
+ *
+ * @pre
+ *  @p statePtr must not be `NULL`.
+ *
+ * @return The calculated xxHash32 value from that state.
+ */
 XXH_PUBLIC_API XXH32_hash_t  XXH32_digest (const XXH32_state_t* statePtr);
 
-XXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH64_state_t* statePtr, unsigned long long seed);
+/*******   Canonical representation   *******/
+
+/*
+ * The default return values from XXH functions are unsigned 32 and 64 bit
+ * integers.
+ * This the simplest and fastest format for further post-processing.
+ *
+ * However, this leaves open the question of what is the order on the byte level,
+ * since little and big endian conventions will store the same number differently.
+ *
+ * The canonical representation settles this issue by mandating big-endian
+ * convention, the same convention as human-readable numbers (large digits first).
+ *
+ * When writing hash values to storage, sending them over a network, or printing
+ * them, it's highly recommended to use the canonical representation to ensure
+ * portability across a wider range of systems, present and future.
+ *
+ * The following functions allow transformation of hash values to and from
+ * canonical format.
+ */
+
+/*!
+ * @brief Canonical (big endian) representation of @ref XXH32_hash_t.
+ */
+typedef struct {
+    unsigned char digest[4]; /*!< Hash bytes, big endian */
+} XXH32_canonical_t;
+
+/*!
+ * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t.
+ *
+ * @param dst The @ref XXH32_canonical_t pointer to be stored to.
+ * @param hash The @ref XXH32_hash_t to be converted.
+ *
+ * @pre
+ *   @p dst must not be `NULL`.
+ */
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+
+/*!
+ * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t.
+ *
+ * @param src The @ref XXH32_canonical_t to convert.
+ *
+ * @pre
+ *   @p src must not be `NULL`.
+ *
+ * @return The converted hash.
+ */
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
+
+
+#ifdef __has_attribute
+# define XXH_HAS_ATTRIBUTE(x) __has_attribute(x)
+#else
+# define XXH_HAS_ATTRIBUTE(x) 0
+#endif
+
+/* C-language Attributes are added in C23. */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201710L) && defined(__has_c_attribute)
+# define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x)
+#else
+# define XXH_HAS_C_ATTRIBUTE(x) 0
+#endif
+
+#if defined(__cplusplus) && defined(__has_cpp_attribute)
+# define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+# define XXH_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+/*
+Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute
+introduced in CPP17 and C23.
+CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough
+C23   : https://en.cppreference.com/w/c/language/attributes/fallthrough
+*/
+#if XXH_HAS_C_ATTRIBUTE(x)
+# define XXH_FALLTHROUGH [[fallthrough]]
+#elif XXH_HAS_CPP_ATTRIBUTE(x)
+# define XXH_FALLTHROUGH [[fallthrough]]
+#elif XXH_HAS_ATTRIBUTE(__fallthrough__)
+# define XXH_FALLTHROUGH __attribute__ ((fallthrough))
+#else
+# define XXH_FALLTHROUGH
+#endif
+
+/*!
+ * @}
+ * @ingroup public
+ * @{
+ */
+
+#ifndef XXH_NO_LONG_LONG
+/*-**********************************************************************
+*  64-bit hash
+************************************************************************/
+#if defined(XXH_DOXYGEN) /* don't include <stdint.h> */
+/*!
+ * @brief An unsigned 64-bit integer.
+ *
+ * Not necessarily defined to `uint64_t` but functionally equivalent.
+ */
+typedef uint64_t XXH64_hash_t;
+#elif !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+#  include <stdint.h>
+   typedef uint64_t XXH64_hash_t;
+#else
+#  include <limits.h>
+#  if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL
+     /* LP64 ABI says uint64_t is unsigned long */
+     typedef unsigned long XXH64_hash_t;
+#  else
+     /* the following type must have a width of 64-bit */
+     typedef unsigned long long XXH64_hash_t;
+#  endif
+#endif
+
+/*!
+ * @}
+ *
+ * @defgroup xxh64_family XXH64 family
+ * @ingroup public
+ * @{
+ * Contains functions used in the classic 64-bit xxHash algorithm.
+ *
+ * @note
+ *   XXH3 provides competitive speed for both 32-bit and 64-bit systems,
+ *   and offers true 64/128 bit hash results.
+ *   It provides better speed for systems with vector processing capabilities.
+ */
+
+
+/*!
+ * @brief Calculates the 64-bit hash of @p input using xxHash64.
+ *
+ * This function usually runs faster on 64-bit systems, but slower on 32-bit
+ * systems (see benchmark).
+ *
+ * @param input The block of data to be hashed, at least @p length bytes in size.
+ * @param length The length of @p input, in bytes.
+ * @param seed The 64-bit seed to alter the hash's output predictably.
+ *
+ * @pre
+ *   The memory between @p input and @p input + @p length must be valid,
+ *   readable, contiguous memory. However, if @p length is `0`, @p input may be
+ *   `NULL`. In C++, this also must be *TriviallyCopyable*.
+ *
+ * @return The calculated 64-bit hash.
+ *
+ * @see
+ *    XXH32(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128():
+ *    Direct equivalents for the other variants of xxHash.
+ * @see
+ *    XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version.
+ */
+XXH_PUBLIC_API XXH64_hash_t XXH64(const void* input, size_t length, XXH64_hash_t seed);
+
+/*******   Streaming   *******/
+/*!
+ * @brief The opaque state struct for the XXH64 streaming API.
+ *
+ * @see XXH64_state_s for details.
+ */
+typedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
+XXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH64_state_t* statePtr, XXH64_hash_t seed);
 XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
 XXH_PUBLIC_API XXH64_hash_t  XXH64_digest (const XXH64_state_t* statePtr);
 
-/*
-These functions generate the xxHash of an input provided in multiple segments.
-Note that, for small input, they are slower than single-call functions, due to state management.
-For small input, prefer `XXH32()` and `XXH64()` .
-
-XXH state must first be allocated, using XXH*_createState() .
-
-Start a new hash by initializing state with a seed, using XXH*_reset().
-
-Then, feed the hash state by calling XXH*_update() as many times as necessary.
-Obviously, input must be allocated and read accessible.
-The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
-
-Finally, a hash value can be produced anytime, by using XXH*_digest().
-This function returns the nn-bits hash as an int or long long.
-
-It's still possible to continue inserting input into the hash state after a digest,
-and generate some new hashes later on, by calling again XXH*_digest().
-
-When done, free XXH state space if it was allocated dynamically.
-*/
-
-
-/* **************************
-*  Utils
-****************************/
-#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))   /* ! C99 */
-#  define restrict   /* disable restrict */
-#endif
-
-XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dst_state, const XXH32_state_t* restrict src_state);
-XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dst_state, const XXH64_state_t* restrict src_state);
-
-
-/* **************************
-*  Canonical representation
-****************************/
-/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
-*  The canonical representation uses human-readable write convention, aka big-endian (large digits first).
-*  These functions allow transformation of hash result into and from its canonical format.
-*  This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
-*/
-typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
-typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
-
-XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+/*******   Canonical representation   *******/
+typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t;
 XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
-
-XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
 XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
 
+#ifndef XXH_NO_XXH3
+/*!
+ * @}
+ * ************************************************************************
+ * @defgroup xxh3_family XXH3 family
+ * @ingroup public
+ * @{
+ *
+ * XXH3 is a more recent hash algorithm featuring:
+ *  - Improved speed for both small and large inputs
+ *  - True 64-bit and 128-bit outputs
+ *  - SIMD acceleration
+ *  - Improved 32-bit viability
+ *
+ * Speed analysis methodology is explained here:
+ *
+ *    https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html
+ *
+ * Compared to XXH64, expect XXH3 to run approximately
+ * ~2x faster on large inputs and >3x faster on small ones,
+ * exact differences vary depending on platform.
+ *
+ * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic,
+ * but does not require it.
+ * Any 32-bit and 64-bit targets that can run XXH32 smoothly
+ * can run XXH3 at competitive speeds, even without vector support.
+ * Further details are explained in the implementation.
+ *
+ * Optimized implementations are provided for AVX512, AVX2, SSE2, NEON, POWER8,
+ * ZVector and scalar targets. This can be controlled via the XXH_VECTOR macro.
+ *
+ * XXH3 implementation is portable:
+ * it has a generic C90 formulation that can be compiled on any platform,
+ * all implementations generage exactly the same hash value on all platforms.
+ * Starting from v0.8.0, it's also labelled "stable", meaning that
+ * any future version will also generate the same hash value.
+ *
+ * XXH3 offers 2 variants, _64bits and _128bits.
+ *
+ * When only 64 bits are needed, prefer invoking the _64bits variant, as it
+ * reduces the amount of mixing, resulting in faster speed on small inputs.
+ * It's also generally simpler to manipulate a scalar return type than a struct.
+ *
+ * The API supports one-shot hashing, streaming mode, and custom secrets.
+ */
+
+/*-**********************************************************************
+*  XXH3 64-bit variant
+************************************************************************/
+
+/* XXH3_64bits():
+ * default 64-bit variant, using default secret and default seed of 0.
+ * It's the fastest variant. */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len);
+
+/*
+ * XXH3_64bits_withSeed():
+ * This variant generates a custom secret on the fly
+ * based on default secret altered using the `seed` value.
+ * While this operation is decently fast, note that it's not completely free.
+ * Note: seed==0 produces the same results as XXH3_64bits().
+ */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed);
+
+/*!
+ * The bare minimum size for a custom secret.
+ *
+ * @see
+ *  XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(),
+ *  XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret().
+ */
+#define XXH3_SECRET_SIZE_MIN 136
+
+/*
+ * XXH3_64bits_withSecret():
+ * It's possible to provide any blob of bytes as a "secret" to generate the hash.
+ * This makes it more difficult for an external actor to prepare an intentional collision.
+ * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN).
+ * However, the quality of the secret impacts the dispersion of the hash algorithm.
+ * Therefore, the secret _must_ look like a bunch of random bytes.
+ * Avoid "trivial" or structured data such as repeated sequences or a text document.
+ * Whenever in doubt about the "randomness" of the blob of bytes,
+ * consider employing "XXH3_generateSecret()" instead (see below).
+ * It will generate a proper high entropy secret derived from the blob of bytes.
+ * Another advantage of using XXH3_generateSecret() is that
+ * it guarantees that all bits within the initial blob of bytes
+ * will impact every bit of the output.
+ * This is not necessarily the case when using the blob of bytes directly
+ * because, when hashing _small_ inputs, only a portion of the secret is employed.
+ */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+
+/*******   Streaming   *******/
+/*
+ * Streaming requires state maintenance.
+ * This operation costs memory and CPU.
+ * As a consequence, streaming is slower than one-shot hashing.
+ * For better performance, prefer one-shot functions whenever applicable.
+ */
+
+/*!
+ * @brief The state struct for the XXH3 streaming API.
+ *
+ * @see XXH3_state_s for details.
+ */
+typedef struct XXH3_state_s XXH3_state_t;
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr);
+XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state);
+
+/*
+ * XXH3_64bits_reset():
+ * Initialize with default parameters.
+ * digest will be equivalent to `XXH3_64bits()`.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr);
+/*
+ * XXH3_64bits_reset_withSeed():
+ * Generate a custom secret from `seed`, and store it into `statePtr`.
+ * digest will be equivalent to `XXH3_64bits_withSeed()`.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+/*
+ * XXH3_64bits_reset_withSecret():
+ * `secret` is referenced, it _must outlive_ the hash streaming session.
+ * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`,
+ * and the quality of produced hash values depends on secret's entropy
+ * (secret's content should look like a bunch of random bytes).
+ * When in doubt about the randomness of a candidate `secret`,
+ * consider employing `XXH3_generateSecret()` instead (see below).
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t  XXH3_64bits_digest (const XXH3_state_t* statePtr);
+
+/* note : canonical representation of XXH3 is the same as XXH64
+ * since they both produce XXH64_hash_t values */
+
+
+/*-**********************************************************************
+*  XXH3 128-bit variant
+************************************************************************/
+
+/*!
+ * @brief The return value from 128-bit hashes.
+ *
+ * Stored in little endian order, although the fields themselves are in native
+ * endianness.
+ */
+typedef struct {
+    XXH64_hash_t low64;   /*!< `value & 0xFFFFFFFFFFFFFFFF` */
+    XXH64_hash_t high64;  /*!< `value >> 64` */
+} XXH128_hash_t;
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+/*******   Streaming   *******/
+/*
+ * Streaming requires state maintenance.
+ * This operation costs memory and CPU.
+ * As a consequence, streaming is slower than one-shot hashing.
+ * For better performance, prefer one-shot functions whenever applicable.
+ *
+ * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits().
+ * Use already declared XXH3_createState() and XXH3_freeState().
+ *
+ * All reset and streaming functions have same meaning as their 64-bit counterpart.
+ */
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr);
+
+/* Following helper functions make it possible to compare XXH128_hast_t values.
+ * Since XXH128_hash_t is a structure, this capability is not offered by the language.
+ * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */
+
+/*!
+ * XXH128_isEqual():
+ * Return: 1 if `h1` and `h2` are equal, 0 if they are not.
+ */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);
+
+/*!
+ * XXH128_cmp():
+ *
+ * This comparator is compatible with stdlib's `qsort()`/`bsearch()`.
+ *
+ * return: >0 if *h128_1  > *h128_2
+ *         =0 if *h128_1 == *h128_2
+ *         <0 if *h128_1  < *h128_2
+ */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2);
+
+
+/*******   Canonical representation   *******/
+typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t;
+XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash);
+XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src);
+
+
+#endif  /* !XXH_NO_XXH3 */
+#endif  /* XXH_NO_LONG_LONG */
+
+/*!
+ * @}
+ */
 #endif /* XXHASH_H_5627135585666179 */
 
 
 
-/* ================================================================================================
-   This section contains definitions which are not guaranteed to remain stable.
-   They may change in future versions, becoming incompatible with a different version of the library.
-   They shall only be used with static linking.
-   Never use these definitions in association with dynamic linking !
-=================================================================================================== */
-#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXH_STATIC_H_3543687687345)
-#define XXH_STATIC_H_3543687687345
+#if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742)
+#define XXHASH_H_STATIC_13879238742
+/* ****************************************************************************
+ * This section contains declarations which are not guaranteed to remain stable.
+ * They may change in future versions, becoming incompatible with a different
+ * version of the library.
+ * These declarations should only be used with static linking.
+ * Never use them in association with dynamic linking!
+ ***************************************************************************** */
 
-/* These definitions are only meant to allow allocation of XXH state
-   statically, on stack, or in a struct for example.
-   Do not use members directly. */
+/*
+ * These definitions are only present to allow static allocation
+ * of XXH states, on stack or in a struct, for example.
+ * Never **ever** access their members directly.
+ */
 
-   struct XXH32_state_s {
-       unsigned total_len_32;
-       unsigned large_len;
-       unsigned v1;
-       unsigned v2;
-       unsigned v3;
-       unsigned v4;
-       unsigned mem32[4];   /* buffer defined as U32 for alignment */
-       unsigned memsize;
-       unsigned reserved;   /* never read nor write, will be removed in a future version */
-   };   /* typedef'd to XXH32_state_t */
-
-   struct XXH64_state_s {
-       unsigned long long total_len;
-       unsigned long long v1;
-       unsigned long long v2;
-       unsigned long long v3;
-       unsigned long long v4;
-       unsigned long long mem64[4];   /* buffer defined as U64 for alignment */
-       unsigned memsize;
-       unsigned reserved[2];          /* never read nor write, will be removed in a future version */
-   };   /* typedef'd to XXH64_state_t */
+/*!
+ * @internal
+ * @brief Structure for XXH32 streaming API.
+ *
+ * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
+ * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is
+ * an opaque type. This allows fields to safely be changed.
+ *
+ * Typedef'd to @ref XXH32_state_t.
+ * Do not access the members of this struct directly.
+ * @see XXH64_state_s, XXH3_state_s
+ */
+struct XXH32_state_s {
+   XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */
+   XXH32_hash_t large_len;    /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
+   XXH32_hash_t v[4];         /*!< Accumulator lanes */
+   XXH32_hash_t mem32[4];     /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
+   XXH32_hash_t memsize;      /*!< Amount of data in @ref mem32 */
+   XXH32_hash_t reserved;     /*!< Reserved field. Do not read nor write to it. */
+};   /* typedef'd to XXH32_state_t */
 
 
-#  ifdef XXH_PRIVATE_API
-#    include "xxhash.c"   /* include xxhash functions as `static`, for inlining */
+#ifndef XXH_NO_LONG_LONG  /* defined when there is no 64-bit support */
+
+/*!
+ * @internal
+ * @brief Structure for XXH64 streaming API.
+ *
+ * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
+ * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is
+ * an opaque type. This allows fields to safely be changed.
+ *
+ * Typedef'd to @ref XXH64_state_t.
+ * Do not access the members of this struct directly.
+ * @see XXH32_state_s, XXH3_state_s
+ */
+struct XXH64_state_s {
+   XXH64_hash_t total_len;    /*!< Total length hashed. This is always 64-bit. */
+   XXH64_hash_t v[4];         /*!< Accumulator lanes */
+   XXH64_hash_t mem64[4];     /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */
+   XXH32_hash_t memsize;      /*!< Amount of data in @ref mem64 */
+   XXH32_hash_t reserved32;   /*!< Reserved field, needed for padding anyways*/
+   XXH64_hash_t reserved64;   /*!< Reserved field. Do not read or write to it. */
+};   /* typedef'd to XXH64_state_t */
+
+
+#ifndef XXH_NO_XXH3
+
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */
+#  include <stdalign.h>
+#  define XXH_ALIGN(n)      alignas(n)
+#elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */
+/* In C++ alignas() is a keyword */
+#  define XXH_ALIGN(n)      alignas(n)
+#elif defined(__GNUC__)
+#  define XXH_ALIGN(n)      __attribute__ ((aligned(n)))
+#elif defined(_MSC_VER)
+#  define XXH_ALIGN(n)      __declspec(align(n))
+#else
+#  define XXH_ALIGN(n)   /* disabled */
+#endif
+
+/* Old GCC versions only accept the attribute after the type in structures. */
+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L))   /* C11+ */ \
+    && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \
+    && defined(__GNUC__)
+#   define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align)
+#else
+#   define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type
+#endif
+
+/*!
+ * @brief The size of the internal XXH3 buffer.
+ *
+ * This is the optimal update size for incremental hashing.
+ *
+ * @see XXH3_64b_update(), XXH3_128b_update().
+ */
+#define XXH3_INTERNALBUFFER_SIZE 256
+
+/*!
+ * @brief Default size of the secret buffer (and @ref XXH3_kSecret).
+ *
+ * This is the size used in @ref XXH3_kSecret and the seeded functions.
+ *
+ * Not to be confused with @ref XXH3_SECRET_SIZE_MIN.
+ */
+#define XXH3_SECRET_DEFAULT_SIZE 192
+
+/*!
+ * @internal
+ * @brief Structure for XXH3 streaming API.
+ *
+ * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY,
+ * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined.
+ * Otherwise it is an opaque type.
+ * Never use this definition in combination with dynamic library.
+ * This allows fields to safely be changed in the future.
+ *
+ * @note ** This structure has a strict alignment requirement of 64 bytes!! **
+ * Do not allocate this with `malloc()` or `new`,
+ * it will not be sufficiently aligned.
+ * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation.
+ *
+ * Typedef'd to @ref XXH3_state_t.
+ * Do never access the members of this struct directly.
+ *
+ * @see XXH3_INITSTATE() for stack initialization.
+ * @see XXH3_createState(), XXH3_freeState().
+ * @see XXH32_state_s, XXH64_state_s
+ */
+struct XXH3_state_s {
+   XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]);
+       /*!< The 8 accumulators. Similar to `vN` in @ref XXH32_state_s::v1 and @ref XXH64_state_s */
+   XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]);
+       /*!< Used to store a custom secret generated from a seed. */
+   XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]);
+       /*!< The internal buffer. @see XXH32_state_s::mem32 */
+   XXH32_hash_t bufferedSize;
+       /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */
+   XXH32_hash_t useSeed;
+       /*!< Reserved field. Needed for padding on 64-bit. */
+   size_t nbStripesSoFar;
+       /*!< Number or stripes processed. */
+   XXH64_hash_t totalLen;
+       /*!< Total length hashed. 64-bit even on 32-bit targets. */
+   size_t nbStripesPerBlock;
+       /*!< Number of stripes per block. */
+   size_t secretLimit;
+       /*!< Size of @ref customSecret or @ref extSecret */
+   XXH64_hash_t seed;
+       /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */
+   XXH64_hash_t reserved64;
+       /*!< Reserved field. */
+   const unsigned char* extSecret;
+       /*!< Reference to an external secret for the _withSecret variants, NULL
+        *   for other variants. */
+   /* note: there may be some padding at the end due to alignment on 64 bytes */
+}; /* typedef'd to XXH3_state_t */
+
+#undef XXH_ALIGN_MEMBER
+
+/*!
+ * @brief Initializes a stack-allocated `XXH3_state_s`.
+ *
+ * When the @ref XXH3_state_t structure is merely emplaced on stack,
+ * it should be initialized with XXH3_INITSTATE() or a memset()
+ * in case its first reset uses XXH3_NNbits_reset_withSeed().
+ * This init can be omitted if the first reset uses default or _withSecret mode.
+ * This operation isn't necessary when the state is created with XXH3_createState().
+ * Note that this doesn't prepare the state for a streaming operation,
+ * it's still necessary to use XXH3_NNbits_reset*() afterwards.
+ */
+#define XXH3_INITSTATE(XXH3_state_ptr)   { (XXH3_state_ptr)->seed = 0; }
+
+
+/* XXH128() :
+ * simple alias to pre-selected XXH3_128bits variant
+ */
+XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed);
+
+
+/* ===   Experimental API   === */
+/* Symbols defined below must be considered tied to a specific library version. */
+
+/*
+ * XXH3_generateSecret():
+ *
+ * Derive a high-entropy secret from any user-defined content, named customSeed.
+ * The generated secret can be used in combination with `*_withSecret()` functions.
+ * The `_withSecret()` variants are useful to provide a higher level of protection than 64-bit seed,
+ * as it becomes much more difficult for an external actor to guess how to impact the calculation logic.
+ *
+ * The function accepts as input a custom seed of any length and any content,
+ * and derives from it a high-entropy secret of length @secretSize
+ * into an already allocated buffer @secretBuffer.
+ * @secretSize must be >= XXH3_SECRET_SIZE_MIN
+ *
+ * The generated secret can then be used with any `*_withSecret()` variant.
+ * Functions `XXH3_128bits_withSecret()`, `XXH3_64bits_withSecret()`,
+ * `XXH3_128bits_reset_withSecret()` and `XXH3_64bits_reset_withSecret()`
+ * are part of this list. They all accept a `secret` parameter
+ * which must be large enough for implementation reasons (>= XXH3_SECRET_SIZE_MIN)
+ * _and_ feature very high entropy (consist of random-looking bytes).
+ * These conditions can be a high bar to meet, so
+ * XXH3_generateSecret() can be employed to ensure proper quality.
+ *
+ * customSeed can be anything. It can have any size, even small ones,
+ * and its content can be anything, even "poor entropy" sources such as a bunch of zeroes.
+ * The resulting `secret` will nonetheless provide all required qualities.
+ *
+ * When customSeedSize > 0, supplying NULL as customSeed is undefined behavior.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize);
+
+
+/*
+ * XXH3_generateSecret_fromSeed():
+ *
+ * Generate the same secret as the _withSeed() variants.
+ *
+ * The resulting secret has a length of XXH3_SECRET_DEFAULT_SIZE (necessarily).
+ * @secretBuffer must be already allocated, of size at least XXH3_SECRET_DEFAULT_SIZE bytes.
+ *
+ * The generated secret can be used in combination with
+ *`*_withSecret()` and `_withSecretandSeed()` variants.
+ * This generator is notably useful in combination with `_withSecretandSeed()`,
+ * as a way to emulate a faster `_withSeed()` variant.
+ */
+XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed);
+
+/*
+ * *_withSecretandSeed() :
+ * These variants generate hash values using either
+ * @seed for "short" keys (< XXH3_MIDSIZE_MAX = 240 bytes)
+ * or @secret for "large" keys (>= XXH3_MIDSIZE_MAX).
+ *
+ * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`.
+ * `_withSeed()` has to generate the secret on the fly for "large" keys.
+ * It's fast, but can be perceptible for "not so large" keys (< 1 KB).
+ * `_withSecret()` has to generate the masks on the fly for "small" keys,
+ * which requires more instructions than _withSeed() variants.
+ * Therefore, _withSecretandSeed variant combines the best of both worlds.
+ *
+ * When @secret has been generated by XXH3_generateSecret_fromSeed(),
+ * this variant produces *exactly* the same results as `_withSeed()` variant,
+ * hence offering only a pure speed benefit on "large" input,
+ * by skipping the need to regenerate the secret for every large input.
+ *
+ * Another usage scenario is to hash the secret to a 64-bit hash value,
+ * for example with XXH3_64bits(), which then becomes the seed,
+ * and then employ both the seed and the secret in _withSecretandSeed().
+ * On top of speed, an added benefit is that each bit in the secret
+ * has a 50% chance to swap each bit in the output,
+ * via its impact to the seed.
+ * This is not guaranteed when using the secret directly in "small data" scenarios,
+ * because only portions of the secret are employed for small data.
+ */
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSecretandSeed(const void* data, size_t len,
+                              const void* secret, size_t secretSize,
+                              XXH64_hash_t seed);
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSecretandSeed(const void* data, size_t len,
+                               const void* secret, size_t secretSize,
+                               XXH64_hash_t seed64);
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr,
+                                    const void* secret, size_t secretSize,
+                                    XXH64_hash_t seed64);
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr,
+                                     const void* secret, size_t secretSize,
+                                     XXH64_hash_t seed64);
+
+
+#endif  /* XXH_NO_XXH3 */
+#endif  /* XXH_NO_LONG_LONG */
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+#  define XXH_IMPLEMENTATION
+#endif
+
+#endif  /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */
+
+
+/* ======================================================================== */
+/* ======================================================================== */
+/* ======================================================================== */
+
+
+/*-**********************************************************************
+ * xxHash implementation
+ *-**********************************************************************
+ * xxHash's implementation used to be hosted inside xxhash.c.
+ *
+ * However, inlining requires implementation to be visible to the compiler,
+ * hence be included alongside the header.
+ * Previously, implementation was hosted inside xxhash.c,
+ * which was then #included when inlining was activated.
+ * This construction created issues with a few build and install systems,
+ * as it required xxhash.c to be stored in /include directory.
+ *
+ * xxHash implementation is now directly integrated within xxhash.h.
+ * As a consequence, xxhash.c is no longer needed in /include.
+ *
+ * xxhash.c is still available and is still useful.
+ * In a "normal" setup, when xxhash is not inlined,
+ * xxhash.h only exposes the prototypes and public symbols,
+ * while xxhash.c can be built into an object file xxhash.o
+ * which can then be linked into the final binary.
+ ************************************************************************/
+
+#if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \
+   || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387)
+#  define XXH_IMPLEM_13a8737387
+
+/* *************************************
+*  Tuning parameters
+***************************************/
+
+/*!
+ * @defgroup tuning Tuning parameters
+ * @{
+ *
+ * Various macros to control xxHash's behavior.
+ */
+#ifdef XXH_DOXYGEN
+/*!
+ * @brief Define this to disable 64-bit code.
+ *
+ * Useful if only using the @ref xxh32_family and you have a strict C90 compiler.
+ */
+#  define XXH_NO_LONG_LONG
+#  undef XXH_NO_LONG_LONG /* don't actually */
+/*!
+ * @brief Controls how unaligned memory is accessed.
+ *
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is
+ * safe and portable.
+ *
+ * Unfortunately, on some target/compiler combinations, the generated assembly
+ * is sub-optimal.
+ *
+ * The below switch allow selection of a different access method
+ * in the search for improved performance.
+ *
+ * @par Possible options:
+ *
+ *  - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy`
+ *   @par
+ *     Use `memcpy()`. Safe and portable. Note that most modern compilers will
+ *     eliminate the function call and treat it as an unaligned access.
+ *
+ *  - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((packed))`
+ *   @par
+ *     Depends on compiler extensions and is therefore not portable.
+ *     This method is safe _if_ your compiler supports it,
+ *     and *generally* as fast or faster than `memcpy`.
+ *
+ *  - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast
+ *  @par
+ *     Casts directly and dereferences. This method doesn't depend on the
+ *     compiler, but it violates the C standard as it directly dereferences an
+ *     unaligned pointer. It can generate buggy code on targets which do not
+ *     support unaligned memory accesses, but in some circumstances, it's the
+ *     only known way to get the most performance.
+ *
+ *  - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift
+ *  @par
+ *     Also portable. This can generate the best code on old compilers which don't
+ *     inline small `memcpy()` calls, and it might also be faster on big-endian
+ *     systems which lack a native byteswap instruction. However, some compilers
+ *     will emit literal byteshifts even if the target supports unaligned access.
+ *  .
+ *
+ * @warning
+ *   Methods 1 and 2 rely on implementation-defined behavior. Use these with
+ *   care, as what works on one compiler/platform/optimization level may cause
+ *   another to read garbage data or even crash.
+ *
+ * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details.
+ *
+ * Prefer these methods in priority order (0 > 3 > 1 > 2)
+ */
+#  define XXH_FORCE_MEMORY_ACCESS 0
+
+/*!
+ * @def XXH_FORCE_ALIGN_CHECK
+ * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32()
+ * and XXH64() only).
+ *
+ * This is an important performance trick for architectures without decent
+ * unaligned memory access performance.
+ *
+ * It checks for input alignment, and when conditions are met, uses a "fast
+ * path" employing direct 32-bit/64-bit reads, resulting in _dramatically
+ * faster_ read speed.
+ *
+ * The check costs one initial branch per hash, which is generally negligible,
+ * but not zero.
+ *
+ * Moreover, it's not useful to generate an additional code path if memory
+ * access uses the same instruction for both aligned and unaligned
+ * addresses (e.g. x86 and aarch64).
+ *
+ * In these cases, the alignment check can be removed by setting this macro to 0.
+ * Then the code will always use unaligned memory access.
+ * Align check is automatically disabled on x86, x64 & arm64,
+ * which are platforms known to offer good unaligned memory accesses performance.
+ *
+ * This option does not affect XXH3 (only XXH32 and XXH64).
+ */
+#  define XXH_FORCE_ALIGN_CHECK 0
+
+/*!
+ * @def XXH_NO_INLINE_HINTS
+ * @brief When non-zero, sets all functions to `static`.
+ *
+ * By default, xxHash tries to force the compiler to inline almost all internal
+ * functions.
+ *
+ * This can usually improve performance due to reduced jumping and improved
+ * constant folding, but significantly increases the size of the binary which
+ * might not be favorable.
+ *
+ * Additionally, sometimes the forced inlining can be detrimental to performance,
+ * depending on the architecture.
+ *
+ * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the
+ * compiler full control on whether to inline or not.
+ *
+ * When not optimizing (-O0), optimizing for size (-Os, -Oz), or using
+ * -fno-inline with GCC or Clang, this will automatically be defined.
+ */
+#  define XXH_NO_INLINE_HINTS 0
+
+/*!
+ * @def XXH32_ENDJMP
+ * @brief Whether to use a jump for `XXH32_finalize`.
+ *
+ * For performance, `XXH32_finalize` uses multiple branches in the finalizer.
+ * This is generally preferable for performance,
+ * but depending on exact architecture, a jmp may be preferable.
+ *
+ * This setting is only possibly making a difference for very small inputs.
+ */
+#  define XXH32_ENDJMP 0
+
+/*!
+ * @internal
+ * @brief Redefines old internal names.
+ *
+ * For compatibility with code that uses xxHash's internals before the names
+ * were changed to improve namespacing. There is no other reason to use this.
+ */
+#  define XXH_OLD_NAMES
+#  undef XXH_OLD_NAMES /* don't actually use, it is ugly. */
+#endif /* XXH_DOXYGEN */
+/*!
+ * @}
+ */
+
+#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
+   /* prefer __packed__ structures (method 1) for gcc on armv7+ and mips */
+#  if !defined(__clang__) && \
+( \
+    (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \
+    ( \
+        defined(__GNUC__) && ( \
+            (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || \
+            ( \
+                defined(__mips__) && \
+                (__mips <= 5 || __mips_isa_rev < 6) && \
+                (!defined(__mips16) || defined(__mips_mips16e2)) \
+            ) \
+        ) \
+    ) \
+)
+#    define XXH_FORCE_MEMORY_ACCESS 1
+#  endif
+#endif
+
+#ifndef XXH_FORCE_ALIGN_CHECK  /* can be defined externally */
+#  if defined(__i386)  || defined(__x86_64__) || defined(__aarch64__) \
+   || defined(_M_IX86) || defined(_M_X64)     || defined(_M_ARM64) /* visual */
+#    define XXH_FORCE_ALIGN_CHECK 0
+#  else
+#    define XXH_FORCE_ALIGN_CHECK 1
+#  endif
+#endif
+
+#ifndef XXH_NO_INLINE_HINTS
+#  if defined(__OPTIMIZE_SIZE__) /* -Os, -Oz */ \
+   || defined(__NO_INLINE__)     /* -O0, -fno-inline */
+#    define XXH_NO_INLINE_HINTS 1
+#  else
+#    define XXH_NO_INLINE_HINTS 0
+#  endif
+#endif
+
+#ifndef XXH32_ENDJMP
+/* generally preferable for performance */
+#  define XXH32_ENDJMP 0
+#endif
+
+/*!
+ * @defgroup impl Implementation
+ * @{
+ */
+
+
+/* *************************************
+*  Includes & Memory related functions
+***************************************/
+/* Modify the local functions below should you wish to use some other memory routines */
+/* for ZSTD_malloc(), ZSTD_free() */
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"  /* size_t, ZSTD_malloc, ZSTD_free, ZSTD_memcpy */
+static void* XXH_malloc(size_t s) { return ZSTD_malloc(s); }
+static void  XXH_free  (void* p)  { ZSTD_free(p); }
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_memcpy(dest,src,size); }
+
+
+/* *************************************
+*  Compiler Specific Options
+***************************************/
+#ifdef _MSC_VER /* Visual Studio warning fix */
+#  pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+#if XXH_NO_INLINE_HINTS  /* disable inlining hints */
+#  if defined(__GNUC__) || defined(__clang__)
+#    define XXH_FORCE_INLINE static __attribute__((unused))
+#  else
+#    define XXH_FORCE_INLINE static
+#  endif
+#  define XXH_NO_INLINE static
+/* enable inlining hints */
+#elif defined(__GNUC__) || defined(__clang__)
+#  define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused))
+#  define XXH_NO_INLINE static __attribute__((noinline))
+#elif defined(_MSC_VER)  /* Visual Studio */
+#  define XXH_FORCE_INLINE static __forceinline
+#  define XXH_NO_INLINE static __declspec(noinline)
+#elif defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))   /* C99 */
+#  define XXH_FORCE_INLINE static inline
+#  define XXH_NO_INLINE static
+#else
+#  define XXH_FORCE_INLINE static
+#  define XXH_NO_INLINE static
+#endif
+
+
+
+/* *************************************
+*  Debug
+***************************************/
+/*!
+ * @ingroup tuning
+ * @def XXH_DEBUGLEVEL
+ * @brief Sets the debugging level.
+ *
+ * XXH_DEBUGLEVEL is expected to be defined externally, typically via the
+ * compiler's command line options. The value must be a number.
+ */
+#ifndef XXH_DEBUGLEVEL
+#  ifdef DEBUGLEVEL /* backwards compat */
+#    define XXH_DEBUGLEVEL DEBUGLEVEL
+#  else
+#    define XXH_DEBUGLEVEL 0
+#  endif
+#endif
+
+#if (XXH_DEBUGLEVEL>=1)
+#  include <assert.h>   /* note: can still be disabled with NDEBUG */
+#  define XXH_ASSERT(c)   assert(c)
+#else
+#  define XXH_ASSERT(c)   ((void)0)
+#endif
+
+/* note: use after variable declarations */
+#ifndef XXH_STATIC_ASSERT
+#  if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)    /* C11 */
+#    include <assert.h>
+#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0)
+#  elif defined(__cplusplus) && (__cplusplus >= 201103L)            /* C++11 */
+#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0)
+#  else
+#    define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0)
+#  endif
+#  define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c)
+#endif
+
+/*!
+ * @internal
+ * @def XXH_COMPILER_GUARD(var)
+ * @brief Used to prevent unwanted optimizations for @p var.
+ *
+ * It uses an empty GCC inline assembly statement with a register constraint
+ * which forces @p var into a general purpose register (e.g. eax, ebx, ecx
+ * on x86) and marks it as modified.
+ *
+ * This is used in a few places to avoid unwanted autovectorization (e.g.
+ * XXH32_round()). All vectorization we want is explicit via intrinsics,
+ * and _usually_ isn't wanted elsewhere.
+ *
+ * We also use it to prevent unwanted constant folding for AArch64 in
+ * XXH3_initCustomSecret_scalar().
+ */
+#if defined(__GNUC__) || defined(__clang__)
+#  define XXH_COMPILER_GUARD(var) __asm__ __volatile__("" : "+r" (var))
+#else
+#  define XXH_COMPILER_GUARD(var) ((void)0)
+#endif
+
+/* *************************************
+*  Basic Types
+***************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+  typedef uint8_t xxh_u8;
+#else
+  typedef unsigned char xxh_u8;
+#endif
+typedef XXH32_hash_t xxh_u32;
+
+#ifdef XXH_OLD_NAMES
+#  define BYTE xxh_u8
+#  define U8   xxh_u8
+#  define U32  xxh_u32
+#endif
+
+/* ***   Memory access   *** */
+
+/*!
+ * @internal
+ * @fn xxh_u32 XXH_read32(const void* ptr)
+ * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness.
+ *
+ * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
+ *
+ * @param ptr The pointer to read from.
+ * @return The 32-bit native endian integer from the bytes at @p ptr.
+ */
+
+/*!
+ * @internal
+ * @fn xxh_u32 XXH_readLE32(const void* ptr)
+ * @brief Reads an unaligned 32-bit little endian integer from @p ptr.
+ *
+ * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
+ *
+ * @param ptr The pointer to read from.
+ * @return The 32-bit little endian integer from the bytes at @p ptr.
+ */
+
+/*!
+ * @internal
+ * @fn xxh_u32 XXH_readBE32(const void* ptr)
+ * @brief Reads an unaligned 32-bit big endian integer from @p ptr.
+ *
+ * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
+ *
+ * @param ptr The pointer to read from.
+ * @return The 32-bit big endian integer from the bytes at @p ptr.
+ */
+
+/*!
+ * @internal
+ * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align)
+ * @brief Like @ref XXH_readLE32(), but has an option for aligned reads.
+ *
+ * Affected by @ref XXH_FORCE_MEMORY_ACCESS.
+ * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is
+ * always @ref XXH_alignment::XXH_unaligned.
+ *
+ * @param ptr The pointer to read from.
+ * @param align Whether @p ptr is aligned.
+ * @pre
+ *   If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte
+ *   aligned.
+ * @return The 32-bit little endian integer from the bytes at @p ptr.
+ */
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+/*
+ * Manual byteshift. Best for old compilers which don't inline memcpy.
+ * We actually directly use XXH_readLE32 and XXH_readBE32.
+ */
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/*
+ * Force direct memory access. Only works on CPU which support unaligned memory
+ * access in hardware.
+ */
+static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/*
+ * __pack instructions are safer but compiler specific, hence potentially
+ * problematic for some compilers.
+ *
+ * Currently only defined for GCC and ICC.
+ */
+#ifdef XXH_OLD_NAMES
+typedef union { xxh_u32 u32; } __attribute__((packed)) unalign;
+#endif
+static xxh_u32 XXH_read32(const void* ptr)
+{
+    typedef union { xxh_u32 u32; } __attribute__((packed)) xxh_unalign;
+    return ((const xxh_unalign*)ptr)->u32;
+}
+
+#else
+
+/*
+ * Portable and safe solution. Generally efficient.
+ * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html
+ */
+static xxh_u32 XXH_read32(const void* memPtr)
+{
+    xxh_u32 val;
+    XXH_memcpy(&val, memPtr, sizeof(val));
+    return val;
+}
+
+#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+
+/* ***   Endianness   *** */
+
+/*!
+ * @ingroup tuning
+ * @def XXH_CPU_LITTLE_ENDIAN
+ * @brief Whether the target is little endian.
+ *
+ * Defined to 1 if the target is little endian, or 0 if it is big endian.
+ * It can be defined externally, for example on the compiler command line.
+ *
+ * If it is not defined,
+ * a runtime check (which is usually constant folded) is used instead.
+ *
+ * @note
+ *   This is not necessarily defined to an integer constant.
+ *
+ * @see XXH_isLittleEndian() for the runtime check.
+ */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+/*
+ * Try to detect endianness automatically, to avoid the nonstandard behavior
+ * in `XXH_isLittleEndian()`
+ */
+#  if defined(_WIN32) /* Windows is always little endian */ \
+     || defined(__LITTLE_ENDIAN__) \
+     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#    define XXH_CPU_LITTLE_ENDIAN 1
+#  elif defined(__BIG_ENDIAN__) \
+     || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#    define XXH_CPU_LITTLE_ENDIAN 0
+#  else
+/*!
+ * @internal
+ * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN.
+ *
+ * Most compilers will constant fold this.
+ */
+static int XXH_isLittleEndian(void)
+{
+    /*
+     * Portable and well-defined behavior.
+     * Don't use static: it is detrimental to performance.
+     */
+    const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 };
+    return one.c[0];
+}
+#   define XXH_CPU_LITTLE_ENDIAN   XXH_isLittleEndian()
+#  endif
+#endif
+
+
+
+
+/* ****************************************
+*  Compiler-specific Functions and Macros
+******************************************/
+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+#ifdef __has_builtin
+#  define XXH_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#  define XXH_HAS_BUILTIN(x) 0
+#endif
+
+/*!
+ * @internal
+ * @def XXH_rotl32(x,r)
+ * @brief 32-bit rotate left.
+ *
+ * @param x The 32-bit integer to be rotated.
+ * @param r The number of bits to rotate.
+ * @pre
+ *   @p r > 0 && @p r < 32
+ * @note
+ *   @p x and @p r may be evaluated multiple times.
+ * @return The rotated result.
+ */
+#if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \
+                               && XXH_HAS_BUILTIN(__builtin_rotateleft64)
+#  define XXH_rotl32 __builtin_rotateleft32
+#  define XXH_rotl64 __builtin_rotateleft64
+/* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */
+#elif defined(_MSC_VER)
+#  define XXH_rotl32(x,r) _rotl(x,r)
+#  define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+#  define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#  define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
+#endif
+
+/*!
+ * @internal
+ * @fn xxh_u32 XXH_swap32(xxh_u32 x)
+ * @brief A 32-bit byteswap.
+ *
+ * @param x The 32-bit integer to byteswap.
+ * @return @p x, byteswapped.
+ */
+#if defined(_MSC_VER)     /* Visual Studio */
+#  define XXH_swap32 _byteswap_ulong
+#elif XXH_GCC_VERSION >= 403
+#  define XXH_swap32 __builtin_bswap32
+#else
+static xxh_u32 XXH_swap32 (xxh_u32 x)
+{
+    return  ((x << 24) & 0xff000000 ) |
+            ((x <<  8) & 0x00ff0000 ) |
+            ((x >>  8) & 0x0000ff00 ) |
+            ((x >> 24) & 0x000000ff );
+}
+#endif
+
+
+/* ***************************
+*  Memory reads
+*****************************/
+
+/*!
+ * @internal
+ * @brief Enum to indicate whether a pointer is aligned.
+ */
+typedef enum {
+    XXH_aligned,  /*!< Aligned */
+    XXH_unaligned /*!< Possibly unaligned */
+} XXH_alignment;
+
+/*
+ * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load.
+ *
+ * This is ideal for older compilers which don't inline memcpy.
+ */
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+
+XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr)
+{
+    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+    return bytePtr[0]
+         | ((xxh_u32)bytePtr[1] << 8)
+         | ((xxh_u32)bytePtr[2] << 16)
+         | ((xxh_u32)bytePtr[3] << 24);
+}
+
+XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr)
+{
+    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+    return bytePtr[3]
+         | ((xxh_u32)bytePtr[2] << 8)
+         | ((xxh_u32)bytePtr[1] << 16)
+         | ((xxh_u32)bytePtr[0] << 24);
+}
+
+#else
+XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+}
+
+static xxh_u32 XXH_readBE32(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
+}
+#endif
+
+XXH_FORCE_INLINE xxh_u32
+XXH_readLE32_align(const void* ptr, XXH_alignment align)
+{
+    if (align==XXH_unaligned) {
+        return XXH_readLE32(ptr);
+    } else {
+        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr);
+    }
+}
+
+
+/* *************************************
+*  Misc
+***************************************/
+/*! @ingroup public */
+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
+
+
+/* *******************************************************************
+*  32-bit hash functions
+*********************************************************************/
+/*!
+ * @}
+ * @defgroup xxh32_impl XXH32 implementation
+ * @ingroup impl
+ * @{
+ */
+ /* #define instead of static const, to be used as initializers */
+#define XXH_PRIME32_1  0x9E3779B1U  /*!< 0b10011110001101110111100110110001 */
+#define XXH_PRIME32_2  0x85EBCA77U  /*!< 0b10000101111010111100101001110111 */
+#define XXH_PRIME32_3  0xC2B2AE3DU  /*!< 0b11000010101100101010111000111101 */
+#define XXH_PRIME32_4  0x27D4EB2FU  /*!< 0b00100111110101001110101100101111 */
+#define XXH_PRIME32_5  0x165667B1U  /*!< 0b00010110010101100110011110110001 */
+
+#ifdef XXH_OLD_NAMES
+#  define PRIME32_1 XXH_PRIME32_1
+#  define PRIME32_2 XXH_PRIME32_2
+#  define PRIME32_3 XXH_PRIME32_3
+#  define PRIME32_4 XXH_PRIME32_4
+#  define PRIME32_5 XXH_PRIME32_5
+#endif
+
+/*!
+ * @internal
+ * @brief Normal stripe processing routine.
+ *
+ * This shuffles the bits so that any bit from @p input impacts several bits in
+ * @p acc.
+ *
+ * @param acc The accumulator lane.
+ * @param input The stripe of input to mix.
+ * @return The mixed accumulator lane.
+ */
+static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input)
+{
+    acc += input * XXH_PRIME32_2;
+    acc  = XXH_rotl32(acc, 13);
+    acc *= XXH_PRIME32_1;
+#if (defined(__SSE4_1__) || defined(__aarch64__)) && !defined(XXH_ENABLE_AUTOVECTORIZE)
+    /*
+     * UGLY HACK:
+     * A compiler fence is the only thing that prevents GCC and Clang from
+     * autovectorizing the XXH32 loop (pragmas and attributes don't work for some
+     * reason) without globally disabling SSE4.1.
+     *
+     * The reason we want to avoid vectorization is because despite working on
+     * 4 integers at a time, there are multiple factors slowing XXH32 down on
+     * SSE4:
+     * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on
+     *   newer chips!) making it slightly slower to multiply four integers at
+     *   once compared to four integers independently. Even when pmulld was
+     *   fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE
+     *   just to multiply unless doing a long operation.
+     *
+     * - Four instructions are required to rotate,
+     *      movqda tmp,  v // not required with VEX encoding
+     *      pslld  tmp, 13 // tmp <<= 13
+     *      psrld  v,   19 // x >>= 19
+     *      por    v,  tmp // x |= tmp
+     *   compared to one for scalar:
+     *      roll   v, 13    // reliably fast across the board
+     *      shldl  v, v, 13 // Sandy Bridge and later prefer this for some reason
+     *
+     * - Instruction level parallelism is actually more beneficial here because
+     *   the SIMD actually serializes this operation: While v1 is rotating, v2
+     *   can load data, while v3 can multiply. SSE forces them to operate
+     *   together.
+     *
+     * This is also enabled on AArch64, as Clang autovectorizes it incorrectly
+     * and it is pointless writing a NEON implementation that is basically the
+     * same speed as scalar for XXH32.
+     */
+    XXH_COMPILER_GUARD(acc);
+#endif
+    return acc;
+}
+
+/*!
+ * @internal
+ * @brief Mixes all bits to finalize the hash.
+ *
+ * The final mix ensures that all input bits have a chance to impact any bit in
+ * the output digest, resulting in an unbiased distribution.
+ *
+ * @param h32 The hash to avalanche.
+ * @return The avalanched hash.
+ */
+static xxh_u32 XXH32_avalanche(xxh_u32 h32)
+{
+    h32 ^= h32 >> 15;
+    h32 *= XXH_PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= XXH_PRIME32_3;
+    h32 ^= h32 >> 16;
+    return(h32);
+}
+
+#define XXH_get32bits(p) XXH_readLE32_align(p, align)
+
+/*!
+ * @internal
+ * @brief Processes the last 0-15 bytes of @p ptr.
+ *
+ * There may be up to 15 bytes remaining to consume from the input.
+ * This final stage will digest them to ensure that all input bytes are present
+ * in the final mix.
+ *
+ * @param h32 The hash to finalize.
+ * @param ptr The pointer to the remaining input.
+ * @param len The remaining length, modulo 16.
+ * @param align Whether @p ptr is aligned.
+ * @return The finalized hash.
+ */
+static xxh_u32
+XXH32_finalize(xxh_u32 h32, const xxh_u8* ptr, size_t len, XXH_alignment align)
+{
+#define XXH_PROCESS1 do {                           \
+    h32 += (*ptr++) * XXH_PRIME32_5;                \
+    h32 = XXH_rotl32(h32, 11) * XXH_PRIME32_1;      \
+} while (0)
+
+#define XXH_PROCESS4 do {                           \
+    h32 += XXH_get32bits(ptr) * XXH_PRIME32_3;      \
+    ptr += 4;                                   \
+    h32  = XXH_rotl32(h32, 17) * XXH_PRIME32_4;     \
+} while (0)
+
+    if (ptr==NULL) XXH_ASSERT(len == 0);
+
+    /* Compact rerolled version; generally faster */
+    if (!XXH32_ENDJMP) {
+        len &= 15;
+        while (len >= 4) {
+            XXH_PROCESS4;
+            len -= 4;
+        }
+        while (len > 0) {
+            XXH_PROCESS1;
+            --len;
+        }
+        return XXH32_avalanche(h32);
+    } else {
+         switch(len&15) /* or switch(bEnd - p) */ {
+           case 12:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 8:       XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 4:       XXH_PROCESS4;
+                         return XXH32_avalanche(h32);
+
+           case 13:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 9:       XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 5:       XXH_PROCESS4;
+                         XXH_PROCESS1;
+                         return XXH32_avalanche(h32);
+
+           case 14:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 10:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 6:       XXH_PROCESS4;
+                         XXH_PROCESS1;
+                         XXH_PROCESS1;
+                         return XXH32_avalanche(h32);
+
+           case 15:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 11:      XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 7:       XXH_PROCESS4;
+                         XXH_FALLTHROUGH;
+           case 3:       XXH_PROCESS1;
+                         XXH_FALLTHROUGH;
+           case 2:       XXH_PROCESS1;
+                         XXH_FALLTHROUGH;
+           case 1:       XXH_PROCESS1;
+                         XXH_FALLTHROUGH;
+           case 0:       return XXH32_avalanche(h32);
+        }
+        XXH_ASSERT(0);
+        return h32;   /* reaching this point is deemed impossible */
+    }
+}
+
+#ifdef XXH_OLD_NAMES
+#  define PROCESS1 XXH_PROCESS1
+#  define PROCESS4 XXH_PROCESS4
+#else
+#  undef XXH_PROCESS1
+#  undef XXH_PROCESS4
+#endif
+
+/*!
+ * @internal
+ * @brief The implementation for @ref XXH32().
+ *
+ * @param input , len , seed Directly passed from @ref XXH32().
+ * @param align Whether @p input is aligned.
+ * @return The calculated hash.
+ */
+XXH_FORCE_INLINE xxh_u32
+XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align)
+{
+    xxh_u32 h32;
+
+    if (input==NULL) XXH_ASSERT(len == 0);
+
+    if (len>=16) {
+        const xxh_u8* const bEnd = input + len;
+        const xxh_u8* const limit = bEnd - 15;
+        xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
+        xxh_u32 v2 = seed + XXH_PRIME32_2;
+        xxh_u32 v3 = seed + 0;
+        xxh_u32 v4 = seed - XXH_PRIME32_1;
+
+        do {
+            v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4;
+            v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4;
+            v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4;
+            v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4;
+        } while (input < limit);
+
+        h32 = XXH_rotl32(v1, 1)  + XXH_rotl32(v2, 7)
+            + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+    } else {
+        h32  = seed + XXH_PRIME32_5;
+    }
+
+    h32 += (xxh_u32)len;
+
+    return XXH32_finalize(h32, input, len&15, align);
+}
+
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed)
+{
+#if 0
+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+    XXH32_state_t state;
+    XXH32_reset(&state, seed);
+    XXH32_update(&state, (const xxh_u8*)input, len);
+    return XXH32_digest(&state);
+#else
+    if (XXH_FORCE_ALIGN_CHECK) {
+        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */
+            return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
+    }   }
+
+    return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);
+#endif
+}
+
+
+
+/*******   Hash streaming   *******/
+/*!
+ * @ingroup xxh32_family
+ */
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
+{
+    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+    XXH_free(statePtr);
+    return XXH_OK;
+}
+
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
+{
+    XXH_memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed)
+{
+    XXH_ASSERT(statePtr != NULL);
+    memset(statePtr, 0, sizeof(*statePtr));
+    statePtr->v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2;
+    statePtr->v[1] = seed + XXH_PRIME32_2;
+    statePtr->v[2] = seed + 0;
+    statePtr->v[3] = seed - XXH_PRIME32_1;
+    return XXH_OK;
+}
+
+
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH32_update(XXH32_state_t* state, const void* input, size_t len)
+{
+    if (input==NULL) {
+        XXH_ASSERT(len == 0);
+        return XXH_OK;
+    }
+
+    {   const xxh_u8* p = (const xxh_u8*)input;
+        const xxh_u8* const bEnd = p + len;
+
+        state->total_len_32 += (XXH32_hash_t)len;
+        state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16));
+
+        if (state->memsize + len < 16)  {   /* fill in tmp buffer */
+            XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len);
+            state->memsize += (XXH32_hash_t)len;
+            return XXH_OK;
+        }
+
+        if (state->memsize) {   /* some data left from previous update */
+            XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize);
+            {   const xxh_u32* p32 = state->mem32;
+                state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++;
+                state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++;
+                state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++;
+                state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32));
+            }
+            p += 16-state->memsize;
+            state->memsize = 0;
+        }
+
+        if (p <= bEnd-16) {
+            const xxh_u8* const limit = bEnd - 16;
+
+            do {
+                state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4;
+                state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4;
+                state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4;
+                state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4;
+            } while (p<=limit);
+
+        }
+
+        if (p < bEnd) {
+            XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
+            state->memsize = (unsigned)(bEnd-p);
+        }
+    }
+
+    return XXH_OK;
+}
+
+
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state)
+{
+    xxh_u32 h32;
+
+    if (state->large_len) {
+        h32 = XXH_rotl32(state->v[0], 1)
+            + XXH_rotl32(state->v[1], 7)
+            + XXH_rotl32(state->v[2], 12)
+            + XXH_rotl32(state->v[3], 18);
+    } else {
+        h32 = state->v[2] /* == seed */ + XXH_PRIME32_5;
+    }
+
+    h32 += state->total_len_32;
+
+    return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned);
+}
+
+
+/*******   Canonical representation   *******/
+
+/*!
+ * @ingroup xxh32_family
+ * The default return values from XXH functions are unsigned 32 and 64 bit
+ * integers.
+ *
+ * The canonical representation uses big endian convention, the same convention
+ * as human-readable numbers (large digits first).
+ *
+ * This way, hash values can be written into a file or buffer, remaining
+ * comparable across different systems.
+ *
+ * The following functions allow transformation of hash values to and from their
+ * canonical format.
+ */
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
+{
+    /* XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); */
+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
+    XXH_memcpy(dst, &hash, sizeof(*dst));
+}
+/*! @ingroup xxh32_family */
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
+{
+    return XXH_readBE32(src);
+}
+
+
+#ifndef XXH_NO_LONG_LONG
+
+/* *******************************************************************
+*  64-bit hash functions
+*********************************************************************/
+/*!
+ * @}
+ * @ingroup impl
+ * @{
+ */
+/*******   Memory access   *******/
+
+typedef XXH64_hash_t xxh_u64;
+
+#ifdef XXH_OLD_NAMES
+#  define U64 xxh_u64
+#endif
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+/*
+ * Manual byteshift. Best for old compilers which don't inline memcpy.
+ * We actually directly use XXH_readLE64 and XXH_readBE64.
+ */
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static xxh_u64 XXH_read64(const void* memPtr)
+{
+    return *(const xxh_u64*) memPtr;
+}
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/*
+ * __pack instructions are safer, but compiler specific, hence potentially
+ * problematic for some compilers.
+ *
+ * Currently only defined for GCC and ICC.
+ */
+#ifdef XXH_OLD_NAMES
+typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64;
+#endif
+static xxh_u64 XXH_read64(const void* ptr)
+{
+    typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) xxh_unalign64;
+    return ((const xxh_unalign64*)ptr)->u64;
+}
+
+#else
+
+/*
+ * Portable and safe solution. Generally efficient.
+ * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html
+ */
+static xxh_u64 XXH_read64(const void* memPtr)
+{
+    xxh_u64 val;
+    XXH_memcpy(&val, memPtr, sizeof(val));
+    return val;
+}
+
+#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+#if defined(_MSC_VER)     /* Visual Studio */
+#  define XXH_swap64 _byteswap_uint64
+#elif XXH_GCC_VERSION >= 403
+#  define XXH_swap64 __builtin_bswap64
+#else
+static xxh_u64 XXH_swap64(xxh_u64 x)
+{
+    return  ((x << 56) & 0xff00000000000000ULL) |
+            ((x << 40) & 0x00ff000000000000ULL) |
+            ((x << 24) & 0x0000ff0000000000ULL) |
+            ((x << 8)  & 0x000000ff00000000ULL) |
+            ((x >> 8)  & 0x00000000ff000000ULL) |
+            ((x >> 24) & 0x0000000000ff0000ULL) |
+            ((x >> 40) & 0x000000000000ff00ULL) |
+            ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+
+/* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3))
+
+XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr)
+{
+    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+    return bytePtr[0]
+         | ((xxh_u64)bytePtr[1] << 8)
+         | ((xxh_u64)bytePtr[2] << 16)
+         | ((xxh_u64)bytePtr[3] << 24)
+         | ((xxh_u64)bytePtr[4] << 32)
+         | ((xxh_u64)bytePtr[5] << 40)
+         | ((xxh_u64)bytePtr[6] << 48)
+         | ((xxh_u64)bytePtr[7] << 56);
+}
+
+XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr)
+{
+    const xxh_u8* bytePtr = (const xxh_u8 *)memPtr;
+    return bytePtr[7]
+         | ((xxh_u64)bytePtr[6] << 8)
+         | ((xxh_u64)bytePtr[5] << 16)
+         | ((xxh_u64)bytePtr[4] << 24)
+         | ((xxh_u64)bytePtr[3] << 32)
+         | ((xxh_u64)bytePtr[2] << 40)
+         | ((xxh_u64)bytePtr[1] << 48)
+         | ((xxh_u64)bytePtr[0] << 56);
+}
+
+#else
+XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+}
+
+static xxh_u64 XXH_readBE64(const void* ptr)
+{
+    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
+}
+#endif
+
+XXH_FORCE_INLINE xxh_u64
+XXH_readLE64_align(const void* ptr, XXH_alignment align)
+{
+    if (align==XXH_unaligned)
+        return XXH_readLE64(ptr);
+    else
+        return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr);
+}
+
+
+/*******   xxh64   *******/
+/*!
+ * @}
+ * @defgroup xxh64_impl XXH64 implementation
+ * @ingroup impl
+ * @{
+ */
+/* #define rather that static const, to be used as initializers */
+#define XXH_PRIME64_1  0x9E3779B185EBCA87ULL  /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */
+#define XXH_PRIME64_2  0xC2B2AE3D27D4EB4FULL  /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */
+#define XXH_PRIME64_3  0x165667B19E3779F9ULL  /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */
+#define XXH_PRIME64_4  0x85EBCA77C2B2AE63ULL  /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
+#define XXH_PRIME64_5  0x27D4EB2F165667C5ULL  /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
+
+#ifdef XXH_OLD_NAMES
+#  define PRIME64_1 XXH_PRIME64_1
+#  define PRIME64_2 XXH_PRIME64_2
+#  define PRIME64_3 XXH_PRIME64_3
+#  define PRIME64_4 XXH_PRIME64_4
+#  define PRIME64_5 XXH_PRIME64_5
+#endif
+
+static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input)
+{
+    acc += input * XXH_PRIME64_2;
+    acc  = XXH_rotl64(acc, 31);
+    acc *= XXH_PRIME64_1;
+    return acc;
+}
+
+static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val)
+{
+    val  = XXH64_round(0, val);
+    acc ^= val;
+    acc  = acc * XXH_PRIME64_1 + XXH_PRIME64_4;
+    return acc;
+}
+
+static xxh_u64 XXH64_avalanche(xxh_u64 h64)
+{
+    h64 ^= h64 >> 33;
+    h64 *= XXH_PRIME64_2;
+    h64 ^= h64 >> 29;
+    h64 *= XXH_PRIME64_3;
+    h64 ^= h64 >> 32;
+    return h64;
+}
+
+
+#define XXH_get64bits(p) XXH_readLE64_align(p, align)
+
+static xxh_u64
+XXH64_finalize(xxh_u64 h64, const xxh_u8* ptr, size_t len, XXH_alignment align)
+{
+    if (ptr==NULL) XXH_ASSERT(len == 0);
+    len &= 31;
+    while (len >= 8) {
+        xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr));
+        ptr += 8;
+        h64 ^= k1;
+        h64  = XXH_rotl64(h64,27) * XXH_PRIME64_1 + XXH_PRIME64_4;
+        len -= 8;
+    }
+    if (len >= 4) {
+        h64 ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1;
+        ptr += 4;
+        h64 = XXH_rotl64(h64, 23) * XXH_PRIME64_2 + XXH_PRIME64_3;
+        len -= 4;
+    }
+    while (len > 0) {
+        h64 ^= (*ptr++) * XXH_PRIME64_5;
+        h64 = XXH_rotl64(h64, 11) * XXH_PRIME64_1;
+        --len;
+    }
+    return  XXH64_avalanche(h64);
+}
+
+#ifdef XXH_OLD_NAMES
+#  define PROCESS1_64 XXH_PROCESS1_64
+#  define PROCESS4_64 XXH_PROCESS4_64
+#  define PROCESS8_64 XXH_PROCESS8_64
+#else
+#  undef XXH_PROCESS1_64
+#  undef XXH_PROCESS4_64
+#  undef XXH_PROCESS8_64
+#endif
+
+XXH_FORCE_INLINE xxh_u64
+XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align)
+{
+    xxh_u64 h64;
+    if (input==NULL) XXH_ASSERT(len == 0);
+
+    if (len>=32) {
+        const xxh_u8* const bEnd = input + len;
+        const xxh_u8* const limit = bEnd - 31;
+        xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
+        xxh_u64 v2 = seed + XXH_PRIME64_2;
+        xxh_u64 v3 = seed + 0;
+        xxh_u64 v4 = seed - XXH_PRIME64_1;
+
+        do {
+            v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8;
+            v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8;
+            v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8;
+            v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8;
+        } while (input<limit);
+
+        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+        h64 = XXH64_mergeRound(h64, v1);
+        h64 = XXH64_mergeRound(h64, v2);
+        h64 = XXH64_mergeRound(h64, v3);
+        h64 = XXH64_mergeRound(h64, v4);
+
+    } else {
+        h64  = seed + XXH_PRIME64_5;
+    }
+
+    h64 += (xxh_u64) len;
+
+    return XXH64_finalize(h64, input, len, align);
+}
+
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, XXH64_hash_t seed)
+{
+#if 0
+    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+    XXH64_state_t state;
+    XXH64_reset(&state, seed);
+    XXH64_update(&state, (const xxh_u8*)input, len);
+    return XXH64_digest(&state);
+#else
+    if (XXH_FORCE_ALIGN_CHECK) {
+        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */
+            return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned);
+    }   }
+
+    return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned);
+
+#endif
+}
+
+/*******   Hash Streaming   *******/
+
+/*! @ingroup xxh64_family*/
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
+{
+    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+    XXH_free(statePtr);
+    return XXH_OK;
+}
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
+{
+    XXH_memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, XXH64_hash_t seed)
+{
+    XXH_ASSERT(statePtr != NULL);
+    memset(statePtr, 0, sizeof(*statePtr));
+    statePtr->v[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2;
+    statePtr->v[1] = seed + XXH_PRIME64_2;
+    statePtr->v[2] = seed + 0;
+    statePtr->v[3] = seed - XXH_PRIME64_1;
+    return XXH_OK;
+}
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH64_update (XXH64_state_t* state, const void* input, size_t len)
+{
+    if (input==NULL) {
+        XXH_ASSERT(len == 0);
+        return XXH_OK;
+    }
+
+    {   const xxh_u8* p = (const xxh_u8*)input;
+        const xxh_u8* const bEnd = p + len;
+
+        state->total_len += len;
+
+        if (state->memsize + len < 32) {  /* fill in tmp buffer */
+            XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len);
+            state->memsize += (xxh_u32)len;
+            return XXH_OK;
+        }
+
+        if (state->memsize) {   /* tmp buffer is full */
+            XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize);
+            state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0));
+            state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1));
+            state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2));
+            state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3));
+            p += 32 - state->memsize;
+            state->memsize = 0;
+        }
+
+        if (p+32 <= bEnd) {
+            const xxh_u8* const limit = bEnd - 32;
+
+            do {
+                state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8;
+                state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8;
+                state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8;
+                state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8;
+            } while (p<=limit);
+
+        }
+
+        if (p < bEnd) {
+            XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
+            state->memsize = (unsigned)(bEnd-p);
+        }
+    }
+
+    return XXH_OK;
+}
+
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest(const XXH64_state_t* state)
+{
+    xxh_u64 h64;
+
+    if (state->total_len >= 32) {
+        h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18);
+        h64 = XXH64_mergeRound(h64, state->v[0]);
+        h64 = XXH64_mergeRound(h64, state->v[1]);
+        h64 = XXH64_mergeRound(h64, state->v[2]);
+        h64 = XXH64_mergeRound(h64, state->v[3]);
+    } else {
+        h64  = state->v[2] /*seed*/ + XXH_PRIME64_5;
+    }
+
+    h64 += (xxh_u64) state->total_len;
+
+    return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned);
+}
+
+
+/******* Canonical representation   *******/
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
+{
+    /* XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); */
+    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
+    XXH_memcpy(dst, &hash, sizeof(*dst));
+}
+
+/*! @ingroup xxh64_family */
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
+{
+    return XXH_readBE64(src);
+}
+
+#ifndef XXH_NO_XXH3
+
+/* *********************************************************************
+*  XXH3
+*  New generation hash designed for speed on small keys and vectorization
+************************************************************************ */
+/*!
+ * @}
+ * @defgroup xxh3_impl XXH3 implementation
+ * @ingroup impl
+ * @{
+ */
+
+/* ===   Compiler specifics   === */
+
+#if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */
+#  define XXH_RESTRICT /* disable */
+#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* >= C99 */
+#  define XXH_RESTRICT   restrict
+#else
+/* Note: it might be useful to define __restrict or __restrict__ for some C++ compilers */
+#  define XXH_RESTRICT   /* disable */
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3))  \
+  || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \
+  || defined(__clang__)
+#    define XXH_likely(x) __builtin_expect(x, 1)
+#    define XXH_unlikely(x) __builtin_expect(x, 0)
+#else
+#    define XXH_likely(x) (x)
+#    define XXH_unlikely(x) (x)
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#  if defined(__ARM_NEON__) || defined(__ARM_NEON) \
+   || defined(__aarch64__)  || defined(_M_ARM) \
+   || defined(_M_ARM64)     || defined(_M_ARM64EC)
+#    define inline __inline__  /* circumvent a clang bug */
+#    include <arm_neon.h>
+#    undef inline
+#  elif defined(__AVX2__)
+#    include <immintrin.h>
+#  elif defined(__SSE2__)
+#    include <emmintrin.h>
+#  endif
+#endif
+
+#if defined(_MSC_VER)
+#  include <intrin.h>
+#endif
+
+/*
+ * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while
+ * remaining a true 64-bit/128-bit hash function.
+ *
+ * This is done by prioritizing a subset of 64-bit operations that can be
+ * emulated without too many steps on the average 32-bit machine.
+ *
+ * For example, these two lines seem similar, and run equally fast on 64-bit:
+ *
+ *   xxh_u64 x;
+ *   x ^= (x >> 47); // good
+ *   x ^= (x >> 13); // bad
+ *
+ * However, to a 32-bit machine, there is a major difference.
+ *
+ * x ^= (x >> 47) looks like this:
+ *
+ *   x.lo ^= (x.hi >> (47 - 32));
+ *
+ * while x ^= (x >> 13) looks like this:
+ *
+ *   // note: funnel shifts are not usually cheap.
+ *   x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13));
+ *   x.hi ^= (x.hi >> 13);
+ *
+ * The first one is significantly faster than the second, simply because the
+ * shift is larger than 32. This means:
+ *  - All the bits we need are in the upper 32 bits, so we can ignore the lower
+ *    32 bits in the shift.
+ *  - The shift result will always fit in the lower 32 bits, and therefore,
+ *    we can ignore the upper 32 bits in the xor.
+ *
+ * Thanks to this optimization, XXH3 only requires these features to be efficient:
+ *
+ *  - Usable unaligned access
+ *  - A 32-bit or 64-bit ALU
+ *      - If 32-bit, a decent ADC instruction
+ *  - A 32 or 64-bit multiply with a 64-bit result
+ *  - For the 128-bit variant, a decent byteswap helps short inputs.
+ *
+ * The first two are already required by XXH32, and almost all 32-bit and 64-bit
+ * platforms which can run XXH32 can run XXH3 efficiently.
+ *
+ * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one
+ * notable exception.
+ *
+ * First of all, Thumb-1 lacks support for the UMULL instruction which
+ * performs the important long multiply. This means numerous __aeabi_lmul
+ * calls.
+ *
+ * Second of all, the 8 functional registers are just not enough.
+ * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need
+ * Lo registers, and this shuffling results in thousands more MOVs than A32.
+ *
+ * A32 and T32 don't have this limitation. They can access all 14 registers,
+ * do a 32->64 multiply with UMULL, and the flexible operand allowing free
+ * shifts is helpful, too.
+ *
+ * Therefore, we do a quick sanity check.
+ *
+ * If compiling Thumb-1 for a target which supports ARM instructions, we will
+ * emit a warning, as it is not a "sane" platform to compile for.
+ *
+ * Usually, if this happens, it is because of an accident and you probably need
+ * to specify -march, as you likely meant to compile for a newer architecture.
+ *
+ * Credit: large sections of the vectorial and asm source code paths
+ *         have been contributed by @easyaspi314
+ */
+#if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM)
+#   warning "XXH3 is highly inefficient without ARM or Thumb-2."
+#endif
+
+/* ==========================================
+ * Vectorization detection
+ * ========================================== */
+
+#ifdef XXH_DOXYGEN
+/*!
+ * @ingroup tuning
+ * @brief Overrides the vectorization implementation chosen for XXH3.
+ *
+ * Can be defined to 0 to disable SIMD or any of the values mentioned in
+ * @ref XXH_VECTOR_TYPE.
+ *
+ * If this is not defined, it uses predefined macros to determine the best
+ * implementation.
+ */
+#  define XXH_VECTOR XXH_SCALAR
+/*!
+ * @ingroup tuning
+ * @brief Possible values for @ref XXH_VECTOR.
+ *
+ * Note that these are actually implemented as macros.
+ *
+ * If this is not defined, it is detected automatically.
+ * @ref XXH_X86DISPATCH overrides this.
+ */
+enum XXH_VECTOR_TYPE /* fake enum */ {
+    XXH_SCALAR = 0,  /*!< Portable scalar version */
+    XXH_SSE2   = 1,  /*!<
+                      * SSE2 for Pentium 4, Opteron, all x86_64.
+                      *
+                      * @note SSE2 is also guaranteed on Windows 10, macOS, and
+                      * Android x86.
+                      */
+    XXH_AVX2   = 2,  /*!< AVX2 for Haswell and Bulldozer */
+    XXH_AVX512 = 3,  /*!< AVX512 for Skylake and Icelake */
+    XXH_NEON   = 4,  /*!< NEON for most ARMv7-A and all AArch64 */
+    XXH_VSX    = 5,  /*!< VSX and ZVector for POWER8/z13 (64-bit) */
+};
+/*!
+ * @ingroup tuning
+ * @brief Selects the minimum alignment for XXH3's accumulators.
+ *
+ * When using SIMD, this should match the alignment required for said vector
+ * type, so, for example, 32 for AVX2.
+ *
+ * Default: Auto detected.
+ */
+#  define XXH_ACC_ALIGN 8
+#endif
+
+/* Actual definition */
+#ifndef XXH_DOXYGEN
+#  define XXH_SCALAR 0
+#  define XXH_SSE2   1
+#  define XXH_AVX2   2
+#  define XXH_AVX512 3
+#  define XXH_NEON   4
+#  define XXH_VSX    5
+#endif
+
+#ifndef XXH_VECTOR    /* can be defined on command line */
+#  if ( \
+        defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \
+     || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \
+   ) && ( \
+        defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \
+    || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \
+   )
+#    define XXH_VECTOR XXH_NEON
+#  elif defined(__AVX512F__)
+#    define XXH_VECTOR XXH_AVX512
+#  elif defined(__AVX2__)
+#    define XXH_VECTOR XXH_AVX2
+#  elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2))
+#    define XXH_VECTOR XXH_SSE2
+#  elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \
+     || (defined(__s390x__) && defined(__VEC__)) \
+     && defined(__GNUC__) /* TODO: IBM XL */
+#    define XXH_VECTOR XXH_VSX
+#  else
+#    define XXH_VECTOR XXH_SCALAR
+#  endif
+#endif
+
+/*
+ * Controls the alignment of the accumulator,
+ * for compatibility with aligned vector loads, which are usually faster.
+ */
+#ifndef XXH_ACC_ALIGN
+#  if defined(XXH_X86DISPATCH)
+#     define XXH_ACC_ALIGN 64  /* for compatibility with avx512 */
+#  elif XXH_VECTOR == XXH_SCALAR  /* scalar */
+#     define XXH_ACC_ALIGN 8
+#  elif XXH_VECTOR == XXH_SSE2  /* sse2 */
+#     define XXH_ACC_ALIGN 16
+#  elif XXH_VECTOR == XXH_AVX2  /* avx2 */
+#     define XXH_ACC_ALIGN 32
+#  elif XXH_VECTOR == XXH_NEON  /* neon */
+#     define XXH_ACC_ALIGN 16
+#  elif XXH_VECTOR == XXH_VSX   /* vsx */
+#     define XXH_ACC_ALIGN 16
+#  elif XXH_VECTOR == XXH_AVX512  /* avx512 */
+#     define XXH_ACC_ALIGN 64
+#  endif
+#endif
+
+#if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \
+    || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512
+#  define XXH_SEC_ALIGN XXH_ACC_ALIGN
+#else
+#  define XXH_SEC_ALIGN 8
+#endif
+
+/*
+ * UGLY HACK:
+ * GCC usually generates the best code with -O3 for xxHash.
+ *
+ * However, when targeting AVX2, it is overzealous in its unrolling resulting
+ * in code roughly 3/4 the speed of Clang.
+ *
+ * There are other issues, such as GCC splitting _mm256_loadu_si256 into
+ * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which
+ * only applies to Sandy and Ivy Bridge... which don't even support AVX2.
+ *
+ * That is why when compiling the AVX2 version, it is recommended to use either
+ *   -O2 -mavx2 -march=haswell
+ * or
+ *   -O2 -mavx2 -mno-avx256-split-unaligned-load
+ * for decent performance, or to use Clang instead.
+ *
+ * Fortunately, we can control the first one with a pragma that forces GCC into
+ * -O2, but the other one we can't control without "failed to inline always
+ * inline function due to target mismatch" warnings.
+ */
+#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
+  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+  && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */
+#  pragma GCC push_options
+#  pragma GCC optimize("-O2")
+#endif
+
+
+#if XXH_VECTOR == XXH_NEON
+/*
+ * NEON's setup for vmlal_u32 is a little more complicated than it is on
+ * SSE2, AVX2, and VSX.
+ *
+ * While PMULUDQ and VMULEUW both perform a mask, VMLAL.U32 performs an upcast.
+ *
+ * To do the same operation, the 128-bit 'Q' register needs to be split into
+ * two 64-bit 'D' registers, performing this operation::
+ *
+ *   [                a                 |                 b                ]
+ *            |              '---------. .--------'                |
+ *            |                         x                          |
+ *            |              .---------' '--------.                |
+ *   [ a & 0xFFFFFFFF | b & 0xFFFFFFFF ],[    a >> 32     |     b >> 32    ]
+ *
+ * Due to significant changes in aarch64, the fastest method for aarch64 is
+ * completely different than the fastest method for ARMv7-A.
+ *
+ * ARMv7-A treats D registers as unions overlaying Q registers, so modifying
+ * D11 will modify the high half of Q5. This is similar to how modifying AH
+ * will only affect bits 8-15 of AX on x86.
+ *
+ * VZIP takes two registers, and puts even lanes in one register and odd lanes
+ * in the other.
+ *
+ * On ARMv7-A, this strangely modifies both parameters in place instead of
+ * taking the usual 3-operand form.
+ *
+ * Therefore, if we want to do this, we can simply use a D-form VZIP.32 on the
+ * lower and upper halves of the Q register to end up with the high and low
+ * halves where we want - all in one instruction.
+ *
+ *   vzip.32   d10, d11       @ d10 = { d10[0], d11[0] }; d11 = { d10[1], d11[1] }
+ *
+ * Unfortunately we need inline assembly for this: Instructions modifying two
+ * registers at once is not possible in GCC or Clang's IR, and they have to
+ * create a copy.
+ *
+ * aarch64 requires a different approach.
+ *
+ * In order to make it easier to write a decent compiler for aarch64, many
+ * quirks were removed, such as conditional execution.
+ *
+ * NEON was also affected by this.
+ *
+ * aarch64 cannot access the high bits of a Q-form register, and writes to a
+ * D-form register zero the high bits, similar to how writes to W-form scalar
+ * registers (or DWORD registers on x86_64) work.
+ *
+ * The formerly free vget_high intrinsics now require a vext (with a few
+ * exceptions)
+ *
+ * Additionally, VZIP was replaced by ZIP1 and ZIP2, which are the equivalent
+ * of PUNPCKL* and PUNPCKH* in SSE, respectively, in order to only modify one
+ * operand.
+ *
+ * The equivalent of the VZIP.32 on the lower and upper halves would be this
+ * mess:
+ *
+ *   ext     v2.4s, v0.4s, v0.4s, #2 // v2 = { v0[2], v0[3], v0[0], v0[1] }
+ *   zip1    v1.2s, v0.2s, v2.2s     // v1 = { v0[0], v2[0] }
+ *   zip2    v0.2s, v0.2s, v1.2s     // v0 = { v0[1], v2[1] }
+ *
+ * Instead, we use a literal downcast, vmovn_u64 (XTN), and vshrn_n_u64 (SHRN):
+ *
+ *   shrn    v1.2s, v0.2d, #32  // v1 = (uint32x2_t)(v0 >> 32);
+ *   xtn     v0.2s, v0.2d       // v0 = (uint32x2_t)(v0 & 0xFFFFFFFF);
+ *
+ * This is available on ARMv7-A, but is less efficient than a single VZIP.32.
+ */
+
+/*!
+ * Function-like macro:
+ * void XXH_SPLIT_IN_PLACE(uint64x2_t &in, uint32x2_t &outLo, uint32x2_t &outHi)
+ * {
+ *     outLo = (uint32x2_t)(in & 0xFFFFFFFF);
+ *     outHi = (uint32x2_t)(in >> 32);
+ *     in = UNDEFINED;
+ * }
+ */
+# if !defined(XXH_NO_VZIP_HACK) /* define to disable */ \
+   && (defined(__GNUC__) || defined(__clang__)) \
+   && (defined(__arm__) || defined(__thumb__) || defined(_M_ARM))
+#  define XXH_SPLIT_IN_PLACE(in, outLo, outHi)                                              \
+    do {                                                                                    \
+      /* Undocumented GCC/Clang operand modifier: %e0 = lower D half, %f0 = upper D half */ \
+      /* https://github.com/gcc-mirror/gcc/blob/38cf91e5/gcc/config/arm/arm.c#L22486 */     \
+      /* https://github.com/llvm-mirror/llvm/blob/2c4ca683/lib/Target/ARM/ARMAsmPrinter.cpp#L399 */ \
+      __asm__("vzip.32  %e0, %f0" : "+w" (in));                                             \
+      (outLo) = vget_low_u32 (vreinterpretq_u32_u64(in));                                   \
+      (outHi) = vget_high_u32(vreinterpretq_u32_u64(in));                                   \
+   } while (0)
+# else
+#  define XXH_SPLIT_IN_PLACE(in, outLo, outHi)                                            \
+    do {                                                                                  \
+      (outLo) = vmovn_u64    (in);                                                        \
+      (outHi) = vshrn_n_u64  ((in), 32);                                                  \
+    } while (0)
+# endif
+
+/*!
+ * @ingroup tuning
+ * @brief Controls the NEON to scalar ratio for XXH3
+ *
+ * On AArch64 when not optimizing for size, XXH3 will run 6 lanes using NEON and
+ * 2 lanes on scalar by default.
+ *
+ * This can be set to 2, 4, 6, or 8. ARMv7 will default to all 8 NEON lanes, as the
+ * emulated 64-bit arithmetic is too slow.
+ *
+ * Modern ARM CPUs are _very_ sensitive to how their pipelines are used.
+ *
+ * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but it can't
+ * have more than 2 NEON (F0/F1) micro-ops. If you are only using NEON instructions,
+ * you are only using 2/3 of the CPU bandwidth.
+ *
+ * This is even more noticeable on the more advanced cores like the A76 which
+ * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once.
+ *
+ * Therefore, @ref XXH3_NEON_LANES lanes will be processed using NEON, and the
+ * remaining lanes will use scalar instructions. This improves the bandwidth
+ * and also gives the integer pipelines something to do besides twiddling loop
+ * counters and pointers.
+ *
+ * This change benefits CPUs with large micro-op buffers without negatively affecting
+ * other CPUs:
+ *
+ *  | Chipset               | Dispatch type       | NEON only | 6:2 hybrid | Diff. |
+ *  |:----------------------|:--------------------|----------:|-----------:|------:|
+ *  | Snapdragon 730 (A76)  | 2 NEON/8 micro-ops  |  8.8 GB/s |  10.1 GB/s |  ~16% |
+ *  | Snapdragon 835 (A73)  | 2 NEON/3 micro-ops  |  5.1 GB/s |   5.3 GB/s |   ~5% |
+ *  | Marvell PXA1928 (A53) | In-order dual-issue |  1.9 GB/s |   1.9 GB/s |    0% |
+ *
+ * It also seems to fix some bad codegen on GCC, making it almost as fast as clang.
+ *
+ * @see XXH3_accumulate_512_neon()
+ */
+# ifndef XXH3_NEON_LANES
+#  if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \
+   && !defined(__OPTIMIZE_SIZE__)
+#   define XXH3_NEON_LANES 6
+#  else
+#   define XXH3_NEON_LANES XXH_ACC_NB
+#  endif
+# endif
+#endif  /* XXH_VECTOR == XXH_NEON */
+
+/*
+ * VSX and Z Vector helpers.
+ *
+ * This is very messy, and any pull requests to clean this up are welcome.
+ *
+ * There are a lot of problems with supporting VSX and s390x, due to
+ * inconsistent intrinsics, spotty coverage, and multiple endiannesses.
+ */
+#if XXH_VECTOR == XXH_VSX
+#  if defined(__s390x__)
+#    include <s390intrin.h>
+#  else
+/* gcc's altivec.h can have the unwanted consequence to unconditionally
+ * #define bool, vector, and pixel keywords,
+ * with bad consequences for programs already using these keywords for other purposes.
+ * The paragraph defining these macros is skipped when __APPLE_ALTIVEC__ is defined.
+ * __APPLE_ALTIVEC__ is _generally_ defined automatically by the compiler,
+ * but it seems that, in some cases, it isn't.
+ * Force the build macro to be defined, so that keywords are not altered.
+ */
+#    if defined(__GNUC__) && !defined(__APPLE_ALTIVEC__)
+#      define __APPLE_ALTIVEC__
+#    endif
+#    include <altivec.h>
 #  endif
 
-#endif /* XXH_STATIC_LINKING_ONLY && XXH_STATIC_H_3543687687345 */
+typedef __vector unsigned long long xxh_u64x2;
+typedef __vector unsigned char xxh_u8x16;
+typedef __vector unsigned xxh_u32x4;
+
+# ifndef XXH_VSX_BE
+#  if defined(__BIG_ENDIAN__) \
+  || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#    define XXH_VSX_BE 1
+#  elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__
+#    warning "-maltivec=be is not recommended. Please use native endianness."
+#    define XXH_VSX_BE 1
+#  else
+#    define XXH_VSX_BE 0
+#  endif
+# endif /* !defined(XXH_VSX_BE) */
+
+# if XXH_VSX_BE
+#  if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__))
+#    define XXH_vec_revb vec_revb
+#  else
+/*!
+ * A polyfill for POWER9's vec_revb().
+ */
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val)
+{
+    xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+                                  0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 };
+    return vec_perm(val, val, vByteSwap);
+}
+#  endif
+# endif /* XXH_VSX_BE */
+
+/*!
+ * Performs an unaligned vector load and byte swaps it on big endian.
+ */
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr)
+{
+    xxh_u64x2 ret;
+    XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2));
+# if XXH_VSX_BE
+    ret = XXH_vec_revb(ret);
+# endif
+    return ret;
+}
+
+/*
+ * vec_mulo and vec_mule are very problematic intrinsics on PowerPC
+ *
+ * These intrinsics weren't added until GCC 8, despite existing for a while,
+ * and they are endian dependent. Also, their meaning swap depending on version.
+ * */
+# if defined(__s390x__)
+ /* s390x is always big endian, no issue on this platform */
+#  define XXH_vec_mulo vec_mulo
+#  define XXH_vec_mule vec_mule
+# elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw)
+/* Clang has a better way to control this, we can just use the builtin which doesn't swap. */
+#  define XXH_vec_mulo __builtin_altivec_vmulouw
+#  define XXH_vec_mule __builtin_altivec_vmuleuw
+# else
+/* gcc needs inline assembly */
+/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b)
+{
+    xxh_u64x2 result;
+    __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+    return result;
+}
+XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b)
+{
+    xxh_u64x2 result;
+    __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+    return result;
+}
+# endif /* XXH_vec_mulo, XXH_vec_mule */
+#endif /* XXH_VECTOR == XXH_VSX */
+
+
+/* prefetch
+ * can be disabled, by declaring XXH_NO_PREFETCH build macro */
+#if defined(XXH_NO_PREFETCH)
+#  define XXH_PREFETCH(ptr)  (void)(ptr)  /* disabled */
+#else
+#  if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))  /* _mm_prefetch() not defined outside of x86/x64 */
+#    include <mmintrin.h>   /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
+#    define XXH_PREFETCH(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
+#  elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
+#    define XXH_PREFETCH(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
+#  else
+#    define XXH_PREFETCH(ptr) (void)(ptr)  /* disabled */
+#  endif
+#endif  /* XXH_NO_PREFETCH */
+
+
+/* ==========================================
+ * XXH3 default settings
+ * ========================================== */
+
+#define XXH_SECRET_DEFAULT_SIZE 192   /* minimum XXH3_SECRET_SIZE_MIN */
+
+#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN)
+#  error "default keyset is not large enough"
+#endif
+
+/*! Pseudorandom secret taken directly from FARSH. */
+XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = {
+    0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
+    0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
+    0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
+    0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
+    0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
+    0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
+    0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
+    0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
+    0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
+    0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
+    0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
+    0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
+};
+
+
+#ifdef XXH_OLD_NAMES
+#  define kSecret XXH3_kSecret
+#endif
+
+#ifdef XXH_DOXYGEN
+/*!
+ * @brief Calculates a 32-bit to 64-bit long multiply.
+ *
+ * Implemented as a macro.
+ *
+ * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't
+ * need to (but it shouldn't need to anyways, it is about 7 instructions to do
+ * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we
+ * use that instead of the normal method.
+ *
+ * If you are compiling for platforms like Thumb-1 and don't have a better option,
+ * you may also want to write your own long multiply routine here.
+ *
+ * @param x, y Numbers to be multiplied
+ * @return 64-bit product of the low 32 bits of @p x and @p y.
+ */
+XXH_FORCE_INLINE xxh_u64
+XXH_mult32to64(xxh_u64 x, xxh_u64 y)
+{
+   return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF);
+}
+#elif defined(_MSC_VER) && defined(_M_IX86)
+#    define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y))
+#else
+/*
+ * Downcast + upcast is usually better than masking on older compilers like
+ * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers.
+ *
+ * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands
+ * and perform a full 64x64 multiply -- entirely redundant on 32-bit.
+ */
+#    define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y))
+#endif
+
+/*!
+ * @brief Calculates a 64->128-bit long multiply.
+ *
+ * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar
+ * version.
+ *
+ * @param lhs , rhs The 64-bit integers to be multiplied
+ * @return The 128-bit result represented in an @ref XXH128_hash_t.
+ */
+static XXH128_hash_t
+XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs)
+{
+    /*
+     * GCC/Clang __uint128_t method.
+     *
+     * On most 64-bit targets, GCC and Clang define a __uint128_t type.
+     * This is usually the best way as it usually uses a native long 64-bit
+     * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64.
+     *
+     * Usually.
+     *
+     * Despite being a 32-bit platform, Clang (and emscripten) define this type
+     * despite not having the arithmetic for it. This results in a laggy
+     * compiler builtin call which calculates a full 128-bit multiply.
+     * In that case it is best to use the portable one.
+     * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677
+     */
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \
+    && defined(__SIZEOF_INT128__) \
+    || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
+
+    __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs;
+    XXH128_hash_t r128;
+    r128.low64  = (xxh_u64)(product);
+    r128.high64 = (xxh_u64)(product >> 64);
+    return r128;
+
+    /*
+     * MSVC for x64's _umul128 method.
+     *
+     * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct);
+     *
+     * This compiles to single operand MUL on x64.
+     */
+#elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC)
+
+#ifndef _MSC_VER
+#   pragma intrinsic(_umul128)
+#endif
+    xxh_u64 product_high;
+    xxh_u64 const product_low = _umul128(lhs, rhs, &product_high);
+    XXH128_hash_t r128;
+    r128.low64  = product_low;
+    r128.high64 = product_high;
+    return r128;
+
+    /*
+     * MSVC for ARM64's __umulh method.
+     *
+     * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method.
+     */
+#elif defined(_M_ARM64) || defined(_M_ARM64EC)
+
+#ifndef _MSC_VER
+#   pragma intrinsic(__umulh)
+#endif
+    XXH128_hash_t r128;
+    r128.low64  = lhs * rhs;
+    r128.high64 = __umulh(lhs, rhs);
+    return r128;
+
+#else
+    /*
+     * Portable scalar method. Optimized for 32-bit and 64-bit ALUs.
+     *
+     * This is a fast and simple grade school multiply, which is shown below
+     * with base 10 arithmetic instead of base 0x100000000.
+     *
+     *           9 3 // D2 lhs = 93
+     *         x 7 5 // D2 rhs = 75
+     *     ----------
+     *           1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15
+     *         4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45
+     *         2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21
+     *     + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63
+     *     ---------
+     *         2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27
+     *     + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67
+     *     ---------
+     *       6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975
+     *
+     * The reasons for adding the products like this are:
+     *  1. It avoids manual carry tracking. Just like how
+     *     (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX.
+     *     This avoids a lot of complexity.
+     *
+     *  2. It hints for, and on Clang, compiles to, the powerful UMAAL
+     *     instruction available in ARM's Digital Signal Processing extension
+     *     in 32-bit ARMv6 and later, which is shown below:
+     *
+     *         void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm)
+     *         {
+     *             xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm;
+     *             *RdLo = (xxh_u32)(product & 0xFFFFFFFF);
+     *             *RdHi = (xxh_u32)(product >> 32);
+     *         }
+     *
+     *     This instruction was designed for efficient long multiplication, and
+     *     allows this to be calculated in only 4 instructions at speeds
+     *     comparable to some 64-bit ALUs.
+     *
+     *  3. It isn't terrible on other platforms. Usually this will be a couple
+     *     of 32-bit ADD/ADCs.
+     */
+
+    /* First calculate all of the cross products. */
+    xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF);
+    xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32,        rhs & 0xFFFFFFFF);
+    xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32);
+    xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32,        rhs >> 32);
+
+    /* Now add the products together. These will never overflow. */
+    xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi;
+    xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32)        + hi_hi;
+    xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF);
+
+    XXH128_hash_t r128;
+    r128.low64  = lower;
+    r128.high64 = upper;
+    return r128;
+#endif
+}
+
+/*!
+ * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it.
+ *
+ * The reason for the separate function is to prevent passing too many structs
+ * around by value. This will hopefully inline the multiply, but we don't force it.
+ *
+ * @param lhs , rhs The 64-bit integers to multiply
+ * @return The low 64 bits of the product XOR'd by the high 64 bits.
+ * @see XXH_mult64to128()
+ */
+static xxh_u64
+XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs)
+{
+    XXH128_hash_t product = XXH_mult64to128(lhs, rhs);
+    return product.low64 ^ product.high64;
+}
+
+/*! Seems to produce slightly better code on GCC for some reason. */
+XXH_FORCE_INLINE xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift)
+{
+    XXH_ASSERT(0 <= shift && shift < 64);
+    return v64 ^ (v64 >> shift);
+}
+
+/*
+ * This is a fast avalanche stage,
+ * suitable when input bits are already partially mixed
+ */
+static XXH64_hash_t XXH3_avalanche(xxh_u64 h64)
+{
+    h64 = XXH_xorshift64(h64, 37);
+    h64 *= 0x165667919E3779F9ULL;
+    h64 = XXH_xorshift64(h64, 32);
+    return h64;
+}
+
+/*
+ * This is a stronger avalanche,
+ * inspired by Pelle Evensen's rrmxmx
+ * preferable when input has not been previously mixed
+ */
+static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len)
+{
+    /* this mix is inspired by Pelle Evensen's rrmxmx */
+    h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24);
+    h64 *= 0x9FB21C651E98DF25ULL;
+    h64 ^= (h64 >> 35) + len ;
+    h64 *= 0x9FB21C651E98DF25ULL;
+    return XXH_xorshift64(h64, 28);
+}
+
+
+/* ==========================================
+ * Short keys
+ * ==========================================
+ * One of the shortcomings of XXH32 and XXH64 was that their performance was
+ * sub-optimal on short lengths. It used an iterative algorithm which strongly
+ * favored lengths that were a multiple of 4 or 8.
+ *
+ * Instead of iterating over individual inputs, we use a set of single shot
+ * functions which piece together a range of lengths and operate in constant time.
+ *
+ * Additionally, the number of multiplies has been significantly reduced. This
+ * reduces latency, especially when emulating 64-bit multiplies on 32-bit.
+ *
+ * Depending on the platform, this may or may not be faster than XXH32, but it
+ * is almost guaranteed to be faster than XXH64.
+ */
+
+/*
+ * At very short lengths, there isn't enough input to fully hide secrets, or use
+ * the entire secret.
+ *
+ * There is also only a limited amount of mixing we can do before significantly
+ * impacting performance.
+ *
+ * Therefore, we use different sections of the secret and always mix two secret
+ * samples with an XOR. This should have no effect on performance on the
+ * seedless or withSeed variants because everything _should_ be constant folded
+ * by modern compilers.
+ *
+ * The XOR mixing hides individual parts of the secret and increases entropy.
+ *
+ * This adds an extra layer of strength for custom secrets.
+ */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(1 <= len && len <= 3);
+    XXH_ASSERT(secret != NULL);
+    /*
+     * len = 1: combined = { input[0], 0x01, input[0], input[0] }
+     * len = 2: combined = { input[1], 0x02, input[0], input[1] }
+     * len = 3: combined = { input[2], 0x03, input[0], input[1] }
+     */
+    {   xxh_u8  const c1 = input[0];
+        xxh_u8  const c2 = input[len >> 1];
+        xxh_u8  const c3 = input[len - 1];
+        xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2  << 24)
+                               | ((xxh_u32)c3 <<  0) | ((xxh_u32)len << 8);
+        xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
+        xxh_u64 const keyed = (xxh_u64)combined ^ bitflip;
+        return XXH64_avalanche(keyed);
+    }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(secret != NULL);
+    XXH_ASSERT(4 <= len && len <= 8);
+    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
+    {   xxh_u32 const input1 = XXH_readLE32(input);
+        xxh_u32 const input2 = XXH_readLE32(input + len - 4);
+        xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed;
+        xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32);
+        xxh_u64 const keyed = input64 ^ bitflip;
+        return XXH3_rrmxmx(keyed, len);
+    }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(secret != NULL);
+    XXH_ASSERT(9 <= len && len <= 16);
+    {   xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed;
+        xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed;
+        xxh_u64 const input_lo = XXH_readLE64(input)           ^ bitflip1;
+        xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2;
+        xxh_u64 const acc = len
+                          + XXH_swap64(input_lo) + input_hi
+                          + XXH3_mul128_fold64(input_lo, input_hi);
+        return XXH3_avalanche(acc);
+    }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(len <= 16);
+    {   if (XXH_likely(len >  8)) return XXH3_len_9to16_64b(input, len, secret, seed);
+        if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed);
+        if (len) return XXH3_len_1to3_64b(input, len, secret, seed);
+        return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64)));
+    }
+}
+
+/*
+ * DISCLAIMER: There are known *seed-dependent* multicollisions here due to
+ * multiplication by zero, affecting hashes of lengths 17 to 240.
+ *
+ * However, they are very unlikely.
+ *
+ * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all
+ * unseeded non-cryptographic hashes, it does not attempt to defend itself
+ * against specially crafted inputs, only random inputs.
+ *
+ * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes
+ * cancelling out the secret is taken an arbitrary number of times (addressed
+ * in XXH3_accumulate_512), this collision is very unlikely with random inputs
+ * and/or proper seeding:
+ *
+ * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a
+ * function that is only called up to 16 times per hash with up to 240 bytes of
+ * input.
+ *
+ * This is not too bad for a non-cryptographic hash function, especially with
+ * only 64 bit outputs.
+ *
+ * The 128-bit variant (which trades some speed for strength) is NOT affected
+ * by this, although it is always a good idea to use a proper seed if you care
+ * about strength.
+ */
+XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input,
+                                     const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64)
+{
+#if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+  && defined(__i386__) && defined(__SSE2__)  /* x86 + SSE2 */ \
+  && !defined(XXH_ENABLE_AUTOVECTORIZE)      /* Define to disable like XXH32 hack */
+    /*
+     * UGLY HACK:
+     * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in
+     * slower code.
+     *
+     * By forcing seed64 into a register, we disrupt the cost model and
+     * cause it to scalarize. See `XXH32_round()`
+     *
+     * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600,
+     * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on
+     * GCC 9.2, despite both emitting scalar code.
+     *
+     * GCC generates much better scalar code than Clang for the rest of XXH3,
+     * which is why finding a more optimal codepath is an interest.
+     */
+    XXH_COMPILER_GUARD(seed64);
+#endif
+    {   xxh_u64 const input_lo = XXH_readLE64(input);
+        xxh_u64 const input_hi = XXH_readLE64(input+8);
+        return XXH3_mul128_fold64(
+            input_lo ^ (XXH_readLE64(secret)   + seed64),
+            input_hi ^ (XXH_readLE64(secret+8) - seed64)
+        );
+    }
+}
+
+/* For mid range keys, XXH3 uses a Mum-hash variant. */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
+                     const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                     XXH64_hash_t seed)
+{
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+    XXH_ASSERT(16 < len && len <= 128);
+
+    {   xxh_u64 acc = len * XXH_PRIME64_1;
+        if (len > 32) {
+            if (len > 64) {
+                if (len > 96) {
+                    acc += XXH3_mix16B(input+48, secret+96, seed);
+                    acc += XXH3_mix16B(input+len-64, secret+112, seed);
+                }
+                acc += XXH3_mix16B(input+32, secret+64, seed);
+                acc += XXH3_mix16B(input+len-48, secret+80, seed);
+            }
+            acc += XXH3_mix16B(input+16, secret+32, seed);
+            acc += XXH3_mix16B(input+len-32, secret+48, seed);
+        }
+        acc += XXH3_mix16B(input+0, secret+0, seed);
+        acc += XXH3_mix16B(input+len-16, secret+16, seed);
+
+        return XXH3_avalanche(acc);
+    }
+}
+
+#define XXH3_MIDSIZE_MAX 240
+
+XXH_NO_INLINE XXH64_hash_t
+XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len,
+                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                      XXH64_hash_t seed)
+{
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+    #define XXH3_MIDSIZE_STARTOFFSET 3
+    #define XXH3_MIDSIZE_LASTOFFSET  17
+
+    {   xxh_u64 acc = len * XXH_PRIME64_1;
+        int const nbRounds = (int)len / 16;
+        int i;
+        for (i=0; i<8; i++) {
+            acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed);
+        }
+        acc = XXH3_avalanche(acc);
+        XXH_ASSERT(nbRounds >= 8);
+#if defined(__clang__)                                /* Clang */ \
+    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \
+    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */
+        /*
+         * UGLY HACK:
+         * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86.
+         * In everywhere else, it uses scalar code.
+         *
+         * For 64->128-bit multiplies, even if the NEON was 100% optimal, it
+         * would still be slower than UMAAL (see XXH_mult64to128).
+         *
+         * Unfortunately, Clang doesn't handle the long multiplies properly and
+         * converts them to the nonexistent "vmulq_u64" intrinsic, which is then
+         * scalarized into an ugly mess of VMOV.32 instructions.
+         *
+         * This mess is difficult to avoid without turning autovectorization
+         * off completely, but they are usually relatively minor and/or not
+         * worth it to fix.
+         *
+         * This loop is the easiest to fix, as unlike XXH32, this pragma
+         * _actually works_ because it is a loop vectorization instead of an
+         * SLP vectorization.
+         */
+        #pragma clang loop vectorize(disable)
+#endif
+        for (i=8 ; i < nbRounds; i++) {
+            acc += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed);
+        }
+        /* last bytes */
+        acc += XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);
+        return XXH3_avalanche(acc);
+    }
+}
+
+
+/* =======     Long Keys     ======= */
+
+#define XXH_STRIPE_LEN 64
+#define XXH_SECRET_CONSUME_RATE 8   /* nb of secret bytes consumed at each accumulation */
+#define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64))
+
+#ifdef XXH_OLD_NAMES
+#  define STRIPE_LEN XXH_STRIPE_LEN
+#  define ACC_NB XXH_ACC_NB
+#endif
+
+XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64)
+{
+    if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64);
+    XXH_memcpy(dst, &v64, sizeof(v64));
+}
+
+/* Several intrinsic functions below are supposed to accept __int64 as argument,
+ * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ .
+ * However, several environments do not define __int64 type,
+ * requiring a workaround.
+ */
+#if !defined (__VMS) \
+  && (defined (__cplusplus) \
+  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+    typedef int64_t xxh_i64;
+#else
+    /* the following type must have a width of 64-bit */
+    typedef long long xxh_i64;
+#endif
+
+
+/*
+ * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized.
+ *
+ * It is a hardened version of UMAC, based off of FARSH's implementation.
+ *
+ * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD
+ * implementations, and it is ridiculously fast.
+ *
+ * We harden it by mixing the original input to the accumulators as well as the product.
+ *
+ * This means that in the (relatively likely) case of a multiply by zero, the
+ * original input is preserved.
+ *
+ * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve
+ * cross-pollination, as otherwise the upper and lower halves would be
+ * essentially independent.
+ *
+ * This doesn't matter on 64-bit hashes since they all get merged together in
+ * the end, so we skip the extra step.
+ *
+ * Both XXH3_64bits and XXH3_128bits use this subroutine.
+ */
+
+#if (XXH_VECTOR == XXH_AVX512) \
+     || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0)
+
+#ifndef XXH_TARGET_AVX512
+# define XXH_TARGET_AVX512  /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc,
+                     const void* XXH_RESTRICT input,
+                     const void* XXH_RESTRICT secret)
+{
+    __m512i* const xacc = (__m512i *) acc;
+    XXH_ASSERT((((size_t)acc) & 63) == 0);
+    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));
+
+    {
+        /* data_vec    = input[0]; */
+        __m512i const data_vec    = _mm512_loadu_si512   (input);
+        /* key_vec     = secret[0]; */
+        __m512i const key_vec     = _mm512_loadu_si512   (secret);
+        /* data_key    = data_vec ^ key_vec; */
+        __m512i const data_key    = _mm512_xor_si512     (data_vec, key_vec);
+        /* data_key_lo = data_key >> 32; */
+        __m512i const data_key_lo = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1));
+        /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+        __m512i const product     = _mm512_mul_epu32     (data_key, data_key_lo);
+        /* xacc[0] += swap(data_vec); */
+        __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2));
+        __m512i const sum       = _mm512_add_epi64(*xacc, data_swap);
+        /* xacc[0] += product; */
+        *xacc = _mm512_add_epi64(product, sum);
+    }
+}
+
+/*
+ * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing.
+ *
+ * Multiplication isn't perfect, as explained by Google in HighwayHash:
+ *
+ *  // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to
+ *  // varying degrees. In descending order of goodness, bytes
+ *  // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32.
+ *  // As expected, the upper and lower bytes are much worse.
+ *
+ * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291
+ *
+ * Since our algorithm uses a pseudorandom secret to add some variance into the
+ * mix, we don't need to (or want to) mix as often or as much as HighwayHash does.
+ *
+ * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid
+ * extraction.
+ *
+ * Both XXH3_64bits and XXH3_128bits use this subroutine.
+ */
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 63) == 0);
+    XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i));
+    {   __m512i* const xacc = (__m512i*) acc;
+        const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1);
+
+        /* xacc[0] ^= (xacc[0] >> 47) */
+        __m512i const acc_vec     = *xacc;
+        __m512i const shifted     = _mm512_srli_epi64    (acc_vec, 47);
+        __m512i const data_vec    = _mm512_xor_si512     (acc_vec, shifted);
+        /* xacc[0] ^= secret; */
+        __m512i const key_vec     = _mm512_loadu_si512   (secret);
+        __m512i const data_key    = _mm512_xor_si512     (data_vec, key_vec);
+
+        /* xacc[0] *= XXH_PRIME32_1; */
+        __m512i const data_key_hi = _mm512_shuffle_epi32 (data_key, (_MM_PERM_ENUM)_MM_SHUFFLE(0, 3, 0, 1));
+        __m512i const prod_lo     = _mm512_mul_epu32     (data_key, prime32);
+        __m512i const prod_hi     = _mm512_mul_epu32     (data_key_hi, prime32);
+        *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32));
+    }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX512 void
+XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0);
+    XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64);
+    XXH_ASSERT(((size_t)customSecret & 63) == 0);
+    (void)(&XXH_writeLE64);
+    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i);
+        __m512i const seed = _mm512_mask_set1_epi64(_mm512_set1_epi64((xxh_i64)seed64), 0xAA, (xxh_i64)(0U - seed64));
+
+        const __m512i* const src  = (const __m512i*) ((const void*) XXH3_kSecret);
+              __m512i* const dest = (      __m512i*) customSecret;
+        int i;
+        XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */
+        XXH_ASSERT(((size_t)dest & 63) == 0);
+        for (i=0; i < nbRounds; ++i) {
+            /* GCC has a bug, _mm512_stream_load_si512 accepts 'void*', not 'void const*',
+             * this will warn "discards 'const' qualifier". */
+            union {
+                const __m512i* cp;
+                void* p;
+            } remote_const_void;
+            remote_const_void.cp = src + i;
+            dest[i] = _mm512_add_epi64(_mm512_stream_load_si512(remote_const_void.p), seed);
+    }   }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_AVX2) \
+    || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0)
+
+#ifndef XXH_TARGET_AVX2
+# define XXH_TARGET_AVX2  /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void
+XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc,
+                    const void* XXH_RESTRICT input,
+                    const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 31) == 0);
+    {   __m256i* const xacc    =       (__m256i *) acc;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm256_loadu_si256 requires  a const __m256i * pointer for some reason. */
+        const         __m256i* const xinput  = (const __m256i *) input;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
+        const         __m256i* const xsecret = (const __m256i *) secret;
+
+        size_t i;
+        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
+            /* data_vec    = xinput[i]; */
+            __m256i const data_vec    = _mm256_loadu_si256    (xinput+i);
+            /* key_vec     = xsecret[i]; */
+            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);
+            /* data_key    = data_vec ^ key_vec; */
+            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);
+            /* data_key_lo = data_key >> 32; */
+            __m256i const data_key_lo = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+            __m256i const product     = _mm256_mul_epu32     (data_key, data_key_lo);
+            /* xacc[i] += swap(data_vec); */
+            __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2));
+            __m256i const sum       = _mm256_add_epi64(xacc[i], data_swap);
+            /* xacc[i] += product; */
+            xacc[i] = _mm256_add_epi64(product, sum);
+    }   }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void
+XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 31) == 0);
+    {   __m256i* const xacc = (__m256i*) acc;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */
+        const         __m256i* const xsecret = (const __m256i *) secret;
+        const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1);
+
+        size_t i;
+        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) {
+            /* xacc[i] ^= (xacc[i] >> 47) */
+            __m256i const acc_vec     = xacc[i];
+            __m256i const shifted     = _mm256_srli_epi64    (acc_vec, 47);
+            __m256i const data_vec    = _mm256_xor_si256     (acc_vec, shifted);
+            /* xacc[i] ^= xsecret; */
+            __m256i const key_vec     = _mm256_loadu_si256   (xsecret+i);
+            __m256i const data_key    = _mm256_xor_si256     (data_vec, key_vec);
+
+            /* xacc[i] *= XXH_PRIME32_1; */
+            __m256i const data_key_hi = _mm256_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+            __m256i const prod_lo     = _mm256_mul_epu32     (data_key, prime32);
+            __m256i const prod_hi     = _mm256_mul_epu32     (data_key_hi, prime32);
+            xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32));
+        }
+    }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0);
+    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6);
+    XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64);
+    (void)(&XXH_writeLE64);
+    XXH_PREFETCH(customSecret);
+    {   __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64);
+
+        const __m256i* const src  = (const __m256i*) ((const void*) XXH3_kSecret);
+              __m256i*       dest = (      __m256i*) customSecret;
+
+#       if defined(__GNUC__) || defined(__clang__)
+        /*
+         * On GCC & Clang, marking 'dest' as modified will cause the compiler:
+         *   - do not extract the secret from sse registers in the internal loop
+         *   - use less common registers, and avoid pushing these reg into stack
+         */
+        XXH_COMPILER_GUARD(dest);
+#       endif
+        XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */
+        XXH_ASSERT(((size_t)dest & 31) == 0);
+
+        /* GCC -O2 need unroll loop manually */
+        dest[0] = _mm256_add_epi64(_mm256_stream_load_si256(src+0), seed);
+        dest[1] = _mm256_add_epi64(_mm256_stream_load_si256(src+1), seed);
+        dest[2] = _mm256_add_epi64(_mm256_stream_load_si256(src+2), seed);
+        dest[3] = _mm256_add_epi64(_mm256_stream_load_si256(src+3), seed);
+        dest[4] = _mm256_add_epi64(_mm256_stream_load_si256(src+4), seed);
+        dest[5] = _mm256_add_epi64(_mm256_stream_load_si256(src+5), seed);
+    }
+}
+
+#endif
+
+/* x86dispatch always generates SSE2 */
+#if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH)
+
+#ifndef XXH_TARGET_SSE2
+# define XXH_TARGET_SSE2  /* disable attribute target */
+#endif
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void
+XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc,
+                    const void* XXH_RESTRICT input,
+                    const void* XXH_RESTRICT secret)
+{
+    /* SSE2 is just a half-scale version of the AVX2 version. */
+    XXH_ASSERT((((size_t)acc) & 15) == 0);
+    {   __m128i* const xacc    =       (__m128i *) acc;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+        const         __m128i* const xinput  = (const __m128i *) input;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+        const         __m128i* const xsecret = (const __m128i *) secret;
+
+        size_t i;
+        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
+            /* data_vec    = xinput[i]; */
+            __m128i const data_vec    = _mm_loadu_si128   (xinput+i);
+            /* key_vec     = xsecret[i]; */
+            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);
+            /* data_key    = data_vec ^ key_vec; */
+            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);
+            /* data_key_lo = data_key >> 32; */
+            __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+            /* product     = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */
+            __m128i const product     = _mm_mul_epu32     (data_key, data_key_lo);
+            /* xacc[i] += swap(data_vec); */
+            __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2));
+            __m128i const sum       = _mm_add_epi64(xacc[i], data_swap);
+            /* xacc[i] += product; */
+            xacc[i] = _mm_add_epi64(product, sum);
+    }   }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void
+XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 15) == 0);
+    {   __m128i* const xacc = (__m128i*) acc;
+        /* Unaligned. This is mainly for pointer arithmetic, and because
+         * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */
+        const         __m128i* const xsecret = (const __m128i *) secret;
+        const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1);
+
+        size_t i;
+        for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) {
+            /* xacc[i] ^= (xacc[i] >> 47) */
+            __m128i const acc_vec     = xacc[i];
+            __m128i const shifted     = _mm_srli_epi64    (acc_vec, 47);
+            __m128i const data_vec    = _mm_xor_si128     (acc_vec, shifted);
+            /* xacc[i] ^= xsecret[i]; */
+            __m128i const key_vec     = _mm_loadu_si128   (xsecret+i);
+            __m128i const data_key    = _mm_xor_si128     (data_vec, key_vec);
+
+            /* xacc[i] *= XXH_PRIME32_1; */
+            __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1));
+            __m128i const prod_lo     = _mm_mul_epu32     (data_key, prime32);
+            __m128i const prod_hi     = _mm_mul_epu32     (data_key_hi, prime32);
+            xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32));
+        }
+    }
+}
+
+XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
+    (void)(&XXH_writeLE64);
+    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i);
+
+#       if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900
+        /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */
+        XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) };
+        __m128i const seed = _mm_load_si128((__m128i const*)seed64x2);
+#       else
+        __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64);
+#       endif
+        int i;
+
+        const void* const src16 = XXH3_kSecret;
+        __m128i* dst16 = (__m128i*) customSecret;
+#       if defined(__GNUC__) || defined(__clang__)
+        /*
+         * On GCC & Clang, marking 'dest' as modified will cause the compiler:
+         *   - do not extract the secret from sse registers in the internal loop
+         *   - use less common registers, and avoid pushing these reg into stack
+         */
+        XXH_COMPILER_GUARD(dst16);
+#       endif
+        XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */
+        XXH_ASSERT(((size_t)dst16 & 15) == 0);
+
+        for (i=0; i < nbRounds; ++i) {
+            dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed);
+    }   }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_NEON)
+
+/* forward declarations for the scalar routines */
+XXH_FORCE_INLINE void
+XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input,
+                 void const* XXH_RESTRICT secret, size_t lane);
+
+XXH_FORCE_INLINE void
+XXH3_scalarScrambleRound(void* XXH_RESTRICT acc,
+                         void const* XXH_RESTRICT secret, size_t lane);
+
+/*!
+ * @internal
+ * @brief The bulk processing loop for NEON.
+ *
+ * The NEON code path is actually partially scalar when running on AArch64. This
+ * is to optimize the pipelining and can have up to 15% speedup depending on the
+ * CPU, and it also mitigates some GCC codegen issues.
+ *
+ * @see XXH3_NEON_LANES for configuring this and details about this optimization.
+ */
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_neon( void* XXH_RESTRICT acc,
+                    const void* XXH_RESTRICT input,
+                    const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 15) == 0);
+    XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0);
+    {
+        uint64x2_t* const xacc = (uint64x2_t *) acc;
+        /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */
+        uint8_t const* const xinput = (const uint8_t *) input;
+        uint8_t const* const xsecret  = (const uint8_t *) secret;
+
+        size_t i;
+        /* NEON for the first few lanes (these loops are normally interleaved) */
+        for (i=0; i < XXH3_NEON_LANES / 2; i++) {
+            /* data_vec = xinput[i]; */
+            uint8x16_t data_vec    = vld1q_u8(xinput  + (i * 16));
+            /* key_vec  = xsecret[i];  */
+            uint8x16_t key_vec     = vld1q_u8(xsecret + (i * 16));
+            uint64x2_t data_key;
+            uint32x2_t data_key_lo, data_key_hi;
+            /* xacc[i] += swap(data_vec); */
+            uint64x2_t const data64  = vreinterpretq_u64_u8(data_vec);
+            uint64x2_t const swapped = vextq_u64(data64, data64, 1);
+            xacc[i] = vaddq_u64 (xacc[i], swapped);
+            /* data_key = data_vec ^ key_vec; */
+            data_key = vreinterpretq_u64_u8(veorq_u8(data_vec, key_vec));
+            /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF);
+             * data_key_hi = (uint32x2_t) (data_key >> 32);
+             * data_key = UNDEFINED; */
+            XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi);
+            /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */
+            xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi);
+
+        }
+        /* Scalar for the remainder. This may be a zero iteration loop. */
+        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {
+            XXH3_scalarRound(acc, input, secret, i);
+        }
+    }
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 15) == 0);
+
+    {   uint64x2_t* xacc       = (uint64x2_t*) acc;
+        uint8_t const* xsecret = (uint8_t const*) secret;
+        uint32x2_t prime       = vdup_n_u32 (XXH_PRIME32_1);
+
+        size_t i;
+        /* NEON for the first few lanes (these loops are normally interleaved) */
+        for (i=0; i < XXH3_NEON_LANES / 2; i++) {
+            /* xacc[i] ^= (xacc[i] >> 47); */
+            uint64x2_t acc_vec  = xacc[i];
+            uint64x2_t shifted  = vshrq_n_u64 (acc_vec, 47);
+            uint64x2_t data_vec = veorq_u64   (acc_vec, shifted);
+
+            /* xacc[i] ^= xsecret[i]; */
+            uint8x16_t key_vec  = vld1q_u8    (xsecret + (i * 16));
+            uint64x2_t data_key = veorq_u64   (data_vec, vreinterpretq_u64_u8(key_vec));
+
+            /* xacc[i] *= XXH_PRIME32_1 */
+            uint32x2_t data_key_lo, data_key_hi;
+            /* data_key_lo = (uint32x2_t) (xacc[i] & 0xFFFFFFFF);
+             * data_key_hi = (uint32x2_t) (xacc[i] >> 32);
+             * xacc[i] = UNDEFINED; */
+            XXH_SPLIT_IN_PLACE(data_key, data_key_lo, data_key_hi);
+            {   /*
+                 * prod_hi = (data_key >> 32) * XXH_PRIME32_1;
+                 *
+                 * Avoid vmul_u32 + vshll_n_u32 since Clang 6 and 7 will
+                 * incorrectly "optimize" this:
+                 *   tmp     = vmul_u32(vmovn_u64(a), vmovn_u64(b));
+                 *   shifted = vshll_n_u32(tmp, 32);
+                 * to this:
+                 *   tmp     = "vmulq_u64"(a, b); // no such thing!
+                 *   shifted = vshlq_n_u64(tmp, 32);
+                 *
+                 * However, unlike SSE, Clang lacks a 64-bit multiply routine
+                 * for NEON, and it scalarizes two 64-bit multiplies instead.
+                 *
+                 * vmull_u32 has the same timing as vmul_u32, and it avoids
+                 * this bug completely.
+                 * See https://bugs.llvm.org/show_bug.cgi?id=39967
+                 */
+                uint64x2_t prod_hi = vmull_u32 (data_key_hi, prime);
+                /* xacc[i] = prod_hi << 32; */
+                xacc[i] = vshlq_n_u64(prod_hi, 32);
+                /* xacc[i] += (prod_hi & 0xFFFFFFFF) * XXH_PRIME32_1; */
+                xacc[i] = vmlal_u32(xacc[i], data_key_lo, prime);
+            }
+        }
+        /* Scalar for the remainder. This may be a zero iteration loop. */
+        for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) {
+            XXH3_scalarScrambleRound(acc, secret, i);
+        }
+    }
+}
+
+#endif
+
+#if (XXH_VECTOR == XXH_VSX)
+
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_vsx(  void* XXH_RESTRICT acc,
+                    const void* XXH_RESTRICT input,
+                    const void* XXH_RESTRICT secret)
+{
+    /* presumed aligned */
+    unsigned int* const xacc = (unsigned int*) acc;
+    xxh_u64x2 const* const xinput   = (xxh_u64x2 const*) input;   /* no alignment restriction */
+    xxh_u64x2 const* const xsecret  = (xxh_u64x2 const*) secret;    /* no alignment restriction */
+    xxh_u64x2 const v32 = { 32, 32 };
+    size_t i;
+    for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
+        /* data_vec = xinput[i]; */
+        xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + i);
+        /* key_vec = xsecret[i]; */
+        xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + i);
+        xxh_u64x2 const data_key = data_vec ^ key_vec;
+        /* shuffled = (data_key << 32) | (data_key >> 32); */
+        xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32);
+        /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */
+        xxh_u64x2 const product  = XXH_vec_mulo((xxh_u32x4)data_key, shuffled);
+        /* acc_vec = xacc[i]; */
+        xxh_u64x2 acc_vec        = (xxh_u64x2)vec_xl(0, xacc + 4 * i);
+        acc_vec += product;
+
+        /* swap high and low halves */
+#ifdef __s390x__
+        acc_vec += vec_permi(data_vec, data_vec, 2);
+#else
+        acc_vec += vec_xxpermdi(data_vec, data_vec, 2);
+#endif
+        /* xacc[i] = acc_vec; */
+        vec_xst((xxh_u32x4)acc_vec, 0, xacc + 4 * i);
+    }
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    XXH_ASSERT((((size_t)acc) & 15) == 0);
+
+    {         xxh_u64x2* const xacc    =       (xxh_u64x2*) acc;
+        const xxh_u64x2* const xsecret = (const xxh_u64x2*) secret;
+        /* constants */
+        xxh_u64x2 const v32  = { 32, 32 };
+        xxh_u64x2 const v47 = { 47, 47 };
+        xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 };
+        size_t i;
+        for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) {
+            /* xacc[i] ^= (xacc[i] >> 47); */
+            xxh_u64x2 const acc_vec  = xacc[i];
+            xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47);
+
+            /* xacc[i] ^= xsecret[i]; */
+            xxh_u64x2 const key_vec  = XXH_vec_loadu(xsecret + i);
+            xxh_u64x2 const data_key = data_vec ^ key_vec;
+
+            /* xacc[i] *= XXH_PRIME32_1 */
+            /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF);  */
+            xxh_u64x2 const prod_even  = XXH_vec_mule((xxh_u32x4)data_key, prime);
+            /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32);  */
+            xxh_u64x2 const prod_odd  = XXH_vec_mulo((xxh_u32x4)data_key, prime);
+            xacc[i] = prod_odd + (prod_even << v32);
+    }   }
+}
+
+#endif
+
+/* scalar variants - universal */
+
+/*!
+ * @internal
+ * @brief Scalar round for @ref XXH3_accumulate_512_scalar().
+ *
+ * This is extracted to its own function because the NEON path uses a combination
+ * of NEON and scalar.
+ */
+XXH_FORCE_INLINE void
+XXH3_scalarRound(void* XXH_RESTRICT acc,
+                 void const* XXH_RESTRICT input,
+                 void const* XXH_RESTRICT secret,
+                 size_t lane)
+{
+    xxh_u64* xacc = (xxh_u64*) acc;
+    xxh_u8 const* xinput  = (xxh_u8 const*) input;
+    xxh_u8 const* xsecret = (xxh_u8 const*) secret;
+    XXH_ASSERT(lane < XXH_ACC_NB);
+    XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0);
+    {
+        xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8);
+        xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8);
+        xacc[lane ^ 1] += data_val; /* swap adjacent lanes */
+        xacc[lane] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32);
+    }
+}
+
+/*!
+ * @internal
+ * @brief Processes a 64 byte block of data using the scalar path.
+ */
+XXH_FORCE_INLINE void
+XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc,
+                     const void* XXH_RESTRICT input,
+                     const void* XXH_RESTRICT secret)
+{
+    size_t i;
+    for (i=0; i < XXH_ACC_NB; i++) {
+        XXH3_scalarRound(acc, input, secret, i);
+    }
+}
+
+/*!
+ * @internal
+ * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar().
+ *
+ * This is extracted to its own function because the NEON path uses a combination
+ * of NEON and scalar.
+ */
+XXH_FORCE_INLINE void
+XXH3_scalarScrambleRound(void* XXH_RESTRICT acc,
+                         void const* XXH_RESTRICT secret,
+                         size_t lane)
+{
+    xxh_u64* const xacc = (xxh_u64*) acc;   /* presumed aligned */
+    const xxh_u8* const xsecret = (const xxh_u8*) secret;   /* no alignment restriction */
+    XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0);
+    XXH_ASSERT(lane < XXH_ACC_NB);
+    {
+        xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8);
+        xxh_u64 acc64 = xacc[lane];
+        acc64 = XXH_xorshift64(acc64, 47);
+        acc64 ^= key64;
+        acc64 *= XXH_PRIME32_1;
+        xacc[lane] = acc64;
+    }
+}
+
+/*!
+ * @internal
+ * @brief Scrambles the accumulators after a large chunk has been read
+ */
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+    size_t i;
+    for (i=0; i < XXH_ACC_NB; i++) {
+        XXH3_scalarScrambleRound(acc, secret, i);
+    }
+}
+
+XXH_FORCE_INLINE void
+XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64)
+{
+    /*
+     * We need a separate pointer for the hack below,
+     * which requires a non-const pointer.
+     * Any decent compiler will optimize this out otherwise.
+     */
+    const xxh_u8* kSecretPtr = XXH3_kSecret;
+    XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
+
+#if defined(__clang__) && defined(__aarch64__)
+    /*
+     * UGLY HACK:
+     * Clang generates a bunch of MOV/MOVK pairs for aarch64, and they are
+     * placed sequentially, in order, at the top of the unrolled loop.
+     *
+     * While MOVK is great for generating constants (2 cycles for a 64-bit
+     * constant compared to 4 cycles for LDR), it fights for bandwidth with
+     * the arithmetic instructions.
+     *
+     *   I   L   S
+     * MOVK
+     * MOVK
+     * MOVK
+     * MOVK
+     * ADD
+     * SUB      STR
+     *          STR
+     * By forcing loads from memory (as the asm line causes Clang to assume
+     * that XXH3_kSecretPtr has been changed), the pipelines are used more
+     * efficiently:
+     *   I   L   S
+     *      LDR
+     *  ADD LDR
+     *  SUB     STR
+     *          STR
+     *
+     * See XXH3_NEON_LANES for details on the pipsline.
+     *
+     * XXH3_64bits_withSeed, len == 256, Snapdragon 835
+     *   without hack: 2654.4 MB/s
+     *   with hack:    3202.9 MB/s
+     */
+    XXH_COMPILER_GUARD(kSecretPtr);
+#endif
+    /*
+     * Note: in debug mode, this overrides the asm optimization
+     * and Clang will emit MOVK chains again.
+     */
+    XXH_ASSERT(kSecretPtr == XXH3_kSecret);
+
+    {   int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;
+        int i;
+        for (i=0; i < nbRounds; i++) {
+            /*
+             * The asm hack causes Clang to assume that kSecretPtr aliases with
+             * customSecret, and on aarch64, this prevented LDP from merging two
+             * loads together for free. Putting the loads together before the stores
+             * properly generates LDP.
+             */
+            xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i)     + seed64;
+            xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64;
+            XXH_writeLE64((xxh_u8*)customSecret + 16*i,     lo);
+            XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi);
+    }   }
+}
+
+
+typedef void (*XXH3_f_accumulate_512)(void* XXH_RESTRICT, const void*, const void*);
+typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*);
+typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64);
+
+
+#if (XXH_VECTOR == XXH_AVX512)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_avx512
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx512
+#define XXH3_initCustomSecret XXH3_initCustomSecret_avx512
+
+#elif (XXH_VECTOR == XXH_AVX2)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_avx2
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_avx2
+#define XXH3_initCustomSecret XXH3_initCustomSecret_avx2
+
+#elif (XXH_VECTOR == XXH_SSE2)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_sse2
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_sse2
+#define XXH3_initCustomSecret XXH3_initCustomSecret_sse2
+
+#elif (XXH_VECTOR == XXH_NEON)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_neon
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_neon
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#elif (XXH_VECTOR == XXH_VSX)
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_vsx
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_vsx
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#else /* scalar */
+
+#define XXH3_accumulate_512 XXH3_accumulate_512_scalar
+#define XXH3_scrambleAcc    XXH3_scrambleAcc_scalar
+#define XXH3_initCustomSecret XXH3_initCustomSecret_scalar
+
+#endif
+
+
+
+#ifndef XXH_PREFETCH_DIST
+#  ifdef __clang__
+#    define XXH_PREFETCH_DIST 320
+#  else
+#    if (XXH_VECTOR == XXH_AVX512)
+#      define XXH_PREFETCH_DIST 512
+#    else
+#      define XXH_PREFETCH_DIST 384
+#    endif
+#  endif  /* __clang__ */
+#endif  /* XXH_PREFETCH_DIST */
+
+/*
+ * XXH3_accumulate()
+ * Loops over XXH3_accumulate_512().
+ * Assumption: nbStripes will not overflow the secret size
+ */
+XXH_FORCE_INLINE void
+XXH3_accumulate(     xxh_u64* XXH_RESTRICT acc,
+                const xxh_u8* XXH_RESTRICT input,
+                const xxh_u8* XXH_RESTRICT secret,
+                      size_t nbStripes,
+                      XXH3_f_accumulate_512 f_acc512)
+{
+    size_t n;
+    for (n = 0; n < nbStripes; n++ ) {
+        const xxh_u8* const in = input + n*XXH_STRIPE_LEN;
+        XXH_PREFETCH(in + XXH_PREFETCH_DIST);
+        f_acc512(acc,
+                 in,
+                 secret + n*XXH_SECRET_CONSUME_RATE);
+    }
+}
+
+XXH_FORCE_INLINE void
+XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc,
+                      const xxh_u8* XXH_RESTRICT input, size_t len,
+                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                            XXH3_f_accumulate_512 f_acc512,
+                            XXH3_f_scrambleAcc f_scramble)
+{
+    size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
+    size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock;
+    size_t const nb_blocks = (len - 1) / block_len;
+
+    size_t n;
+
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+
+    for (n = 0; n < nb_blocks; n++) {
+        XXH3_accumulate(acc, input + n*block_len, secret, nbStripesPerBlock, f_acc512);
+        f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN);
+    }
+
+    /* last partial block */
+    XXH_ASSERT(len > XXH_STRIPE_LEN);
+    {   size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN;
+        XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));
+        XXH3_accumulate(acc, input + nb_blocks*block_len, secret, nbStripes, f_acc512);
+
+        /* last stripe */
+        {   const xxh_u8* const p = input + len - XXH_STRIPE_LEN;
+#define XXH_SECRET_LASTACC_START 7  /* not aligned on 8, last secret is different from acc & scrambler */
+            f_acc512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START);
+    }   }
+}
+
+XXH_FORCE_INLINE xxh_u64
+XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret)
+{
+    return XXH3_mul128_fold64(
+               acc[0] ^ XXH_readLE64(secret),
+               acc[1] ^ XXH_readLE64(secret+8) );
+}
+
+static XXH64_hash_t
+XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start)
+{
+    xxh_u64 result64 = start;
+    size_t i = 0;
+
+    for (i = 0; i < 4; i++) {
+        result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i);
+#if defined(__clang__)                                /* Clang */ \
+    && (defined(__arm__) || defined(__thumb__))       /* ARMv7 */ \
+    && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */  \
+    && !defined(XXH_ENABLE_AUTOVECTORIZE)             /* Define to disable */
+        /*
+         * UGLY HACK:
+         * Prevent autovectorization on Clang ARMv7-a. Exact same problem as
+         * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b.
+         * XXH3_64bits, len == 256, Snapdragon 835:
+         *   without hack: 2063.7 MB/s
+         *   with hack:    2560.7 MB/s
+         */
+        XXH_COMPILER_GUARD(result64);
+#endif
+    }
+
+    return XXH3_avalanche(result64);
+}
+
+#define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \
+                        XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 }
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len,
+                           const void* XXH_RESTRICT secret, size_t secretSize,
+                           XXH3_f_accumulate_512 f_acc512,
+                           XXH3_f_scrambleAcc f_scramble)
+{
+    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;
+
+    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc512, f_scramble);
+
+    /* converge into final hash */
+    XXH_STATIC_ASSERT(sizeof(acc) == 64);
+    /* do not align on 8, so that the secret is different from the accumulator */
+#define XXH_SECRET_MERGEACCS_START 11
+    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+    return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1);
+}
+
+/*
+ * It's important for performance to transmit secret's size (when it's static)
+ * so that the compiler can properly optimize the vectorized loop.
+ * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set.
+ */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len,
+                             XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
+{
+    (void)seed64;
+    return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * It's preferable for performance that XXH3_hashLong is not inlined,
+ * as it results in a smaller function for small data, easier to the instruction cache.
+ * Note that inside this no_inline function, we do inline the internal loop,
+ * and provide a statically defined secret size to allow optimization of vector loop.
+ */
+XXH_NO_INLINE XXH64_hash_t
+XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len,
+                          XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen)
+{
+    (void)seed64; (void)secret; (void)secretLen;
+    return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * XXH3_hashLong_64b_withSeed():
+ * Generate a custom key based on alteration of default XXH3_kSecret with the seed,
+ * and then use this key for long mode hashing.
+ *
+ * This operation is decently fast but nonetheless costs a little bit of time.
+ * Try to avoid it whenever possible (typically when seed==0).
+ *
+ * It's important for performance that XXH3_hashLong is not inlined. Not sure
+ * why (uop cache maybe?), but the difference is large and easily measurable.
+ */
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len,
+                                    XXH64_hash_t seed,
+                                    XXH3_f_accumulate_512 f_acc512,
+                                    XXH3_f_scrambleAcc f_scramble,
+                                    XXH3_f_initCustomSecret f_initSec)
+{
+    if (seed == 0)
+        return XXH3_hashLong_64b_internal(input, len,
+                                          XXH3_kSecret, sizeof(XXH3_kSecret),
+                                          f_acc512, f_scramble);
+    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
+        f_initSec(secret, seed);
+        return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret),
+                                          f_acc512, f_scramble);
+    }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH64_hash_t
+XXH3_hashLong_64b_withSeed(const void* input, size_t len,
+                           XXH64_hash_t seed, const xxh_u8* secret, size_t secretLen)
+{
+    (void)secret; (void)secretLen;
+    return XXH3_hashLong_64b_withSeed_internal(input, len, seed,
+                XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret);
+}
+
+
+typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t,
+                                          XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t);
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len,
+                     XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
+                     XXH3_hashLong64_f f_hashLong)
+{
+    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
+    /*
+     * If an action is to be taken if `secretLen` condition is not respected,
+     * it should be done here.
+     * For now, it's a contract pre-condition.
+     * Adding a check and a branch here would cost performance at every hash.
+     * Also, note that function signature doesn't offer room to return an error.
+     */
+    if (len <= 16)
+        return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
+    if (len <= 128)
+        return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+    if (len <= XXH3_MIDSIZE_MAX)
+        return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+    return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen);
+}
+
+
+/* ===   Public entry point   === */
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* input, size_t len)
+{
+    return XXH3_64bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize)
+{
+    return XXH3_64bits_internal(input, len, 0, secret, secretSize, XXH3_hashLong_64b_withSecret);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSeed(const void* input, size_t len, XXH64_hash_t seed)
+{
+    return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed);
+}
+
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed)
+{
+    if (len <= XXH3_MIDSIZE_MAX)
+        return XXH3_64bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);
+    return XXH3_hashLong_64b_withSecret(input, len, seed, (const xxh_u8*)secret, secretSize);
+}
+
+
+/* ===   XXH3 streaming   === */
+
+/*
+ * Malloc's a pointer that is always aligned to align.
+ *
+ * This must be freed with `XXH_alignedFree()`.
+ *
+ * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte
+ * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2
+ * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON.
+ *
+ * This underalignment previously caused a rather obvious crash which went
+ * completely unnoticed due to XXH3_createState() not actually being tested.
+ * Credit to RedSpah for noticing this bug.
+ *
+ * The alignment is done manually: Functions like posix_memalign or _mm_malloc
+ * are avoided: To maintain portability, we would have to write a fallback
+ * like this anyways, and besides, testing for the existence of library
+ * functions without relying on external build tools is impossible.
+ *
+ * The method is simple: Overallocate, manually align, and store the offset
+ * to the original behind the returned pointer.
+ *
+ * Align must be a power of 2 and 8 <= align <= 128.
+ */
+static void* XXH_alignedMalloc(size_t s, size_t align)
+{
+    XXH_ASSERT(align <= 128 && align >= 8); /* range check */
+    XXH_ASSERT((align & (align-1)) == 0);   /* power of 2 */
+    XXH_ASSERT(s != 0 && s < (s + align));  /* empty/overflow */
+    {   /* Overallocate to make room for manual realignment and an offset byte */
+        xxh_u8* base = (xxh_u8*)XXH_malloc(s + align);
+        if (base != NULL) {
+            /*
+             * Get the offset needed to align this pointer.
+             *
+             * Even if the returned pointer is aligned, there will always be
+             * at least one byte to store the offset to the original pointer.
+             */
+            size_t offset = align - ((size_t)base & (align - 1)); /* base % align */
+            /* Add the offset for the now-aligned pointer */
+            xxh_u8* ptr = base + offset;
+
+            XXH_ASSERT((size_t)ptr % align == 0);
+
+            /* Store the offset immediately before the returned pointer. */
+            ptr[-1] = (xxh_u8)offset;
+            return ptr;
+        }
+        return NULL;
+    }
+}
+/*
+ * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass
+ * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout.
+ */
+static void XXH_alignedFree(void* p)
+{
+    if (p != NULL) {
+        xxh_u8* ptr = (xxh_u8*)p;
+        /* Get the offset byte we added in XXH_malloc. */
+        xxh_u8 offset = ptr[-1];
+        /* Free the original malloc'd pointer */
+        xxh_u8* base = ptr - offset;
+        XXH_free(base);
+    }
+}
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void)
+{
+    XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64);
+    if (state==NULL) return NULL;
+    XXH3_INITSTATE(state);
+    return state;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr)
+{
+    XXH_alignedFree(statePtr);
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API void
+XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state)
+{
+    XXH_memcpy(dst_state, src_state, sizeof(*dst_state));
+}
+
+static void
+XXH3_reset_internal(XXH3_state_t* statePtr,
+                    XXH64_hash_t seed,
+                    const void* secret, size_t secretSize)
+{
+    size_t const initStart = offsetof(XXH3_state_t, bufferedSize);
+    size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart;
+    XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart);
+    XXH_ASSERT(statePtr != NULL);
+    /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */
+    memset((char*)statePtr + initStart, 0, initLength);
+    statePtr->acc[0] = XXH_PRIME32_3;
+    statePtr->acc[1] = XXH_PRIME64_1;
+    statePtr->acc[2] = XXH_PRIME64_2;
+    statePtr->acc[3] = XXH_PRIME64_3;
+    statePtr->acc[4] = XXH_PRIME64_4;
+    statePtr->acc[5] = XXH_PRIME32_2;
+    statePtr->acc[6] = XXH_PRIME64_5;
+    statePtr->acc[7] = XXH_PRIME32_1;
+    statePtr->seed = seed;
+    statePtr->useSeed = (seed != 0);
+    statePtr->extSecret = (const unsigned char*)secret;
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+    statePtr->secretLimit = secretSize - XXH_STRIPE_LEN;
+    statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset(XXH3_state_t* statePtr)
+{
+    if (statePtr == NULL) return XXH_ERROR;
+    XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE);
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+    if (statePtr == NULL) return XXH_ERROR;
+    XXH3_reset_internal(statePtr, 0, secret, secretSize);
+    if (secret == NULL) return XXH_ERROR;
+    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+    if (statePtr == NULL) return XXH_ERROR;
+    if (seed==0) return XXH3_64bits_reset(statePtr);
+    if ((seed != statePtr->seed) || (statePtr->extSecret != NULL))
+        XXH3_initCustomSecret(statePtr->customSecret, seed);
+    XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE);
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed64)
+{
+    if (statePtr == NULL) return XXH_ERROR;
+    if (secret == NULL) return XXH_ERROR;
+    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+    XXH3_reset_internal(statePtr, seed64, secret, secretSize);
+    statePtr->useSeed = 1; /* always, even if seed64==0 */
+    return XXH_OK;
+}
+
+/* Note : when XXH3_consumeStripes() is invoked,
+ * there must be a guarantee that at least one more byte must be consumed from input
+ * so that the function can blindly consume all stripes using the "normal" secret segment */
+XXH_FORCE_INLINE void
+XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc,
+                    size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock,
+                    const xxh_u8* XXH_RESTRICT input, size_t nbStripes,
+                    const xxh_u8* XXH_RESTRICT secret, size_t secretLimit,
+                    XXH3_f_accumulate_512 f_acc512,
+                    XXH3_f_scrambleAcc f_scramble)
+{
+    XXH_ASSERT(nbStripes <= nbStripesPerBlock);  /* can handle max 1 scramble per invocation */
+    XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock);
+    if (nbStripesPerBlock - *nbStripesSoFarPtr <= nbStripes) {
+        /* need a scrambling operation */
+        size_t const nbStripesToEndofBlock = nbStripesPerBlock - *nbStripesSoFarPtr;
+        size_t const nbStripesAfterBlock = nbStripes - nbStripesToEndofBlock;
+        XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripesToEndofBlock, f_acc512);
+        f_scramble(acc, secret + secretLimit);
+        XXH3_accumulate(acc, input + nbStripesToEndofBlock * XXH_STRIPE_LEN, secret, nbStripesAfterBlock, f_acc512);
+        *nbStripesSoFarPtr = nbStripesAfterBlock;
+    } else {
+        XXH3_accumulate(acc, input, secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, f_acc512);
+        *nbStripesSoFarPtr += nbStripes;
+    }
+}
+
+#ifndef XXH3_STREAM_USE_STACK
+# ifndef __clang__ /* clang doesn't need additional stack space */
+#   define XXH3_STREAM_USE_STACK 1
+# endif
+#endif
+/*
+ * Both XXH3_64bits_update and XXH3_128bits_update use this routine.
+ */
+XXH_FORCE_INLINE XXH_errorcode
+XXH3_update(XXH3_state_t* XXH_RESTRICT const state,
+            const xxh_u8* XXH_RESTRICT input, size_t len,
+            XXH3_f_accumulate_512 f_acc512,
+            XXH3_f_scrambleAcc f_scramble)
+{
+    if (input==NULL) {
+        XXH_ASSERT(len == 0);
+        return XXH_OK;
+    }
+
+    XXH_ASSERT(state != NULL);
+    {   const xxh_u8* const bEnd = input + len;
+        const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1
+        /* For some reason, gcc and MSVC seem to suffer greatly
+         * when operating accumulators directly into state.
+         * Operating into stack space seems to enable proper optimization.
+         * clang, on the other hand, doesn't seem to need this trick */
+        XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; memcpy(acc, state->acc, sizeof(acc));
+#else
+        xxh_u64* XXH_RESTRICT const acc = state->acc;
+#endif
+        state->totalLen += len;
+        XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE);
+
+        /* small input : just fill in tmp buffer */
+        if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) {
+            XXH_memcpy(state->buffer + state->bufferedSize, input, len);
+            state->bufferedSize += (XXH32_hash_t)len;
+            return XXH_OK;
+        }
+
+        /* total input is now > XXH3_INTERNALBUFFER_SIZE */
+        #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN)
+        XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0);   /* clean multiple */
+
+        /*
+         * Internal buffer is partially filled (always, except at beginning)
+         * Complete it, then consume it.
+         */
+        if (state->bufferedSize) {
+            size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;
+            XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize);
+            input += loadSize;
+            XXH3_consumeStripes(acc,
+                               &state->nbStripesSoFar, state->nbStripesPerBlock,
+                                state->buffer, XXH3_INTERNALBUFFER_STRIPES,
+                                secret, state->secretLimit,
+                                f_acc512, f_scramble);
+            state->bufferedSize = 0;
+        }
+        XXH_ASSERT(input < bEnd);
+
+        /* large input to consume : ingest per full block */
+        if ((size_t)(bEnd - input) > state->nbStripesPerBlock * XXH_STRIPE_LEN) {
+            size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN;
+            XXH_ASSERT(state->nbStripesPerBlock >= state->nbStripesSoFar);
+            /* join to current block's end */
+            {   size_t const nbStripesToEnd = state->nbStripesPerBlock - state->nbStripesSoFar;
+                XXH_ASSERT(nbStripesToEnd <= nbStripes);
+                XXH3_accumulate(acc, input, secret + state->nbStripesSoFar * XXH_SECRET_CONSUME_RATE, nbStripesToEnd, f_acc512);
+                f_scramble(acc, secret + state->secretLimit);
+                state->nbStripesSoFar = 0;
+                input += nbStripesToEnd * XXH_STRIPE_LEN;
+                nbStripes -= nbStripesToEnd;
+            }
+            /* consume per entire blocks */
+            while(nbStripes >= state->nbStripesPerBlock) {
+                XXH3_accumulate(acc, input, secret, state->nbStripesPerBlock, f_acc512);
+                f_scramble(acc, secret + state->secretLimit);
+                input += state->nbStripesPerBlock * XXH_STRIPE_LEN;
+                nbStripes -= state->nbStripesPerBlock;
+            }
+            /* consume last partial block */
+            XXH3_accumulate(acc, input, secret, nbStripes, f_acc512);
+            input += nbStripes * XXH_STRIPE_LEN;
+            XXH_ASSERT(input < bEnd);  /* at least some bytes left */
+            state->nbStripesSoFar = nbStripes;
+            /* buffer predecessor of last partial stripe */
+            XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN);
+            XXH_ASSERT(bEnd - input <= XXH_STRIPE_LEN);
+        } else {
+            /* content to consume <= block size */
+            /* Consume input by a multiple of internal buffer size */
+            if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) {
+                const xxh_u8* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE;
+                do {
+                    XXH3_consumeStripes(acc,
+                                       &state->nbStripesSoFar, state->nbStripesPerBlock,
+                                        input, XXH3_INTERNALBUFFER_STRIPES,
+                                        secret, state->secretLimit,
+                                        f_acc512, f_scramble);
+                    input += XXH3_INTERNALBUFFER_SIZE;
+                } while (input<limit);
+                /* buffer predecessor of last partial stripe */
+                XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN);
+            }
+        }
+
+        /* Some remaining input (always) : buffer it */
+        XXH_ASSERT(input < bEnd);
+        XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE);
+        XXH_ASSERT(state->bufferedSize == 0);
+        XXH_memcpy(state->buffer, input, (size_t)(bEnd-input));
+        state->bufferedSize = (XXH32_hash_t)(bEnd-input);
+#if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1
+        /* save stack accumulators into state */
+        memcpy(state->acc, acc, sizeof(acc));
+#endif
+    }
+
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+    return XXH3_update(state, (const xxh_u8*)input, len,
+                       XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+
+XXH_FORCE_INLINE void
+XXH3_digest_long (XXH64_hash_t* acc,
+                  const XXH3_state_t* state,
+                  const unsigned char* secret)
+{
+    /*
+     * Digest on a local copy. This way, the state remains unaltered, and it can
+     * continue ingesting more input afterwards.
+     */
+    XXH_memcpy(acc, state->acc, sizeof(state->acc));
+    if (state->bufferedSize >= XXH_STRIPE_LEN) {
+        size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN;
+        size_t nbStripesSoFar = state->nbStripesSoFar;
+        XXH3_consumeStripes(acc,
+                           &nbStripesSoFar, state->nbStripesPerBlock,
+                            state->buffer, nbStripes,
+                            secret, state->secretLimit,
+                            XXH3_accumulate_512, XXH3_scrambleAcc);
+        /* last stripe */
+        XXH3_accumulate_512(acc,
+                            state->buffer + state->bufferedSize - XXH_STRIPE_LEN,
+                            secret + state->secretLimit - XXH_SECRET_LASTACC_START);
+    } else {  /* bufferedSize < XXH_STRIPE_LEN */
+        xxh_u8 lastStripe[XXH_STRIPE_LEN];
+        size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize;
+        XXH_ASSERT(state->bufferedSize > 0);  /* there is always some input buffered */
+        XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);
+        XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize);
+        XXH3_accumulate_512(acc,
+                            lastStripe,
+                            secret + state->secretLimit - XXH_SECRET_LASTACC_START);
+    }
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state)
+{
+    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+    if (state->totalLen > XXH3_MIDSIZE_MAX) {
+        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
+        XXH3_digest_long(acc, state, secret);
+        return XXH3_mergeAccs(acc,
+                              secret + XXH_SECRET_MERGEACCS_START,
+                              (xxh_u64)state->totalLen * XXH_PRIME64_1);
+    }
+    /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */
+    if (state->useSeed)
+        return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+    return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen),
+                                  secret, state->secretLimit + XXH_STRIPE_LEN);
+}
+
+
+
+/* ==========================================
+ * XXH3 128 bits (a.k.a XXH128)
+ * ==========================================
+ * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant,
+ * even without counting the significantly larger output size.
+ *
+ * For example, extra steps are taken to avoid the seed-dependent collisions
+ * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B).
+ *
+ * This strength naturally comes at the cost of some speed, especially on short
+ * lengths. Note that longer hashes are about as fast as the 64-bit version
+ * due to it using only a slight modification of the 64-bit loop.
+ *
+ * XXH128 is also more oriented towards 64-bit machines. It is still extremely
+ * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
+ */
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    /* A doubled version of 1to3_64b with different constants. */
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(1 <= len && len <= 3);
+    XXH_ASSERT(secret != NULL);
+    /*
+     * len = 1: combinedl = { input[0], 0x01, input[0], input[0] }
+     * len = 2: combinedl = { input[1], 0x02, input[0], input[1] }
+     * len = 3: combinedl = { input[2], 0x03, input[0], input[1] }
+     */
+    {   xxh_u8 const c1 = input[0];
+        xxh_u8 const c2 = input[len >> 1];
+        xxh_u8 const c3 = input[len - 1];
+        xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24)
+                                | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8);
+        xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13);
+        xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed;
+        xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed;
+        xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl;
+        xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph;
+        XXH128_hash_t h128;
+        h128.low64  = XXH64_avalanche(keyed_lo);
+        h128.high64 = XXH64_avalanche(keyed_hi);
+        return h128;
+    }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(secret != NULL);
+    XXH_ASSERT(4 <= len && len <= 8);
+    seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32;
+    {   xxh_u32 const input_lo = XXH_readLE32(input);
+        xxh_u32 const input_hi = XXH_readLE32(input + len - 4);
+        xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32);
+        xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed;
+        xxh_u64 const keyed = input_64 ^ bitflip;
+
+        /* Shift len to the left to ensure it is even, this avoids even multiplies. */
+        XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2));
+
+        m128.high64 += (m128.low64 << 1);
+        m128.low64  ^= (m128.high64 >> 3);
+
+        m128.low64   = XXH_xorshift64(m128.low64, 35);
+        m128.low64  *= 0x9FB21C651E98DF25ULL;
+        m128.low64   = XXH_xorshift64(m128.low64, 28);
+        m128.high64  = XXH3_avalanche(m128.high64);
+        return m128;
+    }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(input != NULL);
+    XXH_ASSERT(secret != NULL);
+    XXH_ASSERT(9 <= len && len <= 16);
+    {   xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed;
+        xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed;
+        xxh_u64 const input_lo = XXH_readLE64(input);
+        xxh_u64       input_hi = XXH_readLE64(input + len - 8);
+        XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1);
+        /*
+         * Put len in the middle of m128 to ensure that the length gets mixed to
+         * both the low and high bits in the 128x64 multiply below.
+         */
+        m128.low64 += (xxh_u64)(len - 1) << 54;
+        input_hi   ^= bitfliph;
+        /*
+         * Add the high 32 bits of input_hi to the high 32 bits of m128, then
+         * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to
+         * the high 64 bits of m128.
+         *
+         * The best approach to this operation is different on 32-bit and 64-bit.
+         */
+        if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */
+            /*
+             * 32-bit optimized version, which is more readable.
+             *
+             * On 32-bit, it removes an ADC and delays a dependency between the two
+             * halves of m128.high64, but it generates an extra mask on 64-bit.
+             */
+            m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2);
+        } else {
+            /*
+             * 64-bit optimized (albeit more confusing) version.
+             *
+             * Uses some properties of addition and multiplication to remove the mask:
+             *
+             * Let:
+             *    a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF)
+             *    b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000)
+             *    c = XXH_PRIME32_2
+             *
+             *    a + (b * c)
+             * Inverse Property: x + y - x == y
+             *    a + (b * (1 + c - 1))
+             * Distributive Property: x * (y + z) == (x * y) + (x * z)
+             *    a + (b * 1) + (b * (c - 1))
+             * Identity Property: x * 1 == x
+             *    a + b + (b * (c - 1))
+             *
+             * Substitute a, b, and c:
+             *    input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
+             *
+             * Since input_hi.hi + input_hi.lo == input_hi, we get this:
+             *    input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1))
+             */
+            m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1);
+        }
+        /* m128 ^= XXH_swap64(m128 >> 64); */
+        m128.low64  ^= XXH_swap64(m128.high64);
+
+        {   /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */
+            XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2);
+            h128.high64 += m128.high64 * XXH_PRIME64_2;
+
+            h128.low64   = XXH3_avalanche(h128.low64);
+            h128.high64  = XXH3_avalanche(h128.high64);
+            return h128;
+    }   }
+}
+
+/*
+ * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
+ */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed)
+{
+    XXH_ASSERT(len <= 16);
+    {   if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed);
+        if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed);
+        if (len) return XXH3_len_1to3_128b(input, len, secret, seed);
+        {   XXH128_hash_t h128;
+            xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72);
+            xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88);
+            h128.low64 = XXH64_avalanche(seed ^ bitflipl);
+            h128.high64 = XXH64_avalanche( seed ^ bitfliph);
+            return h128;
+    }   }
+}
+
+/*
+ * A bit slower than XXH3_mix16B, but handles multiply by zero better.
+ */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2,
+              const xxh_u8* secret, XXH64_hash_t seed)
+{
+    acc.low64  += XXH3_mix16B (input_1, secret+0, seed);
+    acc.low64  ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8);
+    acc.high64 += XXH3_mix16B (input_2, secret+16, seed);
+    acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8);
+    return acc;
+}
+
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
+                      const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                      XXH64_hash_t seed)
+{
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+    XXH_ASSERT(16 < len && len <= 128);
+
+    {   XXH128_hash_t acc;
+        acc.low64 = len * XXH_PRIME64_1;
+        acc.high64 = 0;
+        if (len > 32) {
+            if (len > 64) {
+                if (len > 96) {
+                    acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed);
+                }
+                acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed);
+            }
+            acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed);
+        }
+        acc = XXH128_mix32B(acc, input, input+len-16, secret, seed);
+        {   XXH128_hash_t h128;
+            h128.low64  = acc.low64 + acc.high64;
+            h128.high64 = (acc.low64    * XXH_PRIME64_1)
+                        + (acc.high64   * XXH_PRIME64_4)
+                        + ((len - seed) * XXH_PRIME64_2);
+            h128.low64  = XXH3_avalanche(h128.low64);
+            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
+            return h128;
+        }
+    }
+}
+
+XXH_NO_INLINE XXH128_hash_t
+XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len,
+                       const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                       XXH64_hash_t seed)
+{
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+    XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+    {   XXH128_hash_t acc;
+        int const nbRounds = (int)len / 32;
+        int i;
+        acc.low64 = len * XXH_PRIME64_1;
+        acc.high64 = 0;
+        for (i=0; i<4; i++) {
+            acc = XXH128_mix32B(acc,
+                                input  + (32 * i),
+                                input  + (32 * i) + 16,
+                                secret + (32 * i),
+                                seed);
+        }
+        acc.low64 = XXH3_avalanche(acc.low64);
+        acc.high64 = XXH3_avalanche(acc.high64);
+        XXH_ASSERT(nbRounds >= 4);
+        for (i=4 ; i < nbRounds; i++) {
+            acc = XXH128_mix32B(acc,
+                                input + (32 * i),
+                                input + (32 * i) + 16,
+                                secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)),
+                                seed);
+        }
+        /* last bytes */
+        acc = XXH128_mix32B(acc,
+                            input + len - 16,
+                            input + len - 32,
+                            secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16,
+                            0ULL - seed);
+
+        {   XXH128_hash_t h128;
+            h128.low64  = acc.low64 + acc.high64;
+            h128.high64 = (acc.low64    * XXH_PRIME64_1)
+                        + (acc.high64   * XXH_PRIME64_4)
+                        + ((len - seed) * XXH_PRIME64_2);
+            h128.low64  = XXH3_avalanche(h128.low64);
+            h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64);
+            return h128;
+        }
+    }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len,
+                            const xxh_u8* XXH_RESTRICT secret, size_t secretSize,
+                            XXH3_f_accumulate_512 f_acc512,
+                            XXH3_f_scrambleAcc f_scramble)
+{
+    XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC;
+
+    XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc512, f_scramble);
+
+    /* converge into final hash */
+    XXH_STATIC_ASSERT(sizeof(acc) == 64);
+    XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+    {   XXH128_hash_t h128;
+        h128.low64  = XXH3_mergeAccs(acc,
+                                     secret + XXH_SECRET_MERGEACCS_START,
+                                     (xxh_u64)len * XXH_PRIME64_1);
+        h128.high64 = XXH3_mergeAccs(acc,
+                                     secret + secretSize
+                                            - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
+                                     ~((xxh_u64)len * XXH_PRIME64_2));
+        return h128;
+    }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH128_hash_t
+XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len,
+                           XXH64_hash_t seed64,
+                           const void* XXH_RESTRICT secret, size_t secretLen)
+{
+    (void)seed64; (void)secret; (void)secretLen;
+    return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret),
+                                       XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*
+ * It's important for performance to pass @secretLen (when it's static)
+ * to the compiler, so that it can properly optimize the vectorized loop.
+ */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len,
+                              XXH64_hash_t seed64,
+                              const void* XXH_RESTRICT secret, size_t secretLen)
+{
+    (void)seed64;
+    return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen,
+                                       XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len,
+                                XXH64_hash_t seed64,
+                                XXH3_f_accumulate_512 f_acc512,
+                                XXH3_f_scrambleAcc f_scramble,
+                                XXH3_f_initCustomSecret f_initSec)
+{
+    if (seed64 == 0)
+        return XXH3_hashLong_128b_internal(input, len,
+                                           XXH3_kSecret, sizeof(XXH3_kSecret),
+                                           f_acc512, f_scramble);
+    {   XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
+        f_initSec(secret, seed64);
+        return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret),
+                                           f_acc512, f_scramble);
+    }
+}
+
+/*
+ * It's important for performance that XXH3_hashLong is not inlined.
+ */
+XXH_NO_INLINE XXH128_hash_t
+XXH3_hashLong_128b_withSeed(const void* input, size_t len,
+                            XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen)
+{
+    (void)secret; (void)secretLen;
+    return XXH3_hashLong_128b_withSeed_internal(input, len, seed64,
+                XXH3_accumulate_512, XXH3_scrambleAcc, XXH3_initCustomSecret);
+}
+
+typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t,
+                                            XXH64_hash_t, const void* XXH_RESTRICT, size_t);
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_128bits_internal(const void* input, size_t len,
+                      XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen,
+                      XXH3_hashLong128_f f_hl128)
+{
+    XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN);
+    /*
+     * If an action is to be taken if `secret` conditions are not respected,
+     * it should be done here.
+     * For now, it's a contract pre-condition.
+     * Adding a check and a branch here would cost performance at every hash.
+     */
+    if (len <= 16)
+        return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64);
+    if (len <= 128)
+        return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+    if (len <= XXH3_MIDSIZE_MAX)
+        return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64);
+    return f_hl128(input, len, seed64, secret, secretLen);
+}
+
+
+/* ===   Public XXH128 API   === */
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* input, size_t len)
+{
+    return XXH3_128bits_internal(input, len, 0,
+                                 XXH3_kSecret, sizeof(XXH3_kSecret),
+                                 XXH3_hashLong_128b_default);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSecret(const void* input, size_t len, const void* secret, size_t secretSize)
+{
+    return XXH3_128bits_internal(input, len, 0,
+                                 (const xxh_u8*)secret, secretSize,
+                                 XXH3_hashLong_128b_withSecret);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSeed(const void* input, size_t len, XXH64_hash_t seed)
+{
+    return XXH3_128bits_internal(input, len, seed,
+                                 XXH3_kSecret, sizeof(XXH3_kSecret),
+                                 XXH3_hashLong_128b_withSeed);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSecretandSeed(const void* input, size_t len, const void* secret, size_t secretSize, XXH64_hash_t seed)
+{
+    if (len <= XXH3_MIDSIZE_MAX)
+        return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL);
+    return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t
+XXH128(const void* input, size_t len, XXH64_hash_t seed)
+{
+    return XXH3_128bits_withSeed(input, len, seed);
+}
+
+
+/* ===   XXH3 128-bit streaming   === */
+
+/*
+ * All initialization and update functions are identical to 64-bit streaming variant.
+ * The only difference is the finalization routine.
+ */
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset(XXH3_state_t* statePtr)
+{
+    return XXH3_64bits_reset(statePtr);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+    return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+    return XXH3_64bits_reset_withSeed(statePtr, seed);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSecretandSeed(XXH3_state_t* statePtr, const void* secret, size_t secretSize, XXH64_hash_t seed)
+{
+    return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+    return XXH3_update(state, (const xxh_u8*)input, len,
+                       XXH3_accumulate_512, XXH3_scrambleAcc);
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state)
+{
+    const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret;
+    if (state->totalLen > XXH3_MIDSIZE_MAX) {
+        XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB];
+        XXH3_digest_long(acc, state, secret);
+        XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+        {   XXH128_hash_t h128;
+            h128.low64  = XXH3_mergeAccs(acc,
+                                         secret + XXH_SECRET_MERGEACCS_START,
+                                         (xxh_u64)state->totalLen * XXH_PRIME64_1);
+            h128.high64 = XXH3_mergeAccs(acc,
+                                         secret + state->secretLimit + XXH_STRIPE_LEN
+                                                - sizeof(acc) - XXH_SECRET_MERGEACCS_START,
+                                         ~((xxh_u64)state->totalLen * XXH_PRIME64_2));
+            return h128;
+        }
+    }
+    /* len <= XXH3_MIDSIZE_MAX : short code */
+    if (state->seed)
+        return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+    return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen),
+                                   secret, state->secretLimit + XXH_STRIPE_LEN);
+}
+
+/* 128-bit utility functions */
+
+#include <string.h>   /* memcmp, memcpy */
+
+/* return : 1 is equal, 0 if different */
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2)
+{
+    /* note : XXH128_hash_t is compact, it has no padding byte */
+    return !(memcmp(&h1, &h2, sizeof(h1)));
+}
+
+/* This prototype is compatible with stdlib's qsort().
+ * return : >0 if *h128_1  > *h128_2
+ *          <0 if *h128_1  < *h128_2
+ *          =0 if *h128_1 == *h128_2  */
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2)
+{
+    XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1;
+    XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2;
+    int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64);
+    /* note : bets that, in most cases, hash values are different */
+    if (hcmp) return hcmp;
+    return (h1.low64 > h2.low64) - (h2.low64 > h1.low64);
+}
+
+
+/*======   Canonical representation   ======*/
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API void
+XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash)
+{
+    XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t));
+    if (XXH_CPU_LITTLE_ENDIAN) {
+        hash.high64 = XXH_swap64(hash.high64);
+        hash.low64  = XXH_swap64(hash.low64);
+    }
+    XXH_memcpy(dst, &hash.high64, sizeof(hash.high64));
+    XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64));
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH128_hash_t
+XXH128_hashFromCanonical(const XXH128_canonical_t* src)
+{
+    XXH128_hash_t h;
+    h.high64 = XXH_readBE64(src);
+    h.low64  = XXH_readBE64(src->digest + 8);
+    return h;
+}
+
+
+
+/* ==========================================
+ * Secret generators
+ * ==========================================
+ */
+#define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x))
+
+XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128)
+{
+    XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 );
+    XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 );
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API XXH_errorcode
+XXH3_generateSecret(void* secretBuffer, size_t secretSize, const void* customSeed, size_t customSeedSize)
+{
+#if (XXH_DEBUGLEVEL >= 1)
+    XXH_ASSERT(secretBuffer != NULL);
+    XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+#else
+    /* production mode, assert() are disabled */
+    if (secretBuffer == NULL) return XXH_ERROR;
+    if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+#endif
+
+    if (customSeedSize == 0) {
+        customSeed = XXH3_kSecret;
+        customSeedSize = XXH_SECRET_DEFAULT_SIZE;
+    }
+#if (XXH_DEBUGLEVEL >= 1)
+    XXH_ASSERT(customSeed != NULL);
+#else
+    if (customSeed == NULL) return XXH_ERROR;
+#endif
+
+    /* Fill secretBuffer with a copy of customSeed - repeat as needed */
+    {   size_t pos = 0;
+        while (pos < secretSize) {
+            size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize);
+            memcpy((char*)secretBuffer + pos, customSeed, toCopy);
+            pos += toCopy;
+    }   }
+
+    {   size_t const nbSeg16 = secretSize / 16;
+        size_t n;
+        XXH128_canonical_t scrambler;
+        XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0));
+        for (n=0; n<nbSeg16; n++) {
+            XXH128_hash_t const h128 = XXH128(&scrambler, sizeof(scrambler), n);
+            XXH3_combine16((char*)secretBuffer + n*16, h128);
+        }
+        /* last segment */
+        XXH3_combine16((char*)secretBuffer + secretSize - 16, XXH128_hashFromCanonical(&scrambler));
+    }
+    return XXH_OK;
+}
+
+/*! @ingroup xxh3_family */
+XXH_PUBLIC_API void
+XXH3_generateSecret_fromSeed(void* secretBuffer, XXH64_hash_t seed)
+{
+    XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE];
+    XXH3_initCustomSecret(secret, seed);
+    XXH_ASSERT(secretBuffer != NULL);
+    memcpy(secretBuffer, secret, XXH_SECRET_DEFAULT_SIZE);
+}
+
+
+
+/* Pop our optimization override from above */
+#if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \
+  && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \
+  && defined(__OPTIMIZE__) && !defined(__OPTIMIZE_SIZE__) /* respect -O0 and -Os */
+#  pragma GCC pop_options
+#endif
+
+#endif  /* XXH_NO_LONG_LONG */
+
+#endif  /* XXH_NO_XXH3 */
+
+/*!
+ * @}
+ */
+#endif  /* XXH_IMPLEMENTATION */
 
 
 #if defined (__cplusplus)
diff --git a/Utilities/cmzstd/lib/common/zstd_common.c b/Utilities/cmzstd/lib/common/zstd_common.c
index 3d7e35b..3f04c22 100644
--- a/Utilities/cmzstd/lib/common/zstd_common.c
+++ b/Utilities/cmzstd/lib/common/zstd_common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,7 +14,6 @@
 *  Dependencies
 ***************************************/
 #define ZSTD_DEPS_NEED_MALLOC
-#include "zstd_deps.h"   /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */
 #include "error_private.h"
 #include "zstd_internal.h"
 
@@ -47,37 +46,3 @@
 /*! ZSTD_getErrorString() :
  *  provides error code string from enum */
 const char* ZSTD_getErrorString(ZSTD_ErrorCode code) { return ERR_getErrorString(code); }
-
-
-
-/*=**************************************************************
-*  Custom allocator
-****************************************************************/
-void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem)
-{
-    if (customMem.customAlloc)
-        return customMem.customAlloc(customMem.opaque, size);
-    return ZSTD_malloc(size);
-}
-
-void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem)
-{
-    if (customMem.customAlloc) {
-        /* calloc implemented as malloc+memset;
-         * not as efficient as calloc, but next best guess for custom malloc */
-        void* const ptr = customMem.customAlloc(customMem.opaque, size);
-        ZSTD_memset(ptr, 0, size);
-        return ptr;
-    }
-    return ZSTD_calloc(1, size);
-}
-
-void ZSTD_customFree(void* ptr, ZSTD_customMem customMem)
-{
-    if (ptr!=NULL) {
-        if (customMem.customFree)
-            customMem.customFree(customMem.opaque, ptr);
-        else
-            ZSTD_free(ptr);
-    }
-}
diff --git a/Utilities/cmzstd/lib/common/zstd_deps.h b/Utilities/cmzstd/lib/common/zstd_deps.h
index 1421134..4d767ae 100644
--- a/Utilities/cmzstd/lib/common/zstd_deps.h
+++ b/Utilities/cmzstd/lib/common/zstd_deps.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/common/zstd_internal.h b/Utilities/cmzstd/lib/common/zstd_internal.h
index 68252e9..1f942f2 100644
--- a/Utilities/cmzstd/lib/common/zstd_internal.h
+++ b/Utilities/cmzstd/lib/common/zstd_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -19,10 +19,8 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
-#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
-#include <arm_neon.h>
-#endif
 #include "compiler.h"
+#include "cpu.h"
 #include "mem.h"
 #include "debug.h"                 /* assert, DEBUGLOG, RAWLOG, g_debuglevel */
 #include "error_private.h"
@@ -30,7 +28,6 @@
 #include "../zstd.h"
 #define FSE_STATIC_LINKING_ONLY
 #include "fse.h"
-#define HUF_STATIC_LINKING_ONLY
 #include "huf.h"
 #ifndef XXH_STATIC_LINKING_ONLY
 #  define XXH_STATIC_LINKING_ONLY  /* XXH64_state_t */
@@ -60,81 +57,7 @@
 #undef MAX
 #define MIN(a,b) ((a)<(b) ? (a) : (b))
 #define MAX(a,b) ((a)>(b) ? (a) : (b))
-
-/**
- * Ignore: this is an internal helper.
- *
- * This is a helper function to help force C99-correctness during compilation.
- * Under strict compilation modes, variadic macro arguments can't be empty.
- * However, variadic function arguments can be. Using a function therefore lets
- * us statically check that at least one (string) argument was passed,
- * independent of the compilation flags.
- */
-static INLINE_KEYWORD UNUSED_ATTR
-void _force_has_format_string(const char *format, ...) {
-  (void)format;
-}
-
-/**
- * Ignore: this is an internal helper.
- *
- * We want to force this function invocation to be syntactically correct, but
- * we don't want to force runtime evaluation of its arguments.
- */
-#define _FORCE_HAS_FORMAT_STRING(...) \
-  if (0) { \
-    _force_has_format_string(__VA_ARGS__); \
-  }
-
-/**
- * Return the specified error if the condition evaluates to true.
- *
- * In debug modes, prints additional information.
- * In order to do that (particularly, printing the conditional that failed),
- * this can't just wrap RETURN_ERROR().
- */
-#define RETURN_ERROR_IF(cond, err, ...) \
-  if (cond) { \
-    RAWLOG(3, "%s:%d: ERROR!: check %s failed, returning %s", \
-           __FILE__, __LINE__, ZSTD_QUOTE(cond), ZSTD_QUOTE(ERROR(err))); \
-    _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
-    RAWLOG(3, ": " __VA_ARGS__); \
-    RAWLOG(3, "\n"); \
-    return ERROR(err); \
-  }
-
-/**
- * Unconditionally return the specified error.
- *
- * In debug modes, prints additional information.
- */
-#define RETURN_ERROR(err, ...) \
-  do { \
-    RAWLOG(3, "%s:%d: ERROR!: unconditional check failed, returning %s", \
-           __FILE__, __LINE__, ZSTD_QUOTE(ERROR(err))); \
-    _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
-    RAWLOG(3, ": " __VA_ARGS__); \
-    RAWLOG(3, "\n"); \
-    return ERROR(err); \
-  } while(0);
-
-/**
- * If the provided expression evaluates to an error code, returns that error code.
- *
- * In debug modes, prints additional information.
- */
-#define FORWARD_IF_ERROR(err, ...) \
-  do { \
-    size_t const err_code = (err); \
-    if (ERR_isError(err_code)) { \
-      RAWLOG(3, "%s:%d: ERROR!: forwarding error in %s: %s", \
-             __FILE__, __LINE__, ZSTD_QUOTE(err), ERR_getErrorName(err_code)); \
-      _FORCE_HAS_FORMAT_STRING(__VA_ARGS__); \
-      RAWLOG(3, ": " __VA_ARGS__); \
-      RAWLOG(3, "\n"); \
-      return err_code; \
-    } \
-  } while(0);
+#define BOUNDED(min,val,max) (MAX(min,MIN(val,max)))
 
 
 /*-*************************************
@@ -143,7 +66,6 @@
 #define ZSTD_OPT_NUM    (1<<12)
 
 #define ZSTD_REP_NUM      3                 /* number of repcodes */
-#define ZSTD_REP_MOVE     (ZSTD_REP_NUM-1)
 static UNUSED_ATTR const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 };
 
 #define KB *(1 <<10)
@@ -170,9 +92,9 @@
 #define ZSTD_FRAMECHECKSUMSIZE 4
 
 #define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
-#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */)   /* for a non-null block */
+#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */)   /* for a non-null block */
+#define MIN_LITERALS_FOR_4_STREAMS 6
 
-#define HufLog 12
 typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e;
 
 #define LONGNBSEQ 0x7F00
@@ -180,6 +102,7 @@
 #define MINMATCH 3
 
 #define Litbits  8
+#define LitHufLog 11
 #define MaxLit ((1<<Litbits) - 1)
 #define MaxML   52
 #define MaxLL   35
@@ -190,12 +113,14 @@
 #define LLFSELog    9
 #define OffFSELog   8
 #define MaxFSELog  MAX(MAX(MLFSELog, LLFSELog), OffFSELog)
+#define MaxMLBits 16
+#define MaxLLBits 16
 
 #define ZSTD_MAX_HUF_HEADER_SIZE 128 /* header + <= 127 byte tree description */
 /* Each table cannot take more than #symbols * FSELog bits */
 #define ZSTD_MAX_FSE_HEADERS_SIZE (((MaxML + 1) * MLFSELog + (MaxLL + 1) * LLFSELog + (MaxOff + 1) * OffFSELog + 7) / 8)
 
-static UNUSED_ATTR const U32 LL_bits[MaxLL+1] = {
+static UNUSED_ATTR const U8 LL_bits[MaxLL+1] = {
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      1, 1, 1, 1, 2, 2, 3, 3,
@@ -212,7 +137,7 @@
 #define LL_DEFAULTNORMLOG 6  /* for static allocation */
 static UNUSED_ATTR const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG;
 
-static UNUSED_ATTR const U32 ML_bits[MaxML+1] = {
+static UNUSED_ATTR const U8 ML_bits[MaxML+1] = {
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
@@ -247,19 +172,30 @@
 *  Shared functions to include for inlining
 *********************************************/
 static void ZSTD_copy8(void* dst, const void* src) {
-#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
+#if defined(ZSTD_ARCH_ARM_NEON)
     vst1_u8((uint8_t*)dst, vld1_u8((const uint8_t*)src));
 #else
     ZSTD_memcpy(dst, src, 8);
 #endif
 }
-
 #define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; }
+
+/* Need to use memmove here since the literal buffer can now be located within
+   the dst buffer. In circumstances where the op "catches up" to where the
+   literal buffer is, there can be partial overlaps in this call on the final
+   copy if the literal is being shifted by less than 16 bytes. */
 static void ZSTD_copy16(void* dst, const void* src) {
-#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
+#if defined(ZSTD_ARCH_ARM_NEON)
     vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
+#elif defined(ZSTD_ARCH_X86_SSE2)
+    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
+#elif defined(__clang__)
+    ZSTD_memmove(dst, src, 16);
 #else
-    ZSTD_memcpy(dst, src, 16);
+    /* ZSTD_memmove is not inlined properly by gcc */
+    BYTE copy16_buf[16];
+    ZSTD_memcpy(copy16_buf, src, 16);
+    ZSTD_memcpy(dst, copy16_buf, 16);
 #endif
 }
 #define COPY16(d,s) { ZSTD_copy16(d,s); d+=16; s+=16; }
@@ -288,8 +224,6 @@
     BYTE* op = (BYTE*)dst;
     BYTE* const oend = op + length;
 
-    assert(diff >= 8 || (ovtype == ZSTD_no_overlap && diff <= -WILDCOPY_VECLEN));
-
     if (ovtype == ZSTD_overlap_src_before_dst && diff < WILDCOPY_VECLEN) {
         /* Handle short offset copies. */
         do {
@@ -303,12 +237,6 @@
          * one COPY16() in the first call. Then, do two calls per loop since
          * at that point it is more likely to have a high trip count.
          */
-#ifdef __aarch64__
-        do {
-            COPY16(op, ip);
-        }
-        while (op < oend);
-#else
         ZSTD_copy16(op, ip);
         if (16 >= length) return;
         op += 16;
@@ -318,7 +246,6 @@
             COPY16(op, ip);
         }
         while (op < oend);
-#endif
     }
 }
 
@@ -352,9 +279,9 @@
 *  Private declarations
 *********************************************/
 typedef struct seqDef_s {
-    U32 offset;         /* offset == rawOffset + ZSTD_REP_NUM, or equivalently, offCode + 1 */
+    U32 offBase;   /* offBase == Offset + ZSTD_REP_NUM, or repcode 1,2,3 */
     U16 litLength;
-    U16 matchLength;
+    U16 mlBase;    /* mlBase == matchLength - MINMATCH */
 } seqDef;
 
 /* Controls whether seqStore has a single "long" litLength or matchLength. See seqStore_t. */
@@ -367,11 +294,11 @@
 typedef struct {
     seqDef* sequencesStart;
     seqDef* sequences;      /* ptr to end of sequences */
-    BYTE* litStart;
-    BYTE* lit;              /* ptr to end of literals */
-    BYTE* llCode;
-    BYTE* mlCode;
-    BYTE* ofCode;
+    BYTE*  litStart;
+    BYTE*  lit;             /* ptr to end of literals */
+    BYTE*  llCode;
+    BYTE*  mlCode;
+    BYTE*  ofCode;
     size_t maxNbSeq;
     size_t maxNbLit;
 
@@ -379,8 +306,8 @@
      * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment
      * the existing value of the litLength or matchLength by 0x10000.
      */
-    ZSTD_longLengthType_e   longLengthType;
-    U32                     longLengthPos;  /* Index of the sequence to apply long length modification to */
+    ZSTD_longLengthType_e longLengthType;
+    U32                   longLengthPos;  /* Index of the sequence to apply long length modification to */
 } seqStore_t;
 
 typedef struct {
@@ -396,13 +323,13 @@
 {
     ZSTD_sequenceLength seqLen;
     seqLen.litLength = seq->litLength;
-    seqLen.matchLength = seq->matchLength + MINMATCH;
+    seqLen.matchLength = seq->mlBase + MINMATCH;
     if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) {
         if (seqStore->longLengthType == ZSTD_llt_literalLength) {
-            seqLen.litLength += 0xFFFF;
+            seqLen.litLength += 0x10000;
         }
         if (seqStore->longLengthType == ZSTD_llt_matchLength) {
-            seqLen.matchLength += 0xFFFF;
+            seqLen.matchLength += 0x10000;
         }
     }
     return seqLen;
@@ -415,46 +342,13 @@
  *          `decompressedBound != ZSTD_CONTENTSIZE_ERROR`
  */
 typedef struct {
+    size_t nbBlocks;
     size_t compressedSize;
     unsigned long long decompressedBound;
 } ZSTD_frameSizeInfo;   /* decompress & legacy */
 
 const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx);   /* compress & dictBuilder */
-void ZSTD_seqToCodes(const seqStore_t* seqStorePtr);   /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */
-
-/* custom memory allocation functions */
-void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem);
-void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem);
-void ZSTD_customFree(void* ptr, ZSTD_customMem customMem);
-
-
-MEM_STATIC U32 ZSTD_highbit32(U32 val)   /* compress, dictBuilder, decodeCorpus */
-{
-    assert(val != 0);
-    {
-#   if defined(_MSC_VER)   /* Visual */
-#       if STATIC_BMI2 == 1
-            return _lzcnt_u32(val)^31;
-#       else
-            unsigned long r=0;
-            return _BitScanReverse(&r, val) ? (unsigned)r : 0;
-#       endif
-#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* GCC Intrinsic */
-        return __builtin_clz (val) ^ 31;
-#   elif defined(__ICCARM__)    /* IAR Intrinsic */
-        return 31 - __CLZ(val);
-#   else   /* Software version */
-        static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 };
-        U32 v = val;
-        v |= v >> 1;
-        v |= v >> 2;
-        v |= v >> 4;
-        v |= v >> 8;
-        v |= v >> 16;
-        return DeBruijnClz[(v * 0x07C4ACDDU) >> 27];
-#   endif
-    }
-}
+int ZSTD_seqToCodes(const seqStore_t* seqStorePtr);   /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */
 
 
 /* ZSTD_invalidateRepCodes() :
@@ -482,6 +376,14 @@
 size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
                        const void* src, size_t srcSize);
 
+/**
+ * @returns true iff the CPU supports dynamic BMI2 dispatch.
+ */
+MEM_STATIC int ZSTD_cpuSupportsBmi2(void)
+{
+    ZSTD_cpuid_t cpuid = ZSTD_cpuid();
+    return ZSTD_cpuid_bmi1(cpuid) && ZSTD_cpuid_bmi2(cpuid);
+}
 
 #if defined (__cplusplus)
 }
diff --git a/Utilities/cmzstd/lib/common/zstd_trace.h b/Utilities/cmzstd/lib/common/zstd_trace.h
index 485cadf..da20534 100644
--- a/Utilities/cmzstd/lib/common/zstd_trace.h
+++ b/Utilities/cmzstd/lib/common/zstd_trace.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -17,8 +17,17 @@
 
 #include <stddef.h>
 
-/* weak symbol support */
-#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && defined(__GNUC__) && \
+/* weak symbol support
+ * For now, enable conservatively:
+ * - Only GNUC
+ * - Only ELF
+ * - Only x86-64, i386 and aarch64
+ * Also, explicitly disable on platforms known not to work so they aren't
+ * forgotten in the future.
+ */
+#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && \
+    defined(__GNUC__) && defined(__ELF__) && \
+    (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__aarch64__)) && \
     !defined(__APPLE__) && !defined(_WIN32) && !defined(__MINGW32__) && \
     !defined(__CYGWIN__) && !defined(_AIX)
 #  define ZSTD_HAVE_WEAK_SYMBOLS 1
diff --git a/Utilities/cmzstd/lib/compress/clevels.h b/Utilities/cmzstd/lib/compress/clevels.h
new file mode 100644
index 0000000..c18da46
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/clevels.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_CLEVELS_H
+#define ZSTD_CLEVELS_H
+
+#define ZSTD_STATIC_LINKING_ONLY  /* ZSTD_compressionParameters  */
+#include "../zstd.h"
+
+/*-=====  Pre-defined compression levels  =====-*/
+
+#define ZSTD_MAX_CLEVEL     22
+
+#ifdef __GNUC__
+__attribute__((__unused__))
+#endif
+
+static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {
+{   /* "default" - for any srcSize > 256 KB */
+    /* W,  C,  H,  S,  L, TL, strat */
+    { 19, 12, 13,  1,  6,  1, ZSTD_fast    },  /* base for negative levels */
+    { 19, 13, 14,  1,  7,  0, ZSTD_fast    },  /* level  1 */
+    { 20, 15, 16,  1,  6,  0, ZSTD_fast    },  /* level  2 */
+    { 21, 16, 17,  1,  5,  0, ZSTD_dfast   },  /* level  3 */
+    { 21, 18, 18,  1,  5,  0, ZSTD_dfast   },  /* level  4 */
+    { 21, 18, 19,  3,  5,  2, ZSTD_greedy  },  /* level  5 */
+    { 21, 18, 19,  3,  5,  4, ZSTD_lazy    },  /* level  6 */
+    { 21, 19, 20,  4,  5,  8, ZSTD_lazy    },  /* level  7 */
+    { 21, 19, 20,  4,  5, 16, ZSTD_lazy2   },  /* level  8 */
+    { 22, 20, 21,  4,  5, 16, ZSTD_lazy2   },  /* level  9 */
+    { 22, 21, 22,  5,  5, 16, ZSTD_lazy2   },  /* level 10 */
+    { 22, 21, 22,  6,  5, 16, ZSTD_lazy2   },  /* level 11 */
+    { 22, 22, 23,  6,  5, 32, ZSTD_lazy2   },  /* level 12 */
+    { 22, 22, 22,  4,  5, 32, ZSTD_btlazy2 },  /* level 13 */
+    { 22, 22, 23,  5,  5, 32, ZSTD_btlazy2 },  /* level 14 */
+    { 22, 23, 23,  6,  5, 32, ZSTD_btlazy2 },  /* level 15 */
+    { 22, 22, 22,  5,  5, 48, ZSTD_btopt   },  /* level 16 */
+    { 23, 23, 22,  5,  4, 64, ZSTD_btopt   },  /* level 17 */
+    { 23, 23, 22,  6,  3, 64, ZSTD_btultra },  /* level 18 */
+    { 23, 24, 22,  7,  3,256, ZSTD_btultra2},  /* level 19 */
+    { 25, 25, 23,  7,  3,256, ZSTD_btultra2},  /* level 20 */
+    { 26, 26, 24,  7,  3,512, ZSTD_btultra2},  /* level 21 */
+    { 27, 27, 25,  9,  3,999, ZSTD_btultra2},  /* level 22 */
+},
+{   /* for srcSize <= 256 KB */
+    /* W,  C,  H,  S,  L,  T, strat */
+    { 18, 12, 13,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
+    { 18, 13, 14,  1,  6,  0, ZSTD_fast    },  /* level  1 */
+    { 18, 14, 14,  1,  5,  0, ZSTD_dfast   },  /* level  2 */
+    { 18, 16, 16,  1,  4,  0, ZSTD_dfast   },  /* level  3 */
+    { 18, 16, 17,  3,  5,  2, ZSTD_greedy  },  /* level  4.*/
+    { 18, 17, 18,  5,  5,  2, ZSTD_greedy  },  /* level  5.*/
+    { 18, 18, 19,  3,  5,  4, ZSTD_lazy    },  /* level  6.*/
+    { 18, 18, 19,  4,  4,  4, ZSTD_lazy    },  /* level  7 */
+    { 18, 18, 19,  4,  4,  8, ZSTD_lazy2   },  /* level  8 */
+    { 18, 18, 19,  5,  4,  8, ZSTD_lazy2   },  /* level  9 */
+    { 18, 18, 19,  6,  4,  8, ZSTD_lazy2   },  /* level 10 */
+    { 18, 18, 19,  5,  4, 12, ZSTD_btlazy2 },  /* level 11.*/
+    { 18, 19, 19,  7,  4, 12, ZSTD_btlazy2 },  /* level 12.*/
+    { 18, 18, 19,  4,  4, 16, ZSTD_btopt   },  /* level 13 */
+    { 18, 18, 19,  4,  3, 32, ZSTD_btopt   },  /* level 14.*/
+    { 18, 18, 19,  6,  3,128, ZSTD_btopt   },  /* level 15.*/
+    { 18, 19, 19,  6,  3,128, ZSTD_btultra },  /* level 16.*/
+    { 18, 19, 19,  8,  3,256, ZSTD_btultra },  /* level 17.*/
+    { 18, 19, 19,  6,  3,128, ZSTD_btultra2},  /* level 18.*/
+    { 18, 19, 19,  8,  3,256, ZSTD_btultra2},  /* level 19.*/
+    { 18, 19, 19, 10,  3,512, ZSTD_btultra2},  /* level 20.*/
+    { 18, 19, 19, 12,  3,512, ZSTD_btultra2},  /* level 21.*/
+    { 18, 19, 19, 13,  3,999, ZSTD_btultra2},  /* level 22.*/
+},
+{   /* for srcSize <= 128 KB */
+    /* W,  C,  H,  S,  L,  T, strat */
+    { 17, 12, 12,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
+    { 17, 12, 13,  1,  6,  0, ZSTD_fast    },  /* level  1 */
+    { 17, 13, 15,  1,  5,  0, ZSTD_fast    },  /* level  2 */
+    { 17, 15, 16,  2,  5,  0, ZSTD_dfast   },  /* level  3 */
+    { 17, 17, 17,  2,  4,  0, ZSTD_dfast   },  /* level  4 */
+    { 17, 16, 17,  3,  4,  2, ZSTD_greedy  },  /* level  5 */
+    { 17, 16, 17,  3,  4,  4, ZSTD_lazy    },  /* level  6 */
+    { 17, 16, 17,  3,  4,  8, ZSTD_lazy2   },  /* level  7 */
+    { 17, 16, 17,  4,  4,  8, ZSTD_lazy2   },  /* level  8 */
+    { 17, 16, 17,  5,  4,  8, ZSTD_lazy2   },  /* level  9 */
+    { 17, 16, 17,  6,  4,  8, ZSTD_lazy2   },  /* level 10 */
+    { 17, 17, 17,  5,  4,  8, ZSTD_btlazy2 },  /* level 11 */
+    { 17, 18, 17,  7,  4, 12, ZSTD_btlazy2 },  /* level 12 */
+    { 17, 18, 17,  3,  4, 12, ZSTD_btopt   },  /* level 13.*/
+    { 17, 18, 17,  4,  3, 32, ZSTD_btopt   },  /* level 14.*/
+    { 17, 18, 17,  6,  3,256, ZSTD_btopt   },  /* level 15.*/
+    { 17, 18, 17,  6,  3,128, ZSTD_btultra },  /* level 16.*/
+    { 17, 18, 17,  8,  3,256, ZSTD_btultra },  /* level 17.*/
+    { 17, 18, 17, 10,  3,512, ZSTD_btultra },  /* level 18.*/
+    { 17, 18, 17,  5,  3,256, ZSTD_btultra2},  /* level 19.*/
+    { 17, 18, 17,  7,  3,512, ZSTD_btultra2},  /* level 20.*/
+    { 17, 18, 17,  9,  3,512, ZSTD_btultra2},  /* level 21.*/
+    { 17, 18, 17, 11,  3,999, ZSTD_btultra2},  /* level 22.*/
+},
+{   /* for srcSize <= 16 KB */
+    /* W,  C,  H,  S,  L,  T, strat */
+    { 14, 12, 13,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
+    { 14, 14, 15,  1,  5,  0, ZSTD_fast    },  /* level  1 */
+    { 14, 14, 15,  1,  4,  0, ZSTD_fast    },  /* level  2 */
+    { 14, 14, 15,  2,  4,  0, ZSTD_dfast   },  /* level  3 */
+    { 14, 14, 14,  4,  4,  2, ZSTD_greedy  },  /* level  4 */
+    { 14, 14, 14,  3,  4,  4, ZSTD_lazy    },  /* level  5.*/
+    { 14, 14, 14,  4,  4,  8, ZSTD_lazy2   },  /* level  6 */
+    { 14, 14, 14,  6,  4,  8, ZSTD_lazy2   },  /* level  7 */
+    { 14, 14, 14,  8,  4,  8, ZSTD_lazy2   },  /* level  8.*/
+    { 14, 15, 14,  5,  4,  8, ZSTD_btlazy2 },  /* level  9.*/
+    { 14, 15, 14,  9,  4,  8, ZSTD_btlazy2 },  /* level 10.*/
+    { 14, 15, 14,  3,  4, 12, ZSTD_btopt   },  /* level 11.*/
+    { 14, 15, 14,  4,  3, 24, ZSTD_btopt   },  /* level 12.*/
+    { 14, 15, 14,  5,  3, 32, ZSTD_btultra },  /* level 13.*/
+    { 14, 15, 15,  6,  3, 64, ZSTD_btultra },  /* level 14.*/
+    { 14, 15, 15,  7,  3,256, ZSTD_btultra },  /* level 15.*/
+    { 14, 15, 15,  5,  3, 48, ZSTD_btultra2},  /* level 16.*/
+    { 14, 15, 15,  6,  3,128, ZSTD_btultra2},  /* level 17.*/
+    { 14, 15, 15,  7,  3,256, ZSTD_btultra2},  /* level 18.*/
+    { 14, 15, 15,  8,  3,256, ZSTD_btultra2},  /* level 19.*/
+    { 14, 15, 15,  8,  3,512, ZSTD_btultra2},  /* level 20.*/
+    { 14, 15, 15,  9,  3,512, ZSTD_btultra2},  /* level 21.*/
+    { 14, 15, 15, 10,  3,999, ZSTD_btultra2},  /* level 22.*/
+},
+};
+
+
+
+#endif  /* ZSTD_CLEVELS_H */
diff --git a/Utilities/cmzstd/lib/compress/fse_compress.c b/Utilities/cmzstd/lib/compress/fse_compress.c
index 1b6a076..5d37708 100644
--- a/Utilities/cmzstd/lib/compress/fse_compress.c
+++ b/Utilities/cmzstd/lib/compress/fse_compress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * FSE : Finite State Entropy encoder
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -26,6 +26,7 @@
 #define ZSTD_DEPS_NEED_MALLOC
 #define ZSTD_DEPS_NEED_MATH64
 #include "../common/zstd_deps.h"  /* ZSTD_malloc, ZSTD_free, ZSTD_memcpy, ZSTD_memset */
+#include "../common/bits.h" /* ZSTD_highbit32 */
 
 
 /* **************************************************************
@@ -75,13 +76,14 @@
     void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ;
     FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
     U32 const step = FSE_TABLESTEP(tableSize);
+    U32 const maxSV1 = maxSymbolValue+1;
 
-    U32* cumul = (U32*)workSpace;
-    FSE_FUNCTION_TYPE* tableSymbol = (FSE_FUNCTION_TYPE*)(cumul + (maxSymbolValue + 2));
+    U16* cumul = (U16*)workSpace;   /* size = maxSV1 */
+    FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)(cumul + (maxSV1+1));  /* size = tableSize */
 
     U32 highThreshold = tableSize-1;
 
-    if ((size_t)workSpace & 3) return ERROR(GENERIC); /* Must be 4 byte aligned */
+    assert(((size_t)workSpace & 1) == 0);  /* Must be 2 bytes-aligned */
     if (FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) > wkspSize) return ERROR(tableLog_tooLarge);
     /* CTable header */
     tableU16[-2] = (U16) tableLog;
@@ -89,7 +91,7 @@
     assert(tableLog < 16);   /* required for threshold strategy to work */
 
     /* For explanations on how to distribute symbol values over the table :
-     * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
+     * https://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
 
      #ifdef __clang_analyzer__
      ZSTD_memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize);   /* useless initialization, just to keep scan-build happy */
@@ -98,20 +100,61 @@
     /* symbol start positions */
     {   U32 u;
         cumul[0] = 0;
-        for (u=1; u <= maxSymbolValue+1; u++) {
+        for (u=1; u <= maxSV1; u++) {
             if (normalizedCounter[u-1]==-1) {  /* Low proba symbol */
                 cumul[u] = cumul[u-1] + 1;
                 tableSymbol[highThreshold--] = (FSE_FUNCTION_TYPE)(u-1);
             } else {
-                cumul[u] = cumul[u-1] + normalizedCounter[u-1];
+                assert(normalizedCounter[u-1] >= 0);
+                cumul[u] = cumul[u-1] + (U16)normalizedCounter[u-1];
+                assert(cumul[u] >= cumul[u-1]);  /* no overflow */
         }   }
-        cumul[maxSymbolValue+1] = tableSize+1;
+        cumul[maxSV1] = (U16)(tableSize+1);
     }
 
     /* Spread symbols */
-    {   U32 position = 0;
+    if (highThreshold == tableSize - 1) {
+        /* Case for no low prob count symbols. Lay down 8 bytes at a time
+         * to reduce branch misses since we are operating on a small block
+         */
+        BYTE* const spread = tableSymbol + tableSize; /* size = tableSize + 8 (may write beyond tableSize) */
+        {   U64 const add = 0x0101010101010101ull;
+            size_t pos = 0;
+            U64 sv = 0;
+            U32 s;
+            for (s=0; s<maxSV1; ++s, sv += add) {
+                int i;
+                int const n = normalizedCounter[s];
+                MEM_write64(spread + pos, sv);
+                for (i = 8; i < n; i += 8) {
+                    MEM_write64(spread + pos + i, sv);
+                }
+                assert(n>=0);
+                pos += (size_t)n;
+            }
+        }
+        /* Spread symbols across the table. Lack of lowprob symbols means that
+         * we don't need variable sized inner loop, so we can unroll the loop and
+         * reduce branch misses.
+         */
+        {   size_t position = 0;
+            size_t s;
+            size_t const unroll = 2; /* Experimentally determined optimal unroll */
+            assert(tableSize % unroll == 0); /* FSE_MIN_TABLELOG is 5 */
+            for (s = 0; s < (size_t)tableSize; s += unroll) {
+                size_t u;
+                for (u = 0; u < unroll; ++u) {
+                    size_t const uPosition = (position + (u * step)) & tableMask;
+                    tableSymbol[uPosition] = spread[s + u];
+                }
+                position = (position + (unroll * step)) & tableMask;
+            }
+            assert(position == 0);   /* Must have initialized all positions */
+        }
+    } else {
+        U32 position = 0;
         U32 symbol;
-        for (symbol=0; symbol<=maxSymbolValue; symbol++) {
+        for (symbol=0; symbol<maxSV1; symbol++) {
             int nbOccurrences;
             int const freq = normalizedCounter[symbol];
             for (nbOccurrences=0; nbOccurrences<freq; nbOccurrences++) {
@@ -120,7 +163,6 @@
                 while (position > highThreshold)
                     position = (position + step) & tableMask;   /* Low proba area */
         }   }
-
         assert(position==0);  /* Must have initialized all positions */
     }
 
@@ -144,16 +186,17 @@
             case -1:
             case  1:
                 symbolTT[s].deltaNbBits = (tableLog << 16) - (1<<tableLog);
-                symbolTT[s].deltaFindState = total - 1;
+                assert(total <= INT_MAX);
+                symbolTT[s].deltaFindState = (int)(total - 1);
                 total ++;
                 break;
             default :
-                {
-                    U32 const maxBitsOut = tableLog - BIT_highbit32 (normalizedCounter[s]-1);
-                    U32 const minStatePlus = normalizedCounter[s] << maxBitsOut;
+                assert(normalizedCounter[s] > 1);
+                {   U32 const maxBitsOut = tableLog - ZSTD_highbit32 ((U32)normalizedCounter[s]-1);
+                    U32 const minStatePlus = (U32)normalizedCounter[s] << maxBitsOut;
                     symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
-                    symbolTT[s].deltaFindState = total - normalizedCounter[s];
-                    total +=  normalizedCounter[s];
+                    symbolTT[s].deltaFindState = (int)(total - (unsigned)normalizedCounter[s]);
+                    total +=  (unsigned)normalizedCounter[s];
     }   }   }   }
 
 #if 0  /* debug : symbol costs */
@@ -164,32 +207,26 @@
                 symbol, normalizedCounter[symbol],
                 FSE_getMaxNbBits(symbolTT, symbol),
                 (double)FSE_bitCost(symbolTT, tableLog, symbol, 8) / 256);
-        }
-    }
+    }   }
 #endif
 
     return 0;
 }
 
-#ifndef ZSTD_NO_UNUSED_FUNCTIONS
-size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
-{
-    FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE];   /* memset() is not necessary, even if static analyzer complain about it */
-    return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol));
-}
-#endif
-
 
 
 #ifndef FSE_COMMONDEFS_ONLY
 
-
 /*-**************************************************************
 *  FSE NCount encoding
 ****************************************************************/
 size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog)
 {
-    size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog) >> 3) + 3;
+    size_t const maxHeaderSize = (((maxSymbolValue+1) * tableLog
+                                   + 4 /* bitCount initialized at 4 */
+                                   + 2 /* first two symbols may use one additional bit each */) / 8)
+                                    + 1 /* round up to whole nb bytes */
+                                    + 2 /* additional two bytes for bitstream flush */;
     return maxSymbolValue ? maxHeaderSize : FSE_NCOUNTBOUND;  /* maxSymbolValue==0 ? use default */
 }
 
@@ -306,21 +343,11 @@
 *  FSE Compression Code
 ****************************************************************/
 
-FSE_CTable* FSE_createCTable (unsigned maxSymbolValue, unsigned tableLog)
-{
-    size_t size;
-    if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
-    size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32);
-    return (FSE_CTable*)ZSTD_malloc(size);
-}
-
-void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); }
-
 /* provides the minimum logSize to safely represent a distribution */
 static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
 {
-    U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1;
-    U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
+    U32 minBitsSrc = ZSTD_highbit32((U32)(srcSize)) + 1;
+    U32 minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2;
     U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
     assert(srcSize > 1); /* Not supported, RLE should be used instead */
     return minBits;
@@ -328,7 +355,7 @@
 
 unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
 {
-    U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
+    U32 maxBitsSrc = ZSTD_highbit32((U32)(srcSize - 1)) - minus;
     U32 tableLog = maxTableLog;
     U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
     assert(srcSize > 1); /* Not supported, RLE should be used instead */
@@ -496,40 +523,6 @@
     return tableLog;
 }
 
-
-/* fake FSE_CTable, for raw (uncompressed) input */
-size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits)
-{
-    const unsigned tableSize = 1 << nbBits;
-    const unsigned tableMask = tableSize - 1;
-    const unsigned maxSymbolValue = tableMask;
-    void* const ptr = ct;
-    U16* const tableU16 = ( (U16*) ptr) + 2;
-    void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableSize>>1);   /* assumption : tableLog >= 1 */
-    FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
-    unsigned s;
-
-    /* Sanity checks */
-    if (nbBits < 1) return ERROR(GENERIC);             /* min size */
-
-    /* header */
-    tableU16[-2] = (U16) nbBits;
-    tableU16[-1] = (U16) maxSymbolValue;
-
-    /* Build table */
-    for (s=0; s<tableSize; s++)
-        tableU16[s] = (U16)(tableSize + s);
-
-    /* Build Symbol Transformation Table */
-    {   const U32 deltaNbBits = (nbBits << 16) - (1 << nbBits);
-        for (s=0; s<=maxSymbolValue; s++) {
-            symbolTT[s].deltaNbBits = deltaNbBits;
-            symbolTT[s].deltaFindState = s-1;
-    }   }
-
-    return 0;
-}
-
 /* fake FSE_CTable, for rle input (always same symbol) */
 size_t FSE_buildCTable_rle (FSE_CTable* ct, BYTE symbolValue)
 {
@@ -628,82 +621,4 @@
 
 size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }
 
-#ifndef ZSTD_NO_UNUSED_FUNCTIONS
-/* FSE_compress_wksp() :
- * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
- * `wkspSize` size must be `(1<<tableLog)`.
- */
-size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize)
-{
-    BYTE* const ostart = (BYTE*) dst;
-    BYTE* op = ostart;
-    BYTE* const oend = ostart + dstSize;
-
-    unsigned count[FSE_MAX_SYMBOL_VALUE+1];
-    S16   norm[FSE_MAX_SYMBOL_VALUE+1];
-    FSE_CTable* CTable = (FSE_CTable*)workSpace;
-    size_t const CTableSize = FSE_CTABLE_SIZE_U32(tableLog, maxSymbolValue);
-    void* scratchBuffer = (void*)(CTable + CTableSize);
-    size_t const scratchBufferSize = wkspSize - (CTableSize * sizeof(FSE_CTable));
-
-#ifdef __clang_analyzer__
-    memset(norm, 0, sizeof(norm));
-#endif
-
-    /* init conditions */
-    if (wkspSize < FSE_COMPRESS_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge);
-    if (srcSize <= 1) return 0;  /* Not compressible */
-    if (!maxSymbolValue) maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
-    if (!tableLog) tableLog = FSE_DEFAULT_TABLELOG;
-
-    /* Scan input and build symbol stats */
-    {   CHECK_V_F(maxCount, HIST_count_wksp(count, &maxSymbolValue, src, srcSize, scratchBuffer, scratchBufferSize) );
-        if (maxCount == srcSize) return 1;   /* only a single symbol in src : rle */
-        if (maxCount == 1) return 0;         /* each symbol present maximum once => not compressible */
-        if (maxCount < (srcSize >> 7)) return 0;   /* Heuristic : not compressible enough */
-    }
-
-    tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue);
-    CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue, /* useLowProbCount */ srcSize >= 2048) );
-
-    /* Write table description header */
-    {   CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) );
-        op += nc_err;
-    }
-
-    /* Compress */
-    CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, scratchBufferSize) );
-    {   CHECK_V_F(cSize, FSE_compress_usingCTable(op, oend - op, src, srcSize, CTable) );
-        if (cSize == 0) return 0;   /* not enough space for compressed data */
-        op += cSize;
-    }
-
-    /* check compressibility */
-    if ( (size_t)(op-ostart) >= srcSize-1 ) return 0;
-
-    return op-ostart;
-}
-
-typedef struct {
-    FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)];
-    union {
-      U32 hist_wksp[HIST_WKSP_SIZE_U32];
-      BYTE scratchBuffer[1 << FSE_MAX_TABLELOG];
-    } workspace;
-} fseWkspMax_t;
-
-size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog)
-{
-    fseWkspMax_t scratchBuffer;
-    DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_COMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE));   /* compilation failures here means scratchBuffer is not large enough */
-    if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);
-    return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer));
-}
-
-size_t FSE_compress (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
-{
-    return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG);
-}
-#endif
-
 #endif   /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/compress/hist.c b/Utilities/cmzstd/lib/compress/hist.c
index 073c57e..e2fb431 100644
--- a/Utilities/cmzstd/lib/compress/hist.c
+++ b/Utilities/cmzstd/lib/compress/hist.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * hist : Histogram functions
  * part of Finite State Entropy project
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/compress/hist.h b/Utilities/cmzstd/lib/compress/hist.h
index 228ed48..887896b 100644
--- a/Utilities/cmzstd/lib/compress/hist.h
+++ b/Utilities/cmzstd/lib/compress/hist.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * hist : Histogram functions
  * part of Finite State Entropy project
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/compress/huf_compress.c b/Utilities/cmzstd/lib/compress/huf_compress.c
index 485906e..2987187 100644
--- a/Utilities/cmzstd/lib/compress/huf_compress.c
+++ b/Utilities/cmzstd/lib/compress/huf_compress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * Huffman encoder, part of New Generation Entropy library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -29,9 +29,9 @@
 #include "hist.h"
 #define FSE_STATIC_LINKING_ONLY   /* FSE_optimalTableLog_internal */
 #include "../common/fse.h"        /* header compression */
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "../common/error_private.h"
+#include "../common/bits.h"       /* ZSTD_highbit32 */
 
 
 /* **************************************************************
@@ -42,17 +42,93 @@
 
 
 /* **************************************************************
-*  Utils
+*  Required declarations
 ****************************************************************/
-unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
+typedef struct nodeElt_s {
+    U32 count;
+    U16 parent;
+    BYTE byte;
+    BYTE nbBits;
+} nodeElt;
+
+
+/* **************************************************************
+*  Debug Traces
+****************************************************************/
+
+#if DEBUGLEVEL >= 2
+
+static size_t showU32(const U32* arr, size_t size)
 {
-    return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+    size_t u;
+    for (u=0; u<size; u++) {
+        RAWLOG(6, " %u", arr[u]); (void)arr;
+    }
+    RAWLOG(6, " \n");
+    return size;
 }
 
+static size_t HUF_getNbBits(HUF_CElt elt);
+
+static size_t showCTableBits(const HUF_CElt* ctable, size_t size)
+{
+    size_t u;
+    for (u=0; u<size; u++) {
+        RAWLOG(6, " %zu", HUF_getNbBits(ctable[u])); (void)ctable;
+    }
+    RAWLOG(6, " \n");
+    return size;
+
+}
+
+static size_t showHNodeSymbols(const nodeElt* hnode, size_t size)
+{
+    size_t u;
+    for (u=0; u<size; u++) {
+        RAWLOG(6, " %u", hnode[u].byte); (void)hnode;
+    }
+    RAWLOG(6, " \n");
+    return size;
+}
+
+static size_t showHNodeBits(const nodeElt* hnode, size_t size)
+{
+    size_t u;
+    for (u=0; u<size; u++) {
+        RAWLOG(6, " %u", hnode[u].nbBits); (void)hnode;
+    }
+    RAWLOG(6, " \n");
+    return size;
+}
+
+#endif
+
 
 /* *******************************************************
 *  HUF : Huffman block compression
 *********************************************************/
+#define HUF_WORKSPACE_MAX_ALIGNMENT 8
+
+static void* HUF_alignUpWorkspace(void* workspace, size_t* workspaceSizePtr, size_t align)
+{
+    size_t const mask = align - 1;
+    size_t const rem = (size_t)workspace & mask;
+    size_t const add = (align - rem) & mask;
+    BYTE* const aligned = (BYTE*)workspace + add;
+    assert((align & (align - 1)) == 0); /* pow 2 */
+    assert(align <= HUF_WORKSPACE_MAX_ALIGNMENT);
+    if (*workspaceSizePtr >= add) {
+        assert(add < align);
+        assert(((size_t)aligned & mask) == 0);
+        *workspaceSizePtr -= add;
+        return aligned;
+    } else {
+        *workspaceSizePtr = 0;
+        return NULL;
+    }
+}
+
+
 /* HUF_compressWeights() :
  * Same as FSE_compress(), but dedicated to huff0's weights compression.
  * The use case needs much less stack memory.
@@ -67,7 +143,10 @@
     S16 norm[HUF_TABLELOG_MAX+1];
 } HUF_CompressWeightsWksp;
 
-static size_t HUF_compressWeights(void* dst, size_t dstSize, const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize)
+static size_t
+HUF_compressWeights(void* dst, size_t dstSize,
+              const void* weightTable, size_t wtSize,
+                    void* workspace, size_t workspaceSize)
 {
     BYTE* const ostart = (BYTE*) dst;
     BYTE* op = ostart;
@@ -75,7 +154,7 @@
 
     unsigned maxSymbolValue = HUF_TABLELOG_MAX;
     U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER;
-    HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)workspace;
+    HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32));
 
     if (workspaceSize < sizeof(HUF_CompressWeightsWksp)) return ERROR(GENERIC);
 
@@ -106,6 +185,40 @@
     return (size_t)(op-ostart);
 }
 
+static size_t HUF_getNbBits(HUF_CElt elt)
+{
+    return elt & 0xFF;
+}
+
+static size_t HUF_getNbBitsFast(HUF_CElt elt)
+{
+    return elt;
+}
+
+static size_t HUF_getValue(HUF_CElt elt)
+{
+    return elt & ~(size_t)0xFF;
+}
+
+static size_t HUF_getValueFast(HUF_CElt elt)
+{
+    return elt;
+}
+
+static void HUF_setNbBits(HUF_CElt* elt, size_t nbBits)
+{
+    assert(nbBits <= HUF_TABLELOG_ABSOLUTEMAX);
+    *elt = nbBits;
+}
+
+static void HUF_setValue(HUF_CElt* elt, size_t value)
+{
+    size_t const nbBits = HUF_getNbBits(*elt);
+    if (nbBits > 0) {
+        assert((value >> nbBits) == 0);
+        *elt |= value << (sizeof(HUF_CElt) * 8 - nbBits);
+    }
+}
 
 typedef struct {
     HUF_CompressWeightsWksp wksp;
@@ -117,9 +230,12 @@
                             const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog,
                             void* workspace, size_t workspaceSize)
 {
+    HUF_CElt const* const ct = CTable + 1;
     BYTE* op = (BYTE*)dst;
     U32 n;
-    HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)workspace;
+    HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)HUF_alignUpWorkspace(workspace, &workspaceSize, ZSTD_ALIGNOF(U32));
+
+    HUF_STATIC_ASSERT(HUF_CTABLE_WORKSPACE_SIZE >= sizeof(HUF_WriteCTableWksp));
 
     /* check conditions */
     if (workspaceSize < sizeof(HUF_WriteCTableWksp)) return ERROR(GENERIC);
@@ -130,9 +246,10 @@
     for (n=1; n<huffLog+1; n++)
         wksp->bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
     for (n=0; n<maxSymbolValue; n++)
-        wksp->huffWeight[n] = wksp->bitsToWeight[CTable[n].nbBits];
+        wksp->huffWeight[n] = wksp->bitsToWeight[HUF_getNbBits(ct[n])];
 
     /* attempt weights compression by FSE */
+    if (maxDstSize < 1) return ERROR(dstSize_tooSmall);
     {   CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, wksp->huffWeight, maxSymbolValue, &wksp->wksp, sizeof(wksp->wksp)) );
         if ((hSize>1) & (hSize < maxSymbolValue/2)) {   /* FSE compressed */
             op[0] = (BYTE)hSize;
@@ -149,16 +266,6 @@
     return ((maxSymbolValue+1)/2) + 1;
 }
 
-/*! HUF_writeCTable() :
-    `CTable` : Huffman tree to save, using huf representation.
-    @return : size of saved CTable */
-size_t HUF_writeCTable (void* dst, size_t maxDstSize,
-                        const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog)
-{
-    HUF_WriteCTableWksp wksp;
-    return HUF_writeCTable_wksp(dst, maxDstSize, CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp));
-}
-
 
 size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights)
 {
@@ -166,6 +273,7 @@
     U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1];   /* large enough for values from 0 to 16 */
     U32 tableLog = 0;
     U32 nbSymbols = 0;
+    HUF_CElt* const ct = CTable + 1;
 
     /* get symbol weights */
     CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize));
@@ -175,6 +283,8 @@
     if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
     if (nbSymbols > *maxSymbolValuePtr+1) return ERROR(maxSymbolValue_tooSmall);
 
+    CTable[0] = tableLog;
+
     /* Prepare base value per rank */
     {   U32 n, nextRankStart = 0;
         for (n=1; n<=tableLog; n++) {
@@ -186,13 +296,13 @@
     /* fill nbBits */
     {   U32 n; for (n=0; n<nbSymbols; n++) {
             const U32 w = huffWeight[n];
-            CTable[n].nbBits = (BYTE)(tableLog + 1 - w) & -(w != 0);
+            HUF_setNbBits(ct + n, (BYTE)(tableLog + 1 - w) & -(w != 0));
     }   }
 
     /* fill val */
     {   U16 nbPerRank[HUF_TABLELOG_MAX+2]  = {0};  /* support w=0=>n=tableLog+1 */
         U16 valPerRank[HUF_TABLELOG_MAX+2] = {0};
-        { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[CTable[n].nbBits]++; }
+        { U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[HUF_getNbBits(ct[n])]++; }
         /* determine stating value per rank */
         valPerRank[tableLog+1] = 0;   /* for w==0 */
         {   U16 min = 0;
@@ -202,77 +312,73 @@
                 min >>= 1;
         }   }
         /* assign value within rank, symbol order */
-        { U32 n; for (n=0; n<nbSymbols; n++) CTable[n].val = valPerRank[CTable[n].nbBits]++; }
+        { U32 n; for (n=0; n<nbSymbols; n++) HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++); }
     }
 
     *maxSymbolValuePtr = nbSymbols - 1;
     return readSize;
 }
 
-U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue)
+U32 HUF_getNbBitsFromCTable(HUF_CElt const* CTable, U32 symbolValue)
 {
-    const HUF_CElt* table = (const HUF_CElt*)symbolTable;
+    const HUF_CElt* const ct = CTable + 1;
     assert(symbolValue <= HUF_SYMBOLVALUE_MAX);
-    return table[symbolValue].nbBits;
+    return (U32)HUF_getNbBits(ct[symbolValue]);
 }
 
 
-typedef struct nodeElt_s {
-    U32 count;
-    U16 parent;
-    BYTE byte;
-    BYTE nbBits;
-} nodeElt;
-
 /**
  * HUF_setMaxHeight():
- * Enforces maxNbBits on the Huffman tree described in huffNode.
+ * Try to enforce @targetNbBits on the Huffman tree described in @huffNode.
  *
- * It sets all nodes with nbBits > maxNbBits to be maxNbBits. Then it adjusts
- * the tree to so that it is a valid canonical Huffman tree.
+ * It attempts to convert all nodes with nbBits > @targetNbBits
+ * to employ @targetNbBits instead. Then it adjusts the tree
+ * so that it remains a valid canonical Huffman tree.
  *
  * @pre               The sum of the ranks of each symbol == 2^largestBits,
  *                    where largestBits == huffNode[lastNonNull].nbBits.
  * @post              The sum of the ranks of each symbol == 2^largestBits,
- *                    where largestBits is the return value <= maxNbBits.
+ *                    where largestBits is the return value (expected <= targetNbBits).
  *
- * @param huffNode    The Huffman tree modified in place to enforce maxNbBits.
+ * @param huffNode    The Huffman tree modified in place to enforce targetNbBits.
+ *                    It's presumed sorted, from most frequent to rarest symbol.
  * @param lastNonNull The symbol with the lowest count in the Huffman tree.
- * @param maxNbBits   The maximum allowed number of bits, which the Huffman tree
+ * @param targetNbBits  The allowed number of bits, which the Huffman tree
  *                    may not respect. After this function the Huffman tree will
- *                    respect maxNbBits.
- * @return            The maximum number of bits of the Huffman tree after adjustment,
- *                    necessarily no more than maxNbBits.
+ *                    respect targetNbBits.
+ * @return            The maximum number of bits of the Huffman tree after adjustment.
  */
-static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
+static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 targetNbBits)
 {
     const U32 largestBits = huffNode[lastNonNull].nbBits;
-    /* early exit : no elt > maxNbBits, so the tree is already valid. */
-    if (largestBits <= maxNbBits) return largestBits;
+    /* early exit : no elt > targetNbBits, so the tree is already valid. */
+    if (largestBits <= targetNbBits) return largestBits;
+
+    DEBUGLOG(5, "HUF_setMaxHeight (targetNbBits = %u)", targetNbBits);
 
     /* there are several too large elements (at least >= 2) */
     {   int totalCost = 0;
-        const U32 baseCost = 1 << (largestBits - maxNbBits);
+        const U32 baseCost = 1 << (largestBits - targetNbBits);
         int n = (int)lastNonNull;
 
-        /* Adjust any ranks > maxNbBits to maxNbBits.
+        /* Adjust any ranks > targetNbBits to targetNbBits.
          * Compute totalCost, which is how far the sum of the ranks is
          * we are over 2^largestBits after adjust the offending ranks.
          */
-        while (huffNode[n].nbBits > maxNbBits) {
+        while (huffNode[n].nbBits > targetNbBits) {
             totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
-            huffNode[n].nbBits = (BYTE)maxNbBits;
+            huffNode[n].nbBits = (BYTE)targetNbBits;
             n--;
         }
-        /* n stops at huffNode[n].nbBits <= maxNbBits */
-        assert(huffNode[n].nbBits <= maxNbBits);
-        /* n end at index of smallest symbol using < maxNbBits */
-        while (huffNode[n].nbBits == maxNbBits) --n;
+        /* n stops at huffNode[n].nbBits <= targetNbBits */
+        assert(huffNode[n].nbBits <= targetNbBits);
+        /* n end at index of smallest symbol using < targetNbBits */
+        while (huffNode[n].nbBits == targetNbBits) --n;
 
-        /* renorm totalCost from 2^largestBits to 2^maxNbBits
+        /* renorm totalCost from 2^largestBits to 2^targetNbBits
          * note : totalCost is necessarily a multiple of baseCost */
-        assert((totalCost & (baseCost - 1)) == 0);
-        totalCost >>= (largestBits - maxNbBits);
+        assert(((U32)totalCost & (baseCost - 1)) == 0);
+        totalCost >>= (largestBits - targetNbBits);
         assert(totalCost > 0);
 
         /* repay normalized cost */
@@ -281,19 +387,19 @@
 
             /* Get pos of last (smallest = lowest cum. count) symbol per rank */
             ZSTD_memset(rankLast, 0xF0, sizeof(rankLast));
-            {   U32 currentNbBits = maxNbBits;
+            {   U32 currentNbBits = targetNbBits;
                 int pos;
                 for (pos=n ; pos >= 0; pos--) {
                     if (huffNode[pos].nbBits >= currentNbBits) continue;
-                    currentNbBits = huffNode[pos].nbBits;   /* < maxNbBits */
-                    rankLast[maxNbBits-currentNbBits] = (U32)pos;
+                    currentNbBits = huffNode[pos].nbBits;   /* < targetNbBits */
+                    rankLast[targetNbBits-currentNbBits] = (U32)pos;
             }   }
 
             while (totalCost > 0) {
                 /* Try to reduce the next power of 2 above totalCost because we
                  * gain back half the rank.
                  */
-                U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1;
+                U32 nBitsToDecrease = ZSTD_highbit32((U32)totalCost) + 1;
                 for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
                     U32 const highPos = rankLast[nBitsToDecrease];
                     U32 const lowPos = rankLast[nBitsToDecrease-1];
@@ -333,7 +439,7 @@
                     rankLast[nBitsToDecrease] = noSymbol;
                 else {
                     rankLast[nBitsToDecrease]--;
-                    if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease)
+                    if (huffNode[rankLast[nBitsToDecrease]].nbBits != targetNbBits-nBitsToDecrease)
                         rankLast[nBitsToDecrease] = noSymbol;   /* this rank is now empty */
                 }
             }   /* while (totalCost > 0) */
@@ -345,11 +451,11 @@
              * TODO.
              */
             while (totalCost < 0) {  /* Sometimes, cost correction overshoot */
-                /* special case : no rank 1 symbol (using maxNbBits-1);
-                 * let's create one from largest rank 0 (using maxNbBits).
+                /* special case : no rank 1 symbol (using targetNbBits-1);
+                 * let's create one from largest rank 0 (using targetNbBits).
                  */
                 if (rankLast[1] == noSymbol) {
-                    while (huffNode[n].nbBits == maxNbBits) n--;
+                    while (huffNode[n].nbBits == targetNbBits) n--;
                     huffNode[n+1].nbBits--;
                     assert(n >= 0);
                     rankLast[1] = (U32)(n+1);
@@ -363,26 +469,122 @@
         }   /* repay normalized cost */
     }   /* there are several too large elements (at least >= 2) */
 
-    return maxNbBits;
+    return targetNbBits;
 }
 
 typedef struct {
-    U32 base;
-    U32 curr;
+    U16 base;
+    U16 curr;
 } rankPos;
 
-typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
+typedef nodeElt huffNodeTable[2 * (HUF_SYMBOLVALUE_MAX + 1)];
 
-#define RANK_POSITION_TABLE_SIZE 32
+/* Number of buckets available for HUF_sort() */
+#define RANK_POSITION_TABLE_SIZE 192
 
 typedef struct {
   huffNodeTable huffNodeTbl;
   rankPos rankPosition[RANK_POSITION_TABLE_SIZE];
 } HUF_buildCTable_wksp_tables;
 
+/* RANK_POSITION_DISTINCT_COUNT_CUTOFF == Cutoff point in HUF_sort() buckets for which we use log2 bucketing.
+ * Strategy is to use as many buckets as possible for representing distinct
+ * counts while using the remainder to represent all "large" counts.
+ *
+ * To satisfy this requirement for 192 buckets, we can do the following:
+ * Let buckets 0-166 represent distinct counts of [0, 166]
+ * Let buckets 166 to 192 represent all remaining counts up to RANK_POSITION_MAX_COUNT_LOG using log2 bucketing.
+ */
+#define RANK_POSITION_MAX_COUNT_LOG 32
+#define RANK_POSITION_LOG_BUCKETS_BEGIN ((RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */)
+#define RANK_POSITION_DISTINCT_COUNT_CUTOFF (RANK_POSITION_LOG_BUCKETS_BEGIN + ZSTD_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */)
+
+/* Return the appropriate bucket index for a given count. See definition of
+ * RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy.
+ */
+static U32 HUF_getIndex(U32 const count) {
+    return (count < RANK_POSITION_DISTINCT_COUNT_CUTOFF)
+        ? count
+        : ZSTD_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN;
+}
+
+/* Helper swap function for HUF_quickSortPartition() */
+static void HUF_swapNodes(nodeElt* a, nodeElt* b) {
+	nodeElt tmp = *a;
+	*a = *b;
+	*b = tmp;
+}
+
+/* Returns 0 if the huffNode array is not sorted by descending count */
+MEM_STATIC int HUF_isSorted(nodeElt huffNode[], U32 const maxSymbolValue1) {
+    U32 i;
+    for (i = 1; i < maxSymbolValue1; ++i) {
+        if (huffNode[i].count > huffNode[i-1].count) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+/* Insertion sort by descending order */
+HINT_INLINE void HUF_insertionSort(nodeElt huffNode[], int const low, int const high) {
+    int i;
+    int const size = high-low+1;
+    huffNode += low;
+    for (i = 1; i < size; ++i) {
+        nodeElt const key = huffNode[i];
+        int j = i - 1;
+        while (j >= 0 && huffNode[j].count < key.count) {
+            huffNode[j + 1] = huffNode[j];
+            j--;
+        }
+        huffNode[j + 1] = key;
+    }
+}
+
+/* Pivot helper function for quicksort. */
+static int HUF_quickSortPartition(nodeElt arr[], int const low, int const high) {
+    /* Simply select rightmost element as pivot. "Better" selectors like
+     * median-of-three don't experimentally appear to have any benefit.
+     */
+    U32 const pivot = arr[high].count;
+    int i = low - 1;
+    int j = low;
+    for ( ; j < high; j++) {
+        if (arr[j].count > pivot) {
+            i++;
+            HUF_swapNodes(&arr[i], &arr[j]);
+        }
+    }
+    HUF_swapNodes(&arr[i + 1], &arr[high]);
+    return i + 1;
+}
+
+/* Classic quicksort by descending with partially iterative calls
+ * to reduce worst case callstack size.
+ */
+static void HUF_simpleQuickSort(nodeElt arr[], int low, int high) {
+    int const kInsertionSortThreshold = 8;
+    if (high - low < kInsertionSortThreshold) {
+        HUF_insertionSort(arr, low, high);
+        return;
+    }
+    while (low < high) {
+        int const idx = HUF_quickSortPartition(arr, low, high);
+        if (idx - low < high - idx) {
+            HUF_simpleQuickSort(arr, low, idx - 1);
+            low = idx + 1;
+        } else {
+            HUF_simpleQuickSort(arr, idx + 1, high);
+            high = idx - 1;
+        }
+    }
+}
+
 /**
  * HUF_sort():
  * Sorts the symbols [0, maxSymbolValue] by count[symbol] in decreasing order.
+ * This is a typical bucket sorting strategy that uses either quicksort or insertion sort to sort each bucket.
  *
  * @param[out] huffNode       Sorted symbols by decreasing count. Only members `.count` and `.byte` are filled.
  *                            Must have (maxSymbolValue + 1) entries.
@@ -390,42 +592,51 @@
  * @param[in]  maxSymbolValue Maximum symbol value.
  * @param      rankPosition   This is a scratch workspace. Must have RANK_POSITION_TABLE_SIZE entries.
  */
-static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition)
-{
-    int n;
-    int const maxSymbolValue1 = (int)maxSymbolValue + 1;
+static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSymbolValue, rankPos rankPosition[]) {
+    U32 n;
+    U32 const maxSymbolValue1 = maxSymbolValue+1;
 
     /* Compute base and set curr to base.
-     * For symbol s let lowerRank = BIT_highbit32(count[n]+1) and rank = lowerRank + 1.
-     * Then 2^lowerRank <= count[n]+1 <= 2^rank.
+     * For symbol s let lowerRank = HUF_getIndex(count[n]) and rank = lowerRank + 1.
+     * See HUF_getIndex to see bucketing strategy.
      * We attribute each symbol to lowerRank's base value, because we want to know where
      * each rank begins in the output, so for rank R we want to count ranks R+1 and above.
      */
     ZSTD_memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE);
     for (n = 0; n < maxSymbolValue1; ++n) {
-        U32 lowerRank = BIT_highbit32(count[n] + 1);
+        U32 lowerRank = HUF_getIndex(count[n]);
+        assert(lowerRank < RANK_POSITION_TABLE_SIZE - 1);
         rankPosition[lowerRank].base++;
     }
+
     assert(rankPosition[RANK_POSITION_TABLE_SIZE - 1].base == 0);
+    /* Set up the rankPosition table */
     for (n = RANK_POSITION_TABLE_SIZE - 1; n > 0; --n) {
         rankPosition[n-1].base += rankPosition[n].base;
         rankPosition[n-1].curr = rankPosition[n-1].base;
     }
-    /* Sort */
+
+    /* Insert each symbol into their appropriate bucket, setting up rankPosition table. */
     for (n = 0; n < maxSymbolValue1; ++n) {
         U32 const c = count[n];
-        U32 const r = BIT_highbit32(c+1) + 1;
-        U32 pos = rankPosition[r].curr++;
-        /* Insert into the correct position in the rank.
-         * We have at most 256 symbols, so this insertion should be fine.
-         */
-        while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) {
-            huffNode[pos] = huffNode[pos-1];
-            pos--;
-        }
+        U32 const r = HUF_getIndex(c) + 1;
+        U32 const pos = rankPosition[r].curr++;
+        assert(pos < maxSymbolValue1);
         huffNode[pos].count = c;
         huffNode[pos].byte  = (BYTE)n;
     }
+
+    /* Sort each bucket. */
+    for (n = RANK_POSITION_DISTINCT_COUNT_CUTOFF; n < RANK_POSITION_TABLE_SIZE - 1; ++n) {
+        int const bucketSize = rankPosition[n].curr - rankPosition[n].base;
+        U32 const bucketStartIdx = rankPosition[n].base;
+        if (bucketSize > 1) {
+            assert(bucketStartIdx < maxSymbolValue1);
+            HUF_simpleQuickSort(huffNode + bucketStartIdx, 0, bucketSize-1);
+        }
+    }
+
+    assert(HUF_isSorted(huffNode, maxSymbolValue1));
 }
 
 
@@ -449,6 +660,7 @@
     int lowS, lowN;
     int nodeNb = STARTNODE;
     int n, nodeRoot;
+    DEBUGLOG(5, "HUF_buildTree (alphabet size = %u)", maxSymbolValue + 1);
     /* init for parents */
     nonNullRank = (int)maxSymbolValue;
     while(huffNode[nonNullRank].count == 0) nonNullRank--;
@@ -475,6 +687,8 @@
     for (n=0; n<=nonNullRank; n++)
         huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
 
+    DEBUGLOG(6, "Initial distribution of bits completed (%zu sorted symbols)", showHNodeBits(huffNode, maxSymbolValue+1));
+
     return nonNullRank;
 }
 
@@ -490,6 +704,7 @@
  */
 static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, int nonNullRank, U32 maxSymbolValue, U32 maxNbBits)
 {
+    HUF_CElt* const ct = CTable + 1;
     /* fill result into ctable (val, nbBits) */
     int n;
     U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0};
@@ -505,127 +720,373 @@
             min >>= 1;
     }   }
     for (n=0; n<alphabetSize; n++)
-        CTable[huffNode[n].byte].nbBits = huffNode[n].nbBits;   /* push nbBits per symbol, symbol order */
+        HUF_setNbBits(ct + huffNode[n].byte, huffNode[n].nbBits);   /* push nbBits per symbol, symbol order */
     for (n=0; n<alphabetSize; n++)
-        CTable[n].val = valPerRank[CTable[n].nbBits]++;   /* assign value within rank, symbol order */
+        HUF_setValue(ct + n, valPerRank[HUF_getNbBits(ct[n])]++);   /* assign value within rank, symbol order */
+    CTable[0] = maxNbBits;
 }
 
-size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
+size_t
+HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
+                     void* workSpace, size_t wkspSize)
 {
-    HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace;
+    HUF_buildCTable_wksp_tables* const wksp_tables =
+        (HUF_buildCTable_wksp_tables*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(U32));
     nodeElt* const huffNode0 = wksp_tables->huffNodeTbl;
     nodeElt* const huffNode = huffNode0+1;
     int nonNullRank;
 
+    HUF_STATIC_ASSERT(HUF_CTABLE_WORKSPACE_SIZE == sizeof(HUF_buildCTable_wksp_tables));
+
+    DEBUGLOG(5, "HUF_buildCTable_wksp (alphabet size = %u)", maxSymbolValue+1);
+
     /* safety checks */
-    if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC);  /* must be aligned on 4-bytes boundaries */
     if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))
-      return ERROR(workSpace_tooSmall);
+        return ERROR(workSpace_tooSmall);
     if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
     if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
-      return ERROR(maxSymbolValue_tooLarge);
+        return ERROR(maxSymbolValue_tooLarge);
     ZSTD_memset(huffNode0, 0, sizeof(huffNodeTable));
 
     /* sort, decreasing order */
     HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition);
+    DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1));
 
     /* build tree */
     nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
 
-    /* enforce maxTableLog */
+    /* determine and enforce maxTableLog */
     maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
     if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC);   /* check fit into table */
 
-    HUF_buildCTableFromTree(tree, huffNode, nonNullRank, maxSymbolValue, maxNbBits);
+    HUF_buildCTableFromTree(CTable, huffNode, nonNullRank, maxSymbolValue, maxNbBits);
 
     return maxNbBits;
 }
 
 size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)
 {
+    HUF_CElt const* ct = CTable + 1;
     size_t nbBits = 0;
     int s;
     for (s = 0; s <= (int)maxSymbolValue; ++s) {
-        nbBits += CTable[s].nbBits * count[s];
+        nbBits += HUF_getNbBits(ct[s]) * count[s];
     }
     return nbBits >> 3;
 }
 
 int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) {
+  HUF_CElt const* ct = CTable + 1;
   int bad = 0;
   int s;
   for (s = 0; s <= (int)maxSymbolValue; ++s) {
-    bad |= (count[s] != 0) & (CTable[s].nbBits == 0);
+    bad |= (count[s] != 0) & (HUF_getNbBits(ct[s]) == 0);
   }
   return !bad;
 }
 
 size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
 
-FORCE_INLINE_TEMPLATE void
-HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
+/** HUF_CStream_t:
+ * Huffman uses its own BIT_CStream_t implementation.
+ * There are three major differences from BIT_CStream_t:
+ *   1. HUF_addBits() takes a HUF_CElt (size_t) which is
+ *      the pair (nbBits, value) in the format:
+ *      format:
+ *        - Bits [0, 4)            = nbBits
+ *        - Bits [4, 64 - nbBits)  = 0
+ *        - Bits [64 - nbBits, 64) = value
+ *   2. The bitContainer is built from the upper bits and
+ *      right shifted. E.g. to add a new value of N bits
+ *      you right shift the bitContainer by N, then or in
+ *      the new value into the N upper bits.
+ *   3. The bitstream has two bit containers. You can add
+ *      bits to the second container and merge them into
+ *      the first container.
+ */
+
+#define HUF_BITS_IN_CONTAINER (sizeof(size_t) * 8)
+
+typedef struct {
+    size_t bitContainer[2];
+    size_t bitPos[2];
+
+    BYTE* startPtr;
+    BYTE* ptr;
+    BYTE* endPtr;
+} HUF_CStream_t;
+
+/**! HUF_initCStream():
+ * Initializes the bitstream.
+ * @returns 0 or an error code.
+ */
+static size_t HUF_initCStream(HUF_CStream_t* bitC,
+                                  void* startPtr, size_t dstCapacity)
 {
-    BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
+    ZSTD_memset(bitC, 0, sizeof(*bitC));
+    bitC->startPtr = (BYTE*)startPtr;
+    bitC->ptr = bitC->startPtr;
+    bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->bitContainer[0]);
+    if (dstCapacity <= sizeof(bitC->bitContainer[0])) return ERROR(dstSize_tooSmall);
+    return 0;
 }
 
-#define HUF_FLUSHBITS(s)  BIT_flushBits(s)
+/*! HUF_addBits():
+ * Adds the symbol stored in HUF_CElt elt to the bitstream.
+ *
+ * @param elt   The element we're adding. This is a (nbBits, value) pair.
+ *              See the HUF_CStream_t docs for the format.
+ * @param idx   Insert into the bitstream at this idx.
+ * @param kFast This is a template parameter. If the bitstream is guaranteed
+ *              to have at least 4 unused bits after this call it may be 1,
+ *              otherwise it must be 0. HUF_addBits() is faster when fast is set.
+ */
+FORCE_INLINE_TEMPLATE void HUF_addBits(HUF_CStream_t* bitC, HUF_CElt elt, int idx, int kFast)
+{
+    assert(idx <= 1);
+    assert(HUF_getNbBits(elt) <= HUF_TABLELOG_ABSOLUTEMAX);
+    /* This is efficient on x86-64 with BMI2 because shrx
+     * only reads the low 6 bits of the register. The compiler
+     * knows this and elides the mask. When fast is set,
+     * every operation can use the same value loaded from elt.
+     */
+    bitC->bitContainer[idx] >>= HUF_getNbBits(elt);
+    bitC->bitContainer[idx] |= kFast ? HUF_getValueFast(elt) : HUF_getValue(elt);
+    /* We only read the low 8 bits of bitC->bitPos[idx] so it
+     * doesn't matter that the high bits have noise from the value.
+     */
+    bitC->bitPos[idx] += HUF_getNbBitsFast(elt);
+    assert((bitC->bitPos[idx] & 0xFF) <= HUF_BITS_IN_CONTAINER);
+    /* The last 4-bits of elt are dirty if fast is set,
+     * so we must not be overwriting bits that have already been
+     * inserted into the bit container.
+     */
+#if DEBUGLEVEL >= 1
+    {
+        size_t const nbBits = HUF_getNbBits(elt);
+        size_t const dirtyBits = nbBits == 0 ? 0 : ZSTD_highbit32((U32)nbBits) + 1;
+        (void)dirtyBits;
+        /* Middle bits are 0. */
+        assert(((elt >> dirtyBits) << (dirtyBits + nbBits)) == 0);
+        /* We didn't overwrite any bits in the bit container. */
+        assert(!kFast || (bitC->bitPos[idx] & 0xFF) <= HUF_BITS_IN_CONTAINER);
+        (void)dirtyBits;
+    }
+#endif
+}
 
-#define HUF_FLUSHBITS_1(stream) \
-    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
+FORCE_INLINE_TEMPLATE void HUF_zeroIndex1(HUF_CStream_t* bitC)
+{
+    bitC->bitContainer[1] = 0;
+    bitC->bitPos[1] = 0;
+}
 
-#define HUF_FLUSHBITS_2(stream) \
-    if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
+/*! HUF_mergeIndex1() :
+ * Merges the bit container @ index 1 into the bit container @ index 0
+ * and zeros the bit container @ index 1.
+ */
+FORCE_INLINE_TEMPLATE void HUF_mergeIndex1(HUF_CStream_t* bitC)
+{
+    assert((bitC->bitPos[1] & 0xFF) < HUF_BITS_IN_CONTAINER);
+    bitC->bitContainer[0] >>= (bitC->bitPos[1] & 0xFF);
+    bitC->bitContainer[0] |= bitC->bitContainer[1];
+    bitC->bitPos[0] += bitC->bitPos[1];
+    assert((bitC->bitPos[0] & 0xFF) <= HUF_BITS_IN_CONTAINER);
+}
+
+/*! HUF_flushBits() :
+* Flushes the bits in the bit container @ index 0.
+*
+* @post bitPos will be < 8.
+* @param kFast If kFast is set then we must know a-priori that
+*              the bit container will not overflow.
+*/
+FORCE_INLINE_TEMPLATE void HUF_flushBits(HUF_CStream_t* bitC, int kFast)
+{
+    /* The upper bits of bitPos are noisy, so we must mask by 0xFF. */
+    size_t const nbBits = bitC->bitPos[0] & 0xFF;
+    size_t const nbBytes = nbBits >> 3;
+    /* The top nbBits bits of bitContainer are the ones we need. */
+    size_t const bitContainer = bitC->bitContainer[0] >> (HUF_BITS_IN_CONTAINER - nbBits);
+    /* Mask bitPos to account for the bytes we consumed. */
+    bitC->bitPos[0] &= 7;
+    assert(nbBits > 0);
+    assert(nbBits <= sizeof(bitC->bitContainer[0]) * 8);
+    assert(bitC->ptr <= bitC->endPtr);
+    MEM_writeLEST(bitC->ptr, bitContainer);
+    bitC->ptr += nbBytes;
+    assert(!kFast || bitC->ptr <= bitC->endPtr);
+    if (!kFast && bitC->ptr > bitC->endPtr) bitC->ptr = bitC->endPtr;
+    /* bitContainer doesn't need to be modified because the leftover
+     * bits are already the top bitPos bits. And we don't care about
+     * noise in the lower values.
+     */
+}
+
+/*! HUF_endMark()
+ * @returns The Huffman stream end mark: A 1-bit value = 1.
+ */
+static HUF_CElt HUF_endMark(void)
+{
+    HUF_CElt endMark;
+    HUF_setNbBits(&endMark, 1);
+    HUF_setValue(&endMark, 1);
+    return endMark;
+}
+
+/*! HUF_closeCStream() :
+ *  @return Size of CStream, in bytes,
+ *          or 0 if it could not fit into dstBuffer */
+static size_t HUF_closeCStream(HUF_CStream_t* bitC)
+{
+    HUF_addBits(bitC, HUF_endMark(), /* idx */ 0, /* kFast */ 0);
+    HUF_flushBits(bitC, /* kFast */ 0);
+    {
+        size_t const nbBits = bitC->bitPos[0] & 0xFF;
+        if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
+        return (size_t)(bitC->ptr - bitC->startPtr) + (nbBits > 0);
+    }
+}
+
+FORCE_INLINE_TEMPLATE void
+HUF_encodeSymbol(HUF_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable, int idx, int fast)
+{
+    HUF_addBits(bitCPtr, CTable[symbol], idx, fast);
+}
+
+FORCE_INLINE_TEMPLATE void
+HUF_compress1X_usingCTable_internal_body_loop(HUF_CStream_t* bitC,
+                                   const BYTE* ip, size_t srcSize,
+                                   const HUF_CElt* ct,
+                                   int kUnroll, int kFastFlush, int kLastFast)
+{
+    /* Join to kUnroll */
+    int n = (int)srcSize;
+    int rem = n % kUnroll;
+    if (rem > 0) {
+        for (; rem > 0; --rem) {
+            HUF_encodeSymbol(bitC, ip[--n], ct, 0, /* fast */ 0);
+        }
+        HUF_flushBits(bitC, kFastFlush);
+    }
+    assert(n % kUnroll == 0);
+
+    /* Join to 2 * kUnroll */
+    if (n % (2 * kUnroll)) {
+        int u;
+        for (u = 1; u < kUnroll; ++u) {
+            HUF_encodeSymbol(bitC, ip[n - u], ct, 0, 1);
+        }
+        HUF_encodeSymbol(bitC, ip[n - kUnroll], ct, 0, kLastFast);
+        HUF_flushBits(bitC, kFastFlush);
+        n -= kUnroll;
+    }
+    assert(n % (2 * kUnroll) == 0);
+
+    for (; n>0; n-= 2 * kUnroll) {
+        /* Encode kUnroll symbols into the bitstream @ index 0. */
+        int u;
+        for (u = 1; u < kUnroll; ++u) {
+            HUF_encodeSymbol(bitC, ip[n - u], ct, /* idx */ 0, /* fast */ 1);
+        }
+        HUF_encodeSymbol(bitC, ip[n - kUnroll], ct, /* idx */ 0, /* fast */ kLastFast);
+        HUF_flushBits(bitC, kFastFlush);
+        /* Encode kUnroll symbols into the bitstream @ index 1.
+         * This allows us to start filling the bit container
+         * without any data dependencies.
+         */
+        HUF_zeroIndex1(bitC);
+        for (u = 1; u < kUnroll; ++u) {
+            HUF_encodeSymbol(bitC, ip[n - kUnroll - u], ct, /* idx */ 1, /* fast */ 1);
+        }
+        HUF_encodeSymbol(bitC, ip[n - kUnroll - kUnroll], ct, /* idx */ 1, /* fast */ kLastFast);
+        /* Merge bitstream @ index 1 into the bitstream @ index 0 */
+        HUF_mergeIndex1(bitC);
+        HUF_flushBits(bitC, kFastFlush);
+    }
+    assert(n == 0);
+
+}
+
+/**
+ * Returns a tight upper bound on the output space needed by Huffman
+ * with 8 bytes buffer to handle over-writes. If the output is at least
+ * this large we don't need to do bounds checks during Huffman encoding.
+ */
+static size_t HUF_tightCompressBound(size_t srcSize, size_t tableLog)
+{
+    return ((srcSize * tableLog) >> 3) + 8;
+}
+
 
 FORCE_INLINE_TEMPLATE size_t
 HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize,
                                    const void* src, size_t srcSize,
                                    const HUF_CElt* CTable)
 {
+    U32 const tableLog = (U32)CTable[0];
+    HUF_CElt const* ct = CTable + 1;
     const BYTE* ip = (const BYTE*) src;
     BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + dstSize;
     BYTE* op = ostart;
-    size_t n;
-    BIT_CStream_t bitC;
+    HUF_CStream_t bitC;
 
     /* init */
     if (dstSize < 8) return 0;   /* not enough space to compress */
-    { size_t const initErr = BIT_initCStream(&bitC, op, (size_t)(oend-op));
+    { size_t const initErr = HUF_initCStream(&bitC, op, (size_t)(oend-op));
       if (HUF_isError(initErr)) return 0; }
 
-    n = srcSize & ~3;  /* join to mod 4 */
-    switch (srcSize & 3)
-    {
-        case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable);
-                 HUF_FLUSHBITS_2(&bitC);
-		 /* fall-through */
-        case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable);
-                 HUF_FLUSHBITS_1(&bitC);
-		 /* fall-through */
-        case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable);
-                 HUF_FLUSHBITS(&bitC);
-		 /* fall-through */
-        case 0 : /* fall-through */
-        default: break;
+    if (dstSize < HUF_tightCompressBound(srcSize, (size_t)tableLog) || tableLog > 11)
+        HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ MEM_32bits() ? 2 : 4, /* kFast */ 0, /* kLastFast */ 0);
+    else {
+        if (MEM_32bits()) {
+            switch (tableLog) {
+            case 11:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 2, /* kFastFlush */ 1, /* kLastFast */ 0);
+                break;
+            case 10: ZSTD_FALLTHROUGH;
+            case 9: ZSTD_FALLTHROUGH;
+            case 8:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 2, /* kFastFlush */ 1, /* kLastFast */ 1);
+                break;
+            case 7: ZSTD_FALLTHROUGH;
+            default:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 3, /* kFastFlush */ 1, /* kLastFast */ 1);
+                break;
+            }
+        } else {
+            switch (tableLog) {
+            case 11:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 5, /* kFastFlush */ 1, /* kLastFast */ 0);
+                break;
+            case 10:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 5, /* kFastFlush */ 1, /* kLastFast */ 1);
+                break;
+            case 9:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 6, /* kFastFlush */ 1, /* kLastFast */ 0);
+                break;
+            case 8:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 7, /* kFastFlush */ 1, /* kLastFast */ 0);
+                break;
+            case 7:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 8, /* kFastFlush */ 1, /* kLastFast */ 0);
+                break;
+            case 6: ZSTD_FALLTHROUGH;
+            default:
+                HUF_compress1X_usingCTable_internal_body_loop(&bitC, ip, srcSize, ct, /* kUnroll */ 9, /* kFastFlush */ 1, /* kLastFast */ 1);
+                break;
+            }
+        }
     }
+    assert(bitC.ptr <= bitC.endPtr);
 
-    for (; n>0; n-=4) {  /* note : n&3==0 at this stage */
-        HUF_encodeSymbol(&bitC, ip[n- 1], CTable);
-        HUF_FLUSHBITS_1(&bitC);
-        HUF_encodeSymbol(&bitC, ip[n- 2], CTable);
-        HUF_FLUSHBITS_2(&bitC);
-        HUF_encodeSymbol(&bitC, ip[n- 3], CTable);
-        HUF_FLUSHBITS_1(&bitC);
-        HUF_encodeSymbol(&bitC, ip[n- 4], CTable);
-        HUF_FLUSHBITS(&bitC);
-    }
-
-    return BIT_closeCStream(&bitC);
+    return HUF_closeCStream(&bitC);
 }
 
 #if DYNAMIC_BMI2
 
-static TARGET_ATTRIBUTE("bmi2") size_t
+static BMI2_TARGET_ATTRIBUTE size_t
 HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize,
                                    const void* src, size_t srcSize,
                                    const HUF_CElt* CTable)
@@ -644,9 +1105,9 @@
 static size_t
 HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
                               const void* src, size_t srcSize,
-                              const HUF_CElt* CTable, const int bmi2)
+                              const HUF_CElt* CTable, const int flags)
 {
-    if (bmi2) {
+    if (flags & HUF_flags_bmi2) {
         return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
     }
     return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
@@ -657,24 +1118,23 @@
 static size_t
 HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
                               const void* src, size_t srcSize,
-                              const HUF_CElt* CTable, const int bmi2)
+                              const HUF_CElt* CTable, const int flags)
 {
-    (void)bmi2;
+    (void)flags;
     return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
 }
 
 #endif
 
-size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags)
 {
-    return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+    return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags);
 }
 
-
 static size_t
 HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
                               const void* src, size_t srcSize,
-                              const HUF_CElt* CTable, int bmi2)
+                              const HUF_CElt* CTable, int flags)
 {
     size_t const segmentSize = (srcSize+3)/4;   /* first 3 segments */
     const BYTE* ip = (const BYTE*) src;
@@ -688,27 +1148,24 @@
     op += 6;   /* jumpTable */
 
     assert(op <= oend);
-    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
-        if (cSize==0) return 0;
-        assert(cSize <= 65535);
+    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) );
+        if (cSize == 0 || cSize > 65535) return 0;
         MEM_writeLE16(ostart, (U16)cSize);
         op += cSize;
     }
 
     ip += segmentSize;
     assert(op <= oend);
-    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
-        if (cSize==0) return 0;
-        assert(cSize <= 65535);
+    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) );
+        if (cSize == 0 || cSize > 65535) return 0;
         MEM_writeLE16(ostart+2, (U16)cSize);
         op += cSize;
     }
 
     ip += segmentSize;
     assert(op <= oend);
-    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
-        if (cSize==0) return 0;
-        assert(cSize <= 65535);
+    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, flags) );
+        if (cSize == 0 || cSize > 65535) return 0;
         MEM_writeLE16(ostart+4, (U16)cSize);
         op += cSize;
     }
@@ -716,17 +1173,17 @@
     ip += segmentSize;
     assert(op <= oend);
     assert(ip <= iend);
-    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, bmi2) );
-        if (cSize==0) return 0;
+    {   CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, flags) );
+        if (cSize == 0 || cSize > 65535) return 0;
         op += cSize;
     }
 
     return (size_t)(op-ostart);
 }
 
-size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
+size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags)
 {
-    return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
+    return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, flags);
 }
 
 typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e;
@@ -734,11 +1191,11 @@
 static size_t HUF_compressCTable_internal(
                 BYTE* const ostart, BYTE* op, BYTE* const oend,
                 const void* src, size_t srcSize,
-                HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2)
+                HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int flags)
 {
     size_t const cSize = (nbStreams==HUF_singleStream) ?
-                         HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2) :
-                         HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2);
+                         HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags) :
+                         HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, flags);
     if (HUF_isError(cSize)) { return cSize; }
     if (cSize==0) { return 0; }   /* uncompressible */
     op += cSize;
@@ -750,35 +1207,111 @@
 
 typedef struct {
     unsigned count[HUF_SYMBOLVALUE_MAX + 1];
-    HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];
+    HUF_CElt CTable[HUF_CTABLE_SIZE_ST(HUF_SYMBOLVALUE_MAX)];
     union {
         HUF_buildCTable_wksp_tables buildCTable_wksp;
         HUF_WriteCTableWksp writeCTable_wksp;
+        U32 hist_wksp[HIST_WKSP_SIZE_U32];
     } wksps;
 } HUF_compress_tables_t;
 
+#define SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE 4096
+#define SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO 10  /* Must be >= 2 */
+
+unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue)
+{
+    unsigned cardinality = 0;
+    unsigned i;
+
+    for (i = 0; i < maxSymbolValue + 1; i++) {
+        if (count[i] != 0) cardinality += 1;
+    }
+
+    return cardinality;
+}
+
+unsigned HUF_minTableLog(unsigned symbolCardinality)
+{
+    U32 minBitsSymbols = ZSTD_highbit32(symbolCardinality) + 1;
+    return minBitsSymbols;
+}
+
+unsigned HUF_optimalTableLog(
+            unsigned maxTableLog,
+            size_t srcSize,
+            unsigned maxSymbolValue,
+            void* workSpace, size_t wkspSize,
+            HUF_CElt* table,
+      const unsigned* count,
+            int flags)
+{
+    assert(srcSize > 1); /* Not supported, RLE should be used instead */
+    assert(wkspSize >= sizeof(HUF_buildCTable_wksp_tables));
+
+    if (!(flags & HUF_flags_optimalDepth)) {
+        /* cheap evaluation, based on FSE */
+        return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+    }
+
+    {   BYTE* dst = (BYTE*)workSpace + sizeof(HUF_WriteCTableWksp);
+        size_t dstSize = wkspSize - sizeof(HUF_WriteCTableWksp);
+        size_t maxBits, hSize, newSize;
+        const unsigned symbolCardinality = HUF_cardinality(count, maxSymbolValue);
+        const unsigned minTableLog = HUF_minTableLog(symbolCardinality);
+        size_t optSize = ((size_t) ~0) - 1;
+        unsigned optLog = maxTableLog, optLogGuess;
+
+        DEBUGLOG(6, "HUF_optimalTableLog: probing huf depth (srcSize=%zu)", srcSize);
+
+        /* Search until size increases */
+        for (optLogGuess = minTableLog; optLogGuess <= maxTableLog; optLogGuess++) {
+            DEBUGLOG(7, "checking for huffLog=%u", optLogGuess);
+            maxBits = HUF_buildCTable_wksp(table, count, maxSymbolValue, optLogGuess, workSpace, wkspSize);
+            if (ERR_isError(maxBits)) continue;
+
+            if (maxBits < optLogGuess && optLogGuess > minTableLog) break;
+
+            hSize = HUF_writeCTable_wksp(dst, dstSize, table, maxSymbolValue, (U32)maxBits, workSpace, wkspSize);
+
+            if (ERR_isError(hSize)) continue;
+
+            newSize = HUF_estimateCompressedSize(table, count, maxSymbolValue) + hSize;
+
+            if (newSize > optSize + 1) {
+                break;
+            }
+
+            if (newSize < optSize) {
+                optSize = newSize;
+                optLog = optLogGuess;
+            }
+        }
+        assert(optLog <= HUF_TABLELOG_MAX);
+        return optLog;
+    }
+}
+
 /* HUF_compress_internal() :
  * `workSpace_align4` must be aligned on 4-bytes boundaries,
- * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U32 unsigned */
+ * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U64 unsigned */
 static size_t
 HUF_compress_internal (void* dst, size_t dstSize,
                  const void* src, size_t srcSize,
                        unsigned maxSymbolValue, unsigned huffLog,
                        HUF_nbStreams_e nbStreams,
-                       void* workSpace_align4, size_t wkspSize,
-                       HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat,
-                 const int bmi2)
+                       void* workSpace, size_t wkspSize,
+                       HUF_CElt* oldHufTable, HUF_repeat* repeat, int flags)
 {
-    HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace_align4;
+    HUF_compress_tables_t* const table = (HUF_compress_tables_t*)HUF_alignUpWorkspace(workSpace, &wkspSize, ZSTD_ALIGNOF(size_t));
     BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + dstSize;
     BYTE* op = ostart;
 
-    HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE);
-    assert(((size_t)workSpace_align4 & 3) == 0);   /* must be aligned on 4-bytes boundaries */
+    DEBUGLOG(5, "HUF_compress_internal (srcSize=%zu)", srcSize);
+    HUF_STATIC_ASSERT(sizeof(*table) + HUF_WORKSPACE_MAX_ALIGNMENT <= HUF_WORKSPACE_SIZE);
 
     /* checks & inits */
-    if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall);
+    if (wkspSize < sizeof(*table)) return ERROR(workSpace_tooSmall);
     if (!srcSize) return 0;  /* Uncompressed */
     if (!dstSize) return 0;  /* cannot fit anything within dst budget */
     if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong);   /* current block size limit */
@@ -788,17 +1321,34 @@
     if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT;
 
     /* Heuristic : If old table is valid, use it for small inputs */
-    if (preferRepeat && repeat && *repeat == HUF_repeat_valid) {
+    if ((flags & HUF_flags_preferRepeat) && repeat && *repeat == HUF_repeat_valid) {
         return HUF_compressCTable_internal(ostart, op, oend,
                                            src, srcSize,
-                                           nbStreams, oldHufTable, bmi2);
+                                           nbStreams, oldHufTable, flags);
+    }
+
+    /* If uncompressible data is suspected, do a smaller sampling first */
+    DEBUG_STATIC_ASSERT(SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO >= 2);
+    if ((flags & HUF_flags_suspectUncompressible) && srcSize >= (SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE * SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO)) {
+        size_t largestTotal = 0;
+        DEBUGLOG(5, "input suspected incompressible : sampling to check");
+        {   unsigned maxSymbolValueBegin = maxSymbolValue;
+            CHECK_V_F(largestBegin, HIST_count_simple (table->count, &maxSymbolValueBegin, (const BYTE*)src, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) );
+            largestTotal += largestBegin;
+        }
+        {   unsigned maxSymbolValueEnd = maxSymbolValue;
+            CHECK_V_F(largestEnd, HIST_count_simple (table->count, &maxSymbolValueEnd, (const BYTE*)src + srcSize - SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) );
+            largestTotal += largestEnd;
+        }
+        if (largestTotal <= ((2 * SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) >> 7)+4) return 0;   /* heuristic : probably not compressible enough */
     }
 
     /* Scan input and build symbol stats */
-    {   CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace_align4, wkspSize) );
+    {   CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, table->wksps.hist_wksp, sizeof(table->wksps.hist_wksp)) );
         if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; }   /* single symbol, rle */
         if (largest <= (srcSize >> 7)+4) return 0;   /* heuristic : probably not compressible enough */
     }
+    DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1));
 
     /* Check validity of previous table */
     if ( repeat
@@ -807,22 +1357,26 @@
         *repeat = HUF_repeat_none;
     }
     /* Heuristic : use existing table for small inputs */
-    if (preferRepeat && repeat && *repeat != HUF_repeat_none) {
+    if ((flags & HUF_flags_preferRepeat) && repeat && *repeat != HUF_repeat_none) {
         return HUF_compressCTable_internal(ostart, op, oend,
                                            src, srcSize,
-                                           nbStreams, oldHufTable, bmi2);
+                                           nbStreams, oldHufTable, flags);
     }
 
     /* Build Huffman Tree */
-    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
+    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, &table->wksps, sizeof(table->wksps), table->CTable, table->count, flags);
     {   size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count,
                                             maxSymbolValue, huffLog,
                                             &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp));
         CHECK_F(maxBits);
         huffLog = (U32)maxBits;
-        /* Zero unused symbols in CTable, so we can check it for validity */
-        ZSTD_memset(table->CTable + (maxSymbolValue + 1), 0,
-               sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt)));
+        DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1));
+    }
+    /* Zero unused symbols in CTable, so we can check it for validity */
+    {
+        size_t const ctableSize = HUF_CTABLE_SIZE_ST(maxSymbolValue);
+        size_t const unusedSize = sizeof(table->CTable) - ctableSize * sizeof(HUF_CElt);
+        ZSTD_memset(table->CTable + ctableSize, 0, unusedSize);
     }
 
     /* Write table description header */
@@ -835,7 +1389,7 @@
             if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) {
                 return HUF_compressCTable_internal(ostart, op, oend,
                                                    src, srcSize,
-                                                   nbStreams, oldHufTable, bmi2);
+                                                   nbStreams, oldHufTable, flags);
         }   }
 
         /* Use the new huffman table */
@@ -847,91 +1401,35 @@
     }
     return HUF_compressCTable_internal(ostart, op, oend,
                                        src, srcSize,
-                                       nbStreams, table->CTable, bmi2);
-}
-
-
-size_t HUF_compress1X_wksp (void* dst, size_t dstSize,
-                      const void* src, size_t srcSize,
-                      unsigned maxSymbolValue, unsigned huffLog,
-                      void* workSpace, size_t wkspSize)
-{
-    return HUF_compress_internal(dst, dstSize, src, srcSize,
-                                 maxSymbolValue, huffLog, HUF_singleStream,
-                                 workSpace, wkspSize,
-                                 NULL, NULL, 0, 0 /*bmi2*/);
+                                       nbStreams, table->CTable, flags);
 }
 
 size_t HUF_compress1X_repeat (void* dst, size_t dstSize,
                       const void* src, size_t srcSize,
                       unsigned maxSymbolValue, unsigned huffLog,
                       void* workSpace, size_t wkspSize,
-                      HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
+                      HUF_CElt* hufTable, HUF_repeat* repeat, int flags)
 {
+    DEBUGLOG(5, "HUF_compress1X_repeat (srcSize = %zu)", srcSize);
     return HUF_compress_internal(dst, dstSize, src, srcSize,
                                  maxSymbolValue, huffLog, HUF_singleStream,
                                  workSpace, wkspSize, hufTable,
-                                 repeat, preferRepeat, bmi2);
+                                 repeat, flags);
 }
 
 /* HUF_compress4X_repeat():
  * compress input using 4 streams.
- * provide workspace to generate compression tables */
-size_t HUF_compress4X_wksp (void* dst, size_t dstSize,
-                      const void* src, size_t srcSize,
-                      unsigned maxSymbolValue, unsigned huffLog,
-                      void* workSpace, size_t wkspSize)
-{
-    return HUF_compress_internal(dst, dstSize, src, srcSize,
-                                 maxSymbolValue, huffLog, HUF_fourStreams,
-                                 workSpace, wkspSize,
-                                 NULL, NULL, 0, 0 /*bmi2*/);
-}
-
-/* HUF_compress4X_repeat():
- * compress input using 4 streams.
+ * consider skipping quickly
  * re-use an existing huffman compression table */
 size_t HUF_compress4X_repeat (void* dst, size_t dstSize,
                       const void* src, size_t srcSize,
                       unsigned maxSymbolValue, unsigned huffLog,
                       void* workSpace, size_t wkspSize,
-                      HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
+                      HUF_CElt* hufTable, HUF_repeat* repeat, int flags)
 {
+    DEBUGLOG(5, "HUF_compress4X_repeat (srcSize = %zu)", srcSize);
     return HUF_compress_internal(dst, dstSize, src, srcSize,
                                  maxSymbolValue, huffLog, HUF_fourStreams,
                                  workSpace, wkspSize,
-                                 hufTable, repeat, preferRepeat, bmi2);
+                                 hufTable, repeat, flags);
 }
-
-#ifndef ZSTD_NO_UNUSED_FUNCTIONS
-/** HUF_buildCTable() :
- * @return : maxNbBits
- *  Note : count is used before tree is written, so they can safely overlap
- */
-size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits)
-{
-    HUF_buildCTable_wksp_tables workspace;
-    return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace));
-}
-
-size_t HUF_compress1X (void* dst, size_t dstSize,
-                 const void* src, size_t srcSize,
-                 unsigned maxSymbolValue, unsigned huffLog)
-{
-    unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
-    return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
-}
-
-size_t HUF_compress2 (void* dst, size_t dstSize,
-                const void* src, size_t srcSize,
-                unsigned maxSymbolValue, unsigned huffLog)
-{
-    unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
-    return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
-}
-
-size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize)
-{
-    return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT);
-}
-#endif
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress.c b/Utilities/cmzstd/lib/compress/zstd_compress.c
index b7ee298..266186c 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,13 +11,12 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
+#include "../common/allocations.h"  /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */
 #include "../common/zstd_deps.h"  /* INT_MAX, ZSTD_memset, ZSTD_memcpy */
-#include "../common/cpu.h"
 #include "../common/mem.h"
 #include "hist.h"           /* HIST_countFast_wksp */
 #define FSE_STATIC_LINKING_ONLY   /* FSE_encodeSymbol */
 #include "../common/fse.h"
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "zstd_compress_internal.h"
 #include "zstd_compress_sequences.h"
@@ -28,6 +27,7 @@
 #include "zstd_opt.h"
 #include "zstd_ldm.h"
 #include "zstd_compress_superblock.h"
+#include  "../common/bits.h"      /* ZSTD_highbit32, ZSTD_rotateRight_U64 */
 
 /* ***************************************************************
 *  Tuning parameters
@@ -42,19 +42,34 @@
 #  define ZSTD_COMPRESS_HEAPMODE 0
 #endif
 
+/*!
+ * ZSTD_HASHLOG3_MAX :
+ * Maximum size of the hash table dedicated to find 3-bytes matches,
+ * in log format, aka 17 => 1 << 17 == 128Ki positions.
+ * This structure is only used in zstd_opt.
+ * Since allocation is centralized for all strategies, it has to be known here.
+ * The actual (selected) size of the hash table is then stored in ZSTD_matchState_t.hashLog3,
+ * so that zstd_opt.c doesn't need to know about this constant.
+ */
+#ifndef ZSTD_HASHLOG3_MAX
+#  define ZSTD_HASHLOG3_MAX 17
+#endif
 
 /*-*************************************
 *  Helper functions
 ***************************************/
 /* ZSTD_compressBound()
- * Note that the result from this function is only compatible with the "normal"
- * full-block strategy.
- * When there are a lot of small blocks due to frequent flush in streaming mode
- * the overhead of headers can make the compressed data to be larger than the
- * return value of ZSTD_compressBound().
+ * Note that the result from this function is only valid for
+ * the one-pass compression functions.
+ * When employing the streaming mode,
+ * if flushes are frequently altering the size of blocks,
+ * the overhead from block headers can make the compressed data larger
+ * than the return value of ZSTD_compressBound().
  */
 size_t ZSTD_compressBound(size_t srcSize) {
-    return ZSTD_COMPRESSBOUND(srcSize);
+    size_t const r = ZSTD_COMPRESSBOUND(srcSize);
+    if (r==0) return ERROR(srcSize_wrong);
+    return r;
 }
 
 
@@ -72,10 +87,10 @@
     ZSTD_customMem customMem;
     U32 dictID;
     int compressionLevel; /* 0 indicates that advanced API was used to select CDict params */
-    ZSTD_useRowMatchFinderMode_e useRowMatchFinder; /* Indicates whether the CDict was created with params that would use
-                                                     * row-based matchfinder. Unless the cdict is reloaded, we will use
-                                                     * the same greedy/lazy matchfinder at compression time.
-                                                     */
+    ZSTD_paramSwitch_e useRowMatchFinder; /* Indicates whether the CDict was created with params that would use
+                                           * row-based matchfinder. Unless the cdict is reloaded, we will use
+                                           * the same greedy/lazy matchfinder at compression time.
+                                           */
 };  /* typedef'd to ZSTD_CDict within "zstd.h" */
 
 ZSTD_CCtx* ZSTD_createCCtx(void)
@@ -88,7 +103,7 @@
     assert(cctx != NULL);
     ZSTD_memset(cctx, 0, sizeof(*cctx));
     cctx->customMem = memManager;
-    cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+    cctx->bmi2 = ZSTD_cpuSupportsBmi2();
     {   size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters);
         assert(!ZSTD_isError(err));
         (void)err;
@@ -166,12 +181,9 @@
     if (cctx==NULL) return 0;   /* support free on NULL */
     RETURN_ERROR_IF(cctx->staticSize, memory_allocation,
                     "not compatible with static CCtx");
-    {
-        int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx);
+    {   int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx);
         ZSTD_freeCCtxContent(cctx);
-        if (!cctxInWorkspace) {
-            ZSTD_customFree(cctx, cctx->customMem);
-        }
+        if (!cctxInWorkspace) ZSTD_customFree(cctx, cctx->customMem);
     }
     return 0;
 }
@@ -214,55 +226,84 @@
 /* Returns true if the strategy and useRowMatchFinder mode indicate that we will use the row based matchfinder
  * for this compression.
  */
-static int ZSTD_rowMatchFinderUsed(const ZSTD_strategy strategy, const ZSTD_useRowMatchFinderMode_e mode) {
-    assert(mode != ZSTD_urm_auto);
-    return ZSTD_rowMatchFinderSupported(strategy) && (mode == ZSTD_urm_enableRowMatchFinder);
+static int ZSTD_rowMatchFinderUsed(const ZSTD_strategy strategy, const ZSTD_paramSwitch_e mode) {
+    assert(mode != ZSTD_ps_auto);
+    return ZSTD_rowMatchFinderSupported(strategy) && (mode == ZSTD_ps_enable);
 }
 
-/* Returns row matchfinder usage enum given an initial mode and cParams */
-static ZSTD_useRowMatchFinderMode_e ZSTD_resolveRowMatchFinderMode(ZSTD_useRowMatchFinderMode_e mode,
-                                                                   const ZSTD_compressionParameters* const cParams) {
-#if !defined(ZSTD_NO_INTRINSICS) && (defined(__SSE2__) || defined(__ARM_NEON))
+/* Returns row matchfinder usage given an initial mode and cParams */
+static ZSTD_paramSwitch_e ZSTD_resolveRowMatchFinderMode(ZSTD_paramSwitch_e mode,
+                                                         const ZSTD_compressionParameters* const cParams) {
+#if defined(ZSTD_ARCH_X86_SSE2) || defined(ZSTD_ARCH_ARM_NEON)
     int const kHasSIMD128 = 1;
 #else
     int const kHasSIMD128 = 0;
 #endif
-    if (mode != ZSTD_urm_auto) return mode; /* if requested enabled, but no SIMD, we still will use row matchfinder */
-    mode = ZSTD_urm_disableRowMatchFinder;
+    if (mode != ZSTD_ps_auto) return mode; /* if requested enabled, but no SIMD, we still will use row matchfinder */
+    mode = ZSTD_ps_disable;
     if (!ZSTD_rowMatchFinderSupported(cParams->strategy)) return mode;
     if (kHasSIMD128) {
-        if (cParams->windowLog > 14) mode = ZSTD_urm_enableRowMatchFinder;
+        if (cParams->windowLog > 14) mode = ZSTD_ps_enable;
     } else {
-        if (cParams->windowLog > 17) mode = ZSTD_urm_enableRowMatchFinder;
+        if (cParams->windowLog > 17) mode = ZSTD_ps_enable;
     }
     return mode;
 }
 
+/* Returns block splitter usage (generally speaking, when using slower/stronger compression modes) */
+static ZSTD_paramSwitch_e ZSTD_resolveBlockSplitterMode(ZSTD_paramSwitch_e mode,
+                                                        const ZSTD_compressionParameters* const cParams) {
+    if (mode != ZSTD_ps_auto) return mode;
+    return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 17) ? ZSTD_ps_enable : ZSTD_ps_disable;
+}
+
 /* Returns 1 if the arguments indicate that we should allocate a chainTable, 0 otherwise */
 static int ZSTD_allocateChainTable(const ZSTD_strategy strategy,
-                                   const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                                   const ZSTD_paramSwitch_e useRowMatchFinder,
                                    const U32 forDDSDict) {
-    assert(useRowMatchFinder != ZSTD_urm_auto);
+    assert(useRowMatchFinder != ZSTD_ps_auto);
     /* We always should allocate a chaintable if we are allocating a matchstate for a DDS dictionary matchstate.
      * We do not allocate a chaintable if we are using ZSTD_fast, or are using the row-based matchfinder.
      */
     return forDDSDict || ((strategy != ZSTD_fast) && !ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder));
 }
 
-/* Returns 1 if compression parameters are such that we should
+/* Returns ZSTD_ps_enable if compression parameters are such that we should
  * enable long distance matching (wlog >= 27, strategy >= btopt).
- * Returns 0 otherwise.
+ * Returns ZSTD_ps_disable otherwise.
  */
-static U32 ZSTD_CParams_shouldEnableLdm(const ZSTD_compressionParameters* const cParams) {
-    return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27;
+static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode,
+                                 const ZSTD_compressionParameters* const cParams) {
+    if (mode != ZSTD_ps_auto) return mode;
+    return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27) ? ZSTD_ps_enable : ZSTD_ps_disable;
 }
 
-/* Returns 1 if compression parameters are such that we should
- * enable blockSplitter (wlog >= 17, strategy >= btopt).
- * Returns 0 otherwise.
- */
-static U32 ZSTD_CParams_useBlockSplitter(const ZSTD_compressionParameters* const cParams) {
-    return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 17;
+static int ZSTD_resolveExternalSequenceValidation(int mode) {
+    return mode;
+}
+
+/* Resolves maxBlockSize to the default if no value is present. */
+static size_t ZSTD_resolveMaxBlockSize(size_t maxBlockSize) {
+    if (maxBlockSize == 0) {
+        return ZSTD_BLOCKSIZE_MAX;
+    } else {
+        return maxBlockSize;
+    }
+}
+
+static ZSTD_paramSwitch_e ZSTD_resolveExternalRepcodeSearch(ZSTD_paramSwitch_e value, int cLevel) {
+    if (value != ZSTD_ps_auto) return value;
+    if (cLevel < 10) {
+        return ZSTD_ps_disable;
+    } else {
+        return ZSTD_ps_enable;
+    }
+}
+
+/* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged.
+ * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */
+static int ZSTD_CDictIndicesAreTagged(const ZSTD_compressionParameters* const cParams) {
+    return cParams->strategy == ZSTD_fast || cParams->strategy == ZSTD_dfast;
 }
 
 static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
@@ -274,21 +315,18 @@
     cctxParams.cParams = cParams;
 
     /* Adjust advanced params according to cParams */
-    if (ZSTD_CParams_shouldEnableLdm(&cParams)) {
-        DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including LDM into cctx params");
-        cctxParams.ldmParams.enableLdm = 1;
-        /* LDM is enabled by default for optimal parser and window size >= 128MB */
+    cctxParams.ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams.ldmParams.enableLdm, &cParams);
+    if (cctxParams.ldmParams.enableLdm == ZSTD_ps_enable) {
         ZSTD_ldm_adjustParameters(&cctxParams.ldmParams, &cParams);
         assert(cctxParams.ldmParams.hashLog >= cctxParams.ldmParams.bucketSizeLog);
         assert(cctxParams.ldmParams.hashRateLog < 32);
     }
-
-    if (ZSTD_CParams_useBlockSplitter(&cParams)) {
-        DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including block splitting into cctx params");
-        cctxParams.splitBlocks = 1;
-    }
-
+    cctxParams.useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams.useBlockSplitter, &cParams);
     cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams);
+    cctxParams.validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams.validateSequences);
+    cctxParams.maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams.maxBlockSize);
+    cctxParams.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams.searchForExternalRepcodes,
+                                                                             cctxParams.compressionLevel);
     assert(!ZSTD_checkCParams(cParams));
     return cctxParams;
 }
@@ -334,10 +372,13 @@
 #define ZSTD_NO_CLEVEL 0
 
 /**
- * Initializes the cctxParams from params and compressionLevel.
+ * Initializes `cctxParams` from `params` and `compressionLevel`.
  * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL.
  */
-static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_parameters const* params, int compressionLevel)
+static void
+ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams,
+                        const ZSTD_parameters* params,
+                              int compressionLevel)
 {
     assert(!ZSTD_checkCParams(params->cParams));
     ZSTD_memset(cctxParams, 0, sizeof(*cctxParams));
@@ -348,7 +389,13 @@
      */
     cctxParams->compressionLevel = compressionLevel;
     cctxParams->useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams->useRowMatchFinder, &params->cParams);
-    DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d", cctxParams->useRowMatchFinder);
+    cctxParams->useBlockSplitter = ZSTD_resolveBlockSplitterMode(cctxParams->useBlockSplitter, &params->cParams);
+    cctxParams->ldmParams.enableLdm = ZSTD_resolveEnableLdm(cctxParams->ldmParams.enableLdm, &params->cParams);
+    cctxParams->validateSequences = ZSTD_resolveExternalSequenceValidation(cctxParams->validateSequences);
+    cctxParams->maxBlockSize = ZSTD_resolveMaxBlockSize(cctxParams->maxBlockSize);
+    cctxParams->searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(cctxParams->searchForExternalRepcodes, compressionLevel);
+    DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d, useBlockSplitter=%d ldm=%d",
+                cctxParams->useRowMatchFinder, cctxParams->useBlockSplitter, cctxParams->ldmParams.enableLdm);
 }
 
 size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)
@@ -361,7 +408,7 @@
 
 /**
  * Sets cctxParams' cParams and fParams from params, but otherwise leaves them alone.
- * @param param Validated zstd parameters.
+ * @param params Validated zstd parameters.
  */
 static void ZSTD_CCtxParams_setZstdParams(
         ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params)
@@ -470,8 +517,8 @@
         return bounds;
 
     case ZSTD_c_enableLongDistanceMatching:
-        bounds.lowerBound = 0;
-        bounds.upperBound = 1;
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
         return bounds;
 
     case ZSTD_c_ldmHashLog:
@@ -518,9 +565,9 @@
         return bounds;
 
     case ZSTD_c_literalCompressionMode:
-        ZSTD_STATIC_ASSERT(ZSTD_lcm_auto < ZSTD_lcm_huffman && ZSTD_lcm_huffman < ZSTD_lcm_uncompressed);
-        bounds.lowerBound = ZSTD_lcm_auto;
-        bounds.upperBound = ZSTD_lcm_uncompressed;
+        ZSTD_STATIC_ASSERT(ZSTD_ps_auto < ZSTD_ps_enable && ZSTD_ps_enable < ZSTD_ps_disable);
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
         return bounds;
 
     case ZSTD_c_targetCBlockSize:
@@ -549,14 +596,14 @@
         bounds.upperBound = 1;
         return bounds;
 
-    case ZSTD_c_splitBlocks:
-        bounds.lowerBound = 0;
-        bounds.upperBound = 1;
+    case ZSTD_c_useBlockSplitter:
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
         return bounds;
 
     case ZSTD_c_useRowMatchFinder:
-        bounds.lowerBound = (int)ZSTD_urm_auto;
-        bounds.upperBound = (int)ZSTD_urm_enableRowMatchFinder;
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
         return bounds;
 
     case ZSTD_c_deterministicRefPrefix:
@@ -564,6 +611,26 @@
         bounds.upperBound = 1;
         return bounds;
 
+    case ZSTD_c_prefetchCDictTables:
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
+        return bounds;
+
+    case ZSTD_c_enableSeqProducerFallback:
+        bounds.lowerBound = 0;
+        bounds.upperBound = 1;
+        return bounds;
+
+    case ZSTD_c_maxBlockSize:
+        bounds.lowerBound = ZSTD_BLOCKSIZE_MAX_MIN;
+        bounds.upperBound = ZSTD_BLOCKSIZE_MAX;
+        return bounds;
+
+    case ZSTD_c_searchForExternalRepcodes:
+        bounds.lowerBound = (int)ZSTD_ps_auto;
+        bounds.upperBound = (int)ZSTD_ps_disable;
+        return bounds;
+
     default:
         bounds.error = ERROR(parameter_unsupported);
         return bounds;
@@ -625,9 +692,13 @@
     case ZSTD_c_stableOutBuffer:
     case ZSTD_c_blockDelimiters:
     case ZSTD_c_validateSequences:
-    case ZSTD_c_splitBlocks:
+    case ZSTD_c_useBlockSplitter:
     case ZSTD_c_useRowMatchFinder:
     case ZSTD_c_deterministicRefPrefix:
+    case ZSTD_c_prefetchCDictTables:
+    case ZSTD_c_enableSeqProducerFallback:
+    case ZSTD_c_maxBlockSize:
+    case ZSTD_c_searchForExternalRepcodes:
     default:
         return 0;
     }
@@ -640,7 +711,7 @@
         if (ZSTD_isUpdateAuthorized(param)) {
             cctx->cParamsChanged = 1;
         } else {
-            RETURN_ERROR(stage_wrong, "can only set params in ctx init stage");
+            RETURN_ERROR(stage_wrong, "can only set params in cctx init stage");
     }   }
 
     switch(param)
@@ -680,9 +751,13 @@
     case ZSTD_c_stableOutBuffer:
     case ZSTD_c_blockDelimiters:
     case ZSTD_c_validateSequences:
-    case ZSTD_c_splitBlocks:
+    case ZSTD_c_useBlockSplitter:
     case ZSTD_c_useRowMatchFinder:
     case ZSTD_c_deterministicRefPrefix:
+    case ZSTD_c_prefetchCDictTables:
+    case ZSTD_c_enableSeqProducerFallback:
+    case ZSTD_c_maxBlockSize:
+    case ZSTD_c_searchForExternalRepcodes:
         break;
 
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
@@ -738,12 +813,12 @@
     case ZSTD_c_minMatch :
         if (value!=0)   /* 0 => use default */
             BOUNDCHECK(ZSTD_c_minMatch, value);
-        CCtxParams->cParams.minMatch = value;
+        CCtxParams->cParams.minMatch = (U32)value;
         return CCtxParams->cParams.minMatch;
 
     case ZSTD_c_targetLength :
         BOUNDCHECK(ZSTD_c_targetLength, value);
-        CCtxParams->cParams.targetLength = value;
+        CCtxParams->cParams.targetLength = (U32)value;
         return CCtxParams->cParams.targetLength;
 
     case ZSTD_c_strategy :
@@ -756,12 +831,12 @@
         /* Content size written in frame header _when known_ (default:1) */
         DEBUGLOG(4, "set content size flag = %u", (value!=0));
         CCtxParams->fParams.contentSizeFlag = value != 0;
-        return CCtxParams->fParams.contentSizeFlag;
+        return (size_t)CCtxParams->fParams.contentSizeFlag;
 
     case ZSTD_c_checksumFlag :
         /* A 32-bits content checksum will be calculated and written at end of frame (default:0) */
         CCtxParams->fParams.checksumFlag = value != 0;
-        return CCtxParams->fParams.checksumFlag;
+        return (size_t)CCtxParams->fParams.checksumFlag;
 
     case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */
         DEBUGLOG(4, "set dictIDFlag = %u", (value!=0));
@@ -770,18 +845,18 @@
 
     case ZSTD_c_forceMaxWindow :
         CCtxParams->forceWindow = (value != 0);
-        return CCtxParams->forceWindow;
+        return (size_t)CCtxParams->forceWindow;
 
     case ZSTD_c_forceAttachDict : {
         const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value;
-        BOUNDCHECK(ZSTD_c_forceAttachDict, pref);
+        BOUNDCHECK(ZSTD_c_forceAttachDict, (int)pref);
         CCtxParams->attachDictPref = pref;
         return CCtxParams->attachDictPref;
     }
 
     case ZSTD_c_literalCompressionMode : {
-        const ZSTD_literalCompressionMode_e lcm = (ZSTD_literalCompressionMode_e)value;
-        BOUNDCHECK(ZSTD_c_literalCompressionMode, lcm);
+        const ZSTD_paramSwitch_e lcm = (ZSTD_paramSwitch_e)value;
+        BOUNDCHECK(ZSTD_c_literalCompressionMode, (int)lcm);
         CCtxParams->literalCompressionMode = lcm;
         return CCtxParams->literalCompressionMode;
     }
@@ -832,47 +907,48 @@
 
     case ZSTD_c_enableDedicatedDictSearch :
         CCtxParams->enableDedicatedDictSearch = (value!=0);
-        return CCtxParams->enableDedicatedDictSearch;
+        return (size_t)CCtxParams->enableDedicatedDictSearch;
 
     case ZSTD_c_enableLongDistanceMatching :
-        CCtxParams->ldmParams.enableLdm = (value!=0);
+        BOUNDCHECK(ZSTD_c_enableLongDistanceMatching, value);
+        CCtxParams->ldmParams.enableLdm = (ZSTD_paramSwitch_e)value;
         return CCtxParams->ldmParams.enableLdm;
 
     case ZSTD_c_ldmHashLog :
         if (value!=0)   /* 0 ==> auto */
             BOUNDCHECK(ZSTD_c_ldmHashLog, value);
-        CCtxParams->ldmParams.hashLog = value;
+        CCtxParams->ldmParams.hashLog = (U32)value;
         return CCtxParams->ldmParams.hashLog;
 
     case ZSTD_c_ldmMinMatch :
         if (value!=0)   /* 0 ==> default */
             BOUNDCHECK(ZSTD_c_ldmMinMatch, value);
-        CCtxParams->ldmParams.minMatchLength = value;
+        CCtxParams->ldmParams.minMatchLength = (U32)value;
         return CCtxParams->ldmParams.minMatchLength;
 
     case ZSTD_c_ldmBucketSizeLog :
         if (value!=0)   /* 0 ==> default */
             BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value);
-        CCtxParams->ldmParams.bucketSizeLog = value;
+        CCtxParams->ldmParams.bucketSizeLog = (U32)value;
         return CCtxParams->ldmParams.bucketSizeLog;
 
     case ZSTD_c_ldmHashRateLog :
-        RETURN_ERROR_IF(value > ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN,
-                        parameter_outOfBound, "Param out of bounds!");
-        CCtxParams->ldmParams.hashRateLog = value;
+        if (value!=0)   /* 0 ==> default */
+            BOUNDCHECK(ZSTD_c_ldmHashRateLog, value);
+        CCtxParams->ldmParams.hashRateLog = (U32)value;
         return CCtxParams->ldmParams.hashRateLog;
 
     case ZSTD_c_targetCBlockSize :
         if (value!=0)   /* 0 ==> default */
             BOUNDCHECK(ZSTD_c_targetCBlockSize, value);
-        CCtxParams->targetCBlockSize = value;
+        CCtxParams->targetCBlockSize = (U32)value;
         return CCtxParams->targetCBlockSize;
 
     case ZSTD_c_srcSizeHint :
         if (value!=0)    /* 0 ==> default */
             BOUNDCHECK(ZSTD_c_srcSizeHint, value);
         CCtxParams->srcSizeHint = value;
-        return CCtxParams->srcSizeHint;
+        return (size_t)CCtxParams->srcSizeHint;
 
     case ZSTD_c_stableInBuffer:
         BOUNDCHECK(ZSTD_c_stableInBuffer, value);
@@ -894,14 +970,14 @@
         CCtxParams->validateSequences = value;
         return CCtxParams->validateSequences;
 
-    case ZSTD_c_splitBlocks:
-        BOUNDCHECK(ZSTD_c_splitBlocks, value);
-        CCtxParams->splitBlocks = value;
-        return CCtxParams->splitBlocks;
+    case ZSTD_c_useBlockSplitter:
+        BOUNDCHECK(ZSTD_c_useBlockSplitter, value);
+        CCtxParams->useBlockSplitter = (ZSTD_paramSwitch_e)value;
+        return CCtxParams->useBlockSplitter;
 
     case ZSTD_c_useRowMatchFinder:
         BOUNDCHECK(ZSTD_c_useRowMatchFinder, value);
-        CCtxParams->useRowMatchFinder = (ZSTD_useRowMatchFinderMode_e)value;
+        CCtxParams->useRowMatchFinder = (ZSTD_paramSwitch_e)value;
         return CCtxParams->useRowMatchFinder;
 
     case ZSTD_c_deterministicRefPrefix:
@@ -909,6 +985,27 @@
         CCtxParams->deterministicRefPrefix = !!value;
         return CCtxParams->deterministicRefPrefix;
 
+    case ZSTD_c_prefetchCDictTables:
+        BOUNDCHECK(ZSTD_c_prefetchCDictTables, value);
+        CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value;
+        return CCtxParams->prefetchCDictTables;
+
+    case ZSTD_c_enableSeqProducerFallback:
+        BOUNDCHECK(ZSTD_c_enableSeqProducerFallback, value);
+        CCtxParams->enableMatchFinderFallback = value;
+        return CCtxParams->enableMatchFinderFallback;
+
+    case ZSTD_c_maxBlockSize:
+        if (value!=0)    /* 0 ==> default */
+            BOUNDCHECK(ZSTD_c_maxBlockSize, value);
+        CCtxParams->maxBlockSize = value;
+        return CCtxParams->maxBlockSize;
+
+    case ZSTD_c_searchForExternalRepcodes:
+        BOUNDCHECK(ZSTD_c_searchForExternalRepcodes, value);
+        CCtxParams->searchForExternalRepcodes = (ZSTD_paramSwitch_e)value;
+        return CCtxParams->searchForExternalRepcodes;
+
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
     }
 }
@@ -1032,8 +1129,8 @@
     case ZSTD_c_validateSequences :
         *value = (int)CCtxParams->validateSequences;
         break;
-    case ZSTD_c_splitBlocks :
-        *value = (int)CCtxParams->splitBlocks;
+    case ZSTD_c_useBlockSplitter :
+        *value = (int)CCtxParams->useBlockSplitter;
         break;
     case ZSTD_c_useRowMatchFinder :
         *value = (int)CCtxParams->useRowMatchFinder;
@@ -1041,6 +1138,18 @@
     case ZSTD_c_deterministicRefPrefix:
         *value = (int)CCtxParams->deterministicRefPrefix;
         break;
+    case ZSTD_c_prefetchCDictTables:
+        *value = (int)CCtxParams->prefetchCDictTables;
+        break;
+    case ZSTD_c_enableSeqProducerFallback:
+        *value = CCtxParams->enableMatchFinderFallback;
+        break;
+    case ZSTD_c_maxBlockSize:
+        *value = (int)CCtxParams->maxBlockSize;
+        break;
+    case ZSTD_c_searchForExternalRepcodes:
+        *value = (int)CCtxParams->searchForExternalRepcodes;
+        break;
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
     }
     return 0;
@@ -1067,9 +1176,47 @@
     return 0;
 }
 
-ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize)
+size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams)
 {
-    DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize);
+    ZSTD_STATIC_ASSERT(sizeof(cparams) == 7 * 4 /* all params are listed below */);
+    DEBUGLOG(4, "ZSTD_CCtx_setCParams");
+    /* only update if all parameters are valid */
+    FORWARD_IF_ERROR(ZSTD_checkCParams(cparams), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, cparams.windowLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_chainLog, cparams.chainLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_hashLog, cparams.hashLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_searchLog, cparams.searchLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, cparams.minMatch), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_targetLength, cparams.targetLength), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_strategy, cparams.strategy), "");
+    return 0;
+}
+
+size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams)
+{
+    ZSTD_STATIC_ASSERT(sizeof(fparams) == 3 * 4 /* all params are listed below */);
+    DEBUGLOG(4, "ZSTD_CCtx_setFParams");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, fparams.contentSizeFlag != 0), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, fparams.checksumFlag != 0), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(cctx, ZSTD_c_dictIDFlag, fparams.noDictIDFlag == 0), "");
+    return 0;
+}
+
+size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params)
+{
+    DEBUGLOG(4, "ZSTD_CCtx_setParams");
+    /* First check cParams, because we want to update all or none. */
+    FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), "");
+    /* Next set fParams, because this could fail if the cctx isn't in init stage. */
+    FORWARD_IF_ERROR(ZSTD_CCtx_setFParams(cctx, params.fParams), "");
+    /* Finally set cParams, which should succeed. */
+    FORWARD_IF_ERROR(ZSTD_CCtx_setCParams(cctx, params.cParams), "");
+    return 0;
+}
+
+size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize)
+{
+    DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %llu bytes", pledgedSrcSize);
     RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
                     "Can't set pledgedSrcSize when not in init stage.");
     cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1;
@@ -1085,9 +1232,9 @@
         ZSTD_compressionParameters* cParams);
 
 /**
- * Initializes the local dict using the requested parameters.
- * NOTE: This does not use the pledged src size, because it may be used for more
- * than one compression.
+ * Initializes the local dictionary using requested parameters.
+ * NOTE: Initialization does not employ the pledged src size,
+ * because the dictionary may be used for multiple compressions.
  */
 static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx)
 {
@@ -1100,8 +1247,8 @@
         return 0;
     }
     if (dl->cdict != NULL) {
-        assert(cctx->cdict == dl->cdict);
         /* Local dictionary already initialized. */
+        assert(cctx->cdict == dl->cdict);
         return 0;
     }
     assert(dl->dictSize > 0);
@@ -1121,40 +1268,44 @@
 }
 
 size_t ZSTD_CCtx_loadDictionary_advanced(
-        ZSTD_CCtx* cctx, const void* dict, size_t dictSize,
-        ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType)
+        ZSTD_CCtx* cctx,
+        const void* dict, size_t dictSize,
+        ZSTD_dictLoadMethod_e dictLoadMethod,
+        ZSTD_dictContentType_e dictContentType)
 {
-    RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
-                    "Can't load a dictionary when ctx is not in init stage.");
     DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize);
-    ZSTD_clearAllDicts(cctx);  /* in case one already exists */
-    if (dict == NULL || dictSize == 0)  /* no dictionary mode */
+    RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
+                    "Can't load a dictionary when cctx is not in init stage.");
+    ZSTD_clearAllDicts(cctx);  /* erase any previously set dictionary */
+    if (dict == NULL || dictSize == 0)  /* no dictionary */
         return 0;
     if (dictLoadMethod == ZSTD_dlm_byRef) {
         cctx->localDict.dict = dict;
     } else {
+        /* copy dictionary content inside CCtx to own its lifetime */
         void* dictBuffer;
         RETURN_ERROR_IF(cctx->staticSize, memory_allocation,
-                        "no malloc for static CCtx");
+                        "static CCtx can't allocate for an internal copy of dictionary");
         dictBuffer = ZSTD_customMalloc(dictSize, cctx->customMem);
-        RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!");
+        RETURN_ERROR_IF(dictBuffer==NULL, memory_allocation,
+                        "allocation failed for dictionary content");
         ZSTD_memcpy(dictBuffer, dict, dictSize);
-        cctx->localDict.dictBuffer = dictBuffer;
-        cctx->localDict.dict = dictBuffer;
+        cctx->localDict.dictBuffer = dictBuffer;  /* owned ptr to free */
+        cctx->localDict.dict = dictBuffer;        /* read-only reference */
     }
     cctx->localDict.dictSize = dictSize;
     cctx->localDict.dictContentType = dictContentType;
     return 0;
 }
 
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(
+size_t ZSTD_CCtx_loadDictionary_byReference(
       ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
 {
     return ZSTD_CCtx_loadDictionary_advanced(
             cctx, dict, dictSize, ZSTD_dlm_byRef, ZSTD_dct_auto);
 }
 
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
+size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize)
 {
     return ZSTD_CCtx_loadDictionary_advanced(
             cctx, dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto);
@@ -1210,8 +1361,9 @@
     if ( (reset == ZSTD_reset_parameters)
       || (reset == ZSTD_reset_session_and_parameters) ) {
         RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
-                        "Can't reset parameters only when not in init stage.");
+                        "Reset parameters is only possible during init stage.");
         ZSTD_clearAllDicts(cctx);
+        ZSTD_memset(&cctx->externalMatchCtx, 0, sizeof(cctx->externalMatchCtx));
         return ZSTD_CCtxParams_reset(&cctx->requestedParams);
     }
     return 0;
@@ -1308,7 +1460,8 @@
 ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,
                             unsigned long long srcSize,
                             size_t dictSize,
-                            ZSTD_cParamMode_e mode)
+                            ZSTD_cParamMode_e mode,
+                            ZSTD_paramSwitch_e useRowMatchFinder)
 {
     const U64 minSrcSize = 513; /* (1<<9) + 1 */
     const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);
@@ -1324,7 +1477,7 @@
         break;
     case ZSTD_cpm_createCDict:
         /* Assume a small source size when creating a dictionary
-         * with an unkown source size.
+         * with an unknown source size.
          */
         if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN)
             srcSize = minSrcSize;
@@ -1342,8 +1495,8 @@
     }
 
     /* resize windowLog if input is small enough, to use less memory */
-    if ( (srcSize < maxWindowResize)
-      && (dictSize < maxWindowResize) )  {
+    if ( (srcSize <= maxWindowResize)
+      && (dictSize <= maxWindowResize) )  {
         U32 const tSize = (U32)(srcSize + dictSize);
         static U32 const hashSizeMin = 1 << ZSTD_HASHLOG_MIN;
         U32 const srcLog = (tSize < hashSizeMin) ? ZSTD_HASHLOG_MIN :
@@ -1361,6 +1514,42 @@
     if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)
         cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN;  /* minimum wlog required for valid frame header */
 
+    /* We can't use more than 32 bits of hash in total, so that means that we require:
+     * (hashLog + 8) <= 32 && (chainLog + 8) <= 32
+     */
+    if (mode == ZSTD_cpm_createCDict && ZSTD_CDictIndicesAreTagged(&cPar)) {
+        U32 const maxShortCacheHashLog = 32 - ZSTD_SHORT_CACHE_TAG_BITS;
+        if (cPar.hashLog > maxShortCacheHashLog) {
+            cPar.hashLog = maxShortCacheHashLog;
+        }
+        if (cPar.chainLog > maxShortCacheHashLog) {
+            cPar.chainLog = maxShortCacheHashLog;
+        }
+    }
+
+
+    /* At this point, we aren't 100% sure if we are using the row match finder.
+     * Unless it is explicitly disabled, conservatively assume that it is enabled.
+     * In this case it will only be disabled for small sources, so shrinking the
+     * hash log a little bit shouldn't result in any ratio loss.
+     */
+    if (useRowMatchFinder == ZSTD_ps_auto)
+        useRowMatchFinder = ZSTD_ps_enable;
+
+    /* We can't hash more than 32-bits in total. So that means that we require:
+     * (hashLog - rowLog + 8) <= 32
+     */
+    if (ZSTD_rowMatchFinderUsed(cPar.strategy, useRowMatchFinder)) {
+        /* Switch to 32-entry rows if searchLog is 5 (or more) */
+        U32 const rowLog = BOUNDED(4, cPar.searchLog, 6);
+        U32 const maxRowHashLog = 32 - ZSTD_ROW_HASH_TAG_BITS;
+        U32 const maxHashLog = maxRowHashLog + rowLog;
+        assert(cPar.hashLog >= rowLog);
+        if (cPar.hashLog > maxHashLog) {
+            cPar.hashLog = maxHashLog;
+        }
+    }
+
     return cPar;
 }
 
@@ -1371,7 +1560,7 @@
 {
     cPar = ZSTD_clampCParams(cPar);   /* resulting cPar is necessarily valid (all parameters within range) */
     if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN;
-    return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown);
+    return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown, ZSTD_ps_auto);
 }
 
 static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode);
@@ -1398,16 +1587,16 @@
       srcSizeHint = CCtxParams->srcSizeHint;
     }
     cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize, mode);
-    if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
+    if (CCtxParams->ldmParams.enableLdm == ZSTD_ps_enable) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
     ZSTD_overrideCParams(&cParams, &CCtxParams->cParams);
     assert(!ZSTD_checkCParams(cParams));
     /* srcSizeHint == 0 means 0 */
-    return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode);
+    return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode, CCtxParams->useRowMatchFinder);
 }
 
 static size_t
 ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,
-                       const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                       const ZSTD_paramSwitch_e useRowMatchFinder,
                        const U32 enableDedicatedDictSearch,
                        const U32 forCCtx)
 {
@@ -1431,7 +1620,7 @@
       + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t))
       + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));
     size_t const lazyAdditionalSpace = ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)
-                                            ? ZSTD_cwksp_aligned_alloc_size(hSize*sizeof(U16))
+                                            ? ZSTD_cwksp_aligned_alloc_size(hSize)
                                             : 0;
     size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt))
                                 ? optPotentialSpace
@@ -1440,26 +1629,34 @@
 
     /* tables are guaranteed to be sized in multiples of 64 bytes (or 16 uint32_t) */
     ZSTD_STATIC_ASSERT(ZSTD_HASHLOG_MIN >= 4 && ZSTD_WINDOWLOG_MIN >= 4 && ZSTD_CHAINLOG_MIN >= 4);
-    assert(useRowMatchFinder != ZSTD_urm_auto);
+    assert(useRowMatchFinder != ZSTD_ps_auto);
 
     DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
                 (U32)chainSize, (U32)hSize, (U32)h3Size);
     return tableSpace + optSpace + slackSpace + lazyAdditionalSpace;
 }
 
+/* Helper function for calculating memory requirements.
+ * Gives a tighter bound than ZSTD_sequenceBound() by taking minMatch into account. */
+static size_t ZSTD_maxNbSeq(size_t blockSize, unsigned minMatch, int useSequenceProducer) {
+    U32 const divider = (minMatch==3 || useSequenceProducer) ? 3 : 4;
+    return blockSize / divider;
+}
+
 static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal(
         const ZSTD_compressionParameters* cParams,
         const ldmParams_t* ldmParams,
         const int isStatic,
-        const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+        const ZSTD_paramSwitch_e useRowMatchFinder,
         const size_t buffInSize,
         const size_t buffOutSize,
-        const U64 pledgedSrcSize)
+        const U64 pledgedSrcSize,
+        int useSequenceProducer,
+        size_t maxBlockSize)
 {
-    size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << cParams->windowLog), pledgedSrcSize));
-    size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
-    U32    const divider = (cParams->minMatch==3) ? 3 : 4;
-    size_t const maxNbSeq = blockSize / divider;
+    size_t const windowSize = (size_t) BOUNDED(1ULL, 1ULL << cParams->windowLog, pledgedSrcSize);
+    size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(maxBlockSize), windowSize);
+    size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, cParams->minMatch, useSequenceProducer);
     size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)
                             + ZSTD_cwksp_aligned_alloc_size(maxNbSeq * sizeof(seqDef))
                             + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));
@@ -1469,7 +1666,7 @@
 
     size_t const ldmSpace = ZSTD_ldm_getTableSize(*ldmParams);
     size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(*ldmParams, blockSize);
-    size_t const ldmSeqSpace = ldmParams->enableLdm ?
+    size_t const ldmSeqSpace = ldmParams->enableLdm == ZSTD_ps_enable ?
         ZSTD_cwksp_aligned_alloc_size(maxNbLdmSeq * sizeof(rawSeq)) : 0;
 
 
@@ -1478,6 +1675,11 @@
 
     size_t const cctxSpace = isStatic ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0;
 
+    size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize);
+    size_t const externalSeqSpace = useSequenceProducer
+        ? ZSTD_cwksp_aligned_alloc_size(maxNbExternalSeq * sizeof(ZSTD_Sequence))
+        : 0;
+
     size_t const neededSpace =
         cctxSpace +
         entropySpace +
@@ -1486,7 +1688,8 @@
         ldmSeqSpace +
         matchStateSize +
         tokenSpace +
-        bufferSpace;
+        bufferSpace +
+        externalSeqSpace;
 
     DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace);
     return neededSpace;
@@ -1496,15 +1699,15 @@
 {
     ZSTD_compressionParameters const cParams =
                 ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
-    ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder,
-                                                                                         &cParams);
+    ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder,
+                                                                               &cParams);
 
     RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only.");
     /* estimateCCtxSize is for one-shot compression. So no buffers should
      * be needed. However, we still allocate two 0-sized buffers, which can
      * take space under ASAN. */
     return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
-        &cParams, &params->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN);
+        &cParams, &params->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize);
 }
 
 size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)
@@ -1514,9 +1717,9 @@
         /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */
         size_t noRowCCtxSize;
         size_t rowCCtxSize;
-        initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder;
+        initialParams.useRowMatchFinder = ZSTD_ps_disable;
         noRowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams);
-        initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder;
+        initialParams.useRowMatchFinder = ZSTD_ps_enable;
         rowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams);
         return MAX(noRowCCtxSize, rowCCtxSize);
     } else {
@@ -1554,18 +1757,18 @@
     RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only.");
     {   ZSTD_compressionParameters const cParams =
                 ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
-        size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
+        size_t const blockSize = MIN(ZSTD_resolveMaxBlockSize(params->maxBlockSize), (size_t)1 << cParams.windowLog);
         size_t const inBuffSize = (params->inBufferMode == ZSTD_bm_buffered)
                 ? ((size_t)1 << cParams.windowLog) + blockSize
                 : 0;
         size_t const outBuffSize = (params->outBufferMode == ZSTD_bm_buffered)
                 ? ZSTD_compressBound(blockSize) + 1
                 : 0;
-        ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, &params->cParams);
+        ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, &params->cParams);
 
         return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
             &cParams, &params->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize,
-            ZSTD_CONTENTSIZE_UNKNOWN);
+            ZSTD_CONTENTSIZE_UNKNOWN, params->useSequenceProducer, params->maxBlockSize);
     }
 }
 
@@ -1576,9 +1779,9 @@
         /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */
         size_t noRowCCtxSize;
         size_t rowCCtxSize;
-        initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder;
+        initialParams.useRowMatchFinder = ZSTD_ps_disable;
         noRowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams);
-        initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder;
+        initialParams.useRowMatchFinder = ZSTD_ps_enable;
         rowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams);
         return MAX(noRowCCtxSize, rowCCtxSize);
     } else {
@@ -1708,12 +1911,25 @@
     ZSTD_resetTarget_CCtx
 } ZSTD_resetTarget_e;
 
+/* Mixes bits in a 64 bits in a value, based on XXH3_rrmxmx */
+static U64 ZSTD_bitmix(U64 val, U64 len) {
+    val ^= ZSTD_rotateRight_U64(val, 49) ^ ZSTD_rotateRight_U64(val, 24);
+    val *= 0x9FB21C651E98DF25ULL;
+    val ^= (val >> 35) + len ;
+    val *= 0x9FB21C651E98DF25ULL;
+    return val ^ (val >> 28);
+}
+
+/* Mixes in the hashSalt and hashSaltEntropy to create a new hashSalt */
+static void ZSTD_advanceHashSalt(ZSTD_matchState_t* ms) {
+    ms->hashSalt = ZSTD_bitmix(ms->hashSalt, 8) ^ ZSTD_bitmix((U64) ms->hashSaltEntropy, 4);
+}
 
 static size_t
 ZSTD_reset_matchState(ZSTD_matchState_t* ms,
                       ZSTD_cwksp* ws,
                 const ZSTD_compressionParameters* cParams,
-                const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                const ZSTD_paramSwitch_e useRowMatchFinder,
                 const ZSTD_compResetPolicy_e crp,
                 const ZSTD_indexResetPolicy_e forceResetIndex,
                 const ZSTD_resetTarget_e forWho)
@@ -1728,13 +1944,14 @@
     size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0;
 
     DEBUGLOG(4, "reset indices : %u", forceResetIndex == ZSTDirp_reset);
-    assert(useRowMatchFinder != ZSTD_urm_auto);
+    assert(useRowMatchFinder != ZSTD_ps_auto);
     if (forceResetIndex == ZSTDirp_reset) {
         ZSTD_window_init(&ms->window);
         ZSTD_cwksp_mark_tables_dirty(ws);
     }
 
     ms->hashLog3 = hashLog3;
+    ms->lazySkipping = 0;
 
     ZSTD_invalidateMatchState(ms);
 
@@ -1756,6 +1973,27 @@
         ZSTD_cwksp_clean_tables(ws);
     }
 
+    if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) {
+        /* Row match finder needs an additional table of hashes ("tags") */
+        size_t const tagTableSize = hSize;
+        /* We want to generate a new salt in case we reset a Cctx, but we always want to use
+         * 0 when we reset a Cdict */
+        if(forWho == ZSTD_resetTarget_CCtx) {
+            ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned_init_once(ws, tagTableSize);
+            ZSTD_advanceHashSalt(ms);
+        } else {
+            /* When we are not salting we want to always memset the memory */
+            ms->tagTable = (BYTE*) ZSTD_cwksp_reserve_aligned(ws, tagTableSize);
+            ZSTD_memset(ms->tagTable, 0, tagTableSize);
+            ms->hashSalt = 0;
+        }
+        {   /* Switch to 32-entry rows if searchLog is 5 (or more) */
+            U32 const rowLog = BOUNDED(4, cParams->searchLog, 6);
+            assert(cParams->hashLog >= rowLog);
+            ms->rowHashLog = cParams->hashLog - rowLog;
+        }
+    }
+
     /* opt parser space */
     if ((forWho == ZSTD_resetTarget_CCtx) && (cParams->strategy >= ZSTD_btopt)) {
         DEBUGLOG(4, "reserving optimal parser space");
@@ -1767,19 +2005,6 @@
         ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));
     }
 
-    if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) {
-        {   /* Row match finder needs an additional table of hashes ("tags") */
-            size_t const tagTableSize = hSize*sizeof(U16);
-            ms->tagTable = (U16*)ZSTD_cwksp_reserve_aligned(ws, tagTableSize);
-            if (ms->tagTable) ZSTD_memset(ms->tagTable, 0, tagTableSize);
-        }
-        {   /* Switch to 32-entry rows if searchLog is 5 (or more) */
-            U32 const rowLog = cParams->searchLog < 5 ? 4 : 5;
-            assert(cParams->hashLog > rowLog);
-            ms->rowHashLog = cParams->hashLog - rowLog;
-        }
-    }
-
     ms->cParams = *cParams;
 
     RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation,
@@ -1824,8 +2049,8 @@
                                       ZSTD_buffered_policy_e const zbuff)
 {
     ZSTD_cwksp* const ws = &zc->workspace;
-    DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u, useRowMatchFinder=%d",
-                (U32)pledgedSrcSize, params->cParams.windowLog, (int)params->useRowMatchFinder);
+    DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u, useRowMatchFinder=%d useBlockSplitter=%d",
+                (U32)pledgedSrcSize, params->cParams.windowLog, (int)params->useRowMatchFinder, (int)params->useBlockSplitter);
     assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams)));
 
     zc->isFirstBlock = 1;
@@ -1836,8 +2061,11 @@
     zc->appliedParams = *params;
     params = &zc->appliedParams;
 
-    assert(params->useRowMatchFinder != ZSTD_urm_auto);
-    if (params->ldmParams.enableLdm) {
+    assert(params->useRowMatchFinder != ZSTD_ps_auto);
+    assert(params->useBlockSplitter != ZSTD_ps_auto);
+    assert(params->ldmParams.enableLdm != ZSTD_ps_auto);
+    assert(params->maxBlockSize != 0);
+    if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
         /* Adjust long distance matching parameters */
         ZSTD_ldm_adjustParameters(&zc->appliedParams.ldmParams, &params->cParams);
         assert(params->ldmParams.hashLog >= params->ldmParams.bucketSizeLog);
@@ -1845,9 +2073,8 @@
     }
 
     {   size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize));
-        size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
-        U32    const divider = (params->cParams.minMatch==3) ? 3 : 4;
-        size_t const maxNbSeq = blockSize / divider;
+        size_t const blockSize = MIN(params->maxBlockSize, windowSize);
+        size_t const maxNbSeq = ZSTD_maxNbSeq(blockSize, params->cParams.minMatch, params->useSequenceProducer);
         size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered)
                 ? ZSTD_compressBound(blockSize) + 1
                 : 0;
@@ -1864,7 +2091,7 @@
         size_t const neededSpace =
             ZSTD_estimateCCtxSize_usingCCtxParams_internal(
                 &params->cParams, &params->ldmParams, zc->staticSize != 0, params->useRowMatchFinder,
-                buffInSize, buffOutSize, pledgedSrcSize);
+                buffInSize, buffOutSize, pledgedSrcSize, params->useSequenceProducer, params->maxBlockSize);
         int resizeWorkspace;
 
         FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!");
@@ -1900,13 +2127,14 @@
                 zc->blockState.nextCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t));
                 RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate nextCBlock");
                 zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, ENTROPY_WORKSPACE_SIZE);
-                RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate entropyWorkspace");
+                RETURN_ERROR_IF(zc->entropyWorkspace == NULL, memory_allocation, "couldn't allocate entropyWorkspace");
         }   }
 
         ZSTD_cwksp_clear(ws);
 
         /* init params */
         zc->blockState.matchState.cParams = params->cParams;
+        zc->blockState.matchState.prefetchCDictTables = params->prefetchCDictTables == ZSTD_ps_enable;
         zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;
         zc->consumedSrcSize = 0;
         zc->producedCSize = 0;
@@ -1923,13 +2151,46 @@
 
         ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);
 
+        FORWARD_IF_ERROR(ZSTD_reset_matchState(
+                &zc->blockState.matchState,
+                ws,
+                &params->cParams,
+                params->useRowMatchFinder,
+                crp,
+                needsIndexReset,
+                ZSTD_resetTarget_CCtx), "");
+
+        zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef));
+
+        /* ldm hash table */
+        if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
+            /* TODO: avoid memset? */
+            size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog;
+            zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t));
+            ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t));
+            zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq));
+            zc->maxNbLdmSequences = maxNbLdmSeq;
+
+            ZSTD_window_init(&zc->ldmState.window);
+            zc->ldmState.loadedDictEnd = 0;
+        }
+
+        /* reserve space for block-level external sequences */
+        if (params->useSequenceProducer) {
+            size_t const maxNbExternalSeq = ZSTD_sequenceBound(blockSize);
+            zc->externalMatchCtx.seqBufferCapacity = maxNbExternalSeq;
+            zc->externalMatchCtx.seqBuffer =
+                (ZSTD_Sequence*)ZSTD_cwksp_reserve_aligned(ws, maxNbExternalSeq * sizeof(ZSTD_Sequence));
+        }
+
+        /* buffers */
+
         /* ZSTD_wildcopy() is used to copy into the literals buffer,
          * so we have to oversize the buffer by WILDCOPY_OVERLENGTH bytes.
          */
         zc->seqStore.litStart = ZSTD_cwksp_reserve_buffer(ws, blockSize + WILDCOPY_OVERLENGTH);
         zc->seqStore.maxNbLit = blockSize;
 
-        /* buffers */
         zc->bufferedPolicy = zbuff;
         zc->inBuffSize = buffInSize;
         zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize);
@@ -1937,7 +2198,7 @@
         zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize);
 
         /* ldm bucketOffsets table */
-        if (params->ldmParams.enableLdm) {
+        if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
             /* TODO: avoid memset? */
             size_t const numBuckets =
                   ((size_t)1) << (params->ldmParams.hashLog -
@@ -1952,32 +2213,9 @@
         zc->seqStore.llCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE));
         zc->seqStore.mlCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE));
         zc->seqStore.ofCode = ZSTD_cwksp_reserve_buffer(ws, maxNbSeq * sizeof(BYTE));
-        zc->seqStore.sequencesStart = (seqDef*)ZSTD_cwksp_reserve_aligned(ws, maxNbSeq * sizeof(seqDef));
 
-        FORWARD_IF_ERROR(ZSTD_reset_matchState(
-            &zc->blockState.matchState,
-            ws,
-            &params->cParams,
-            params->useRowMatchFinder,
-            crp,
-            needsIndexReset,
-            ZSTD_resetTarget_CCtx), "");
-
-        /* ldm hash table */
-        if (params->ldmParams.enableLdm) {
-            /* TODO: avoid memset? */
-            size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog;
-            zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t));
-            ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t));
-            zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq));
-            zc->maxNbLdmSequences = maxNbLdmSeq;
-
-            ZSTD_window_init(&zc->ldmState.window);
-            zc->ldmState.loadedDictEnd = 0;
-        }
-
-        assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace));
         DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws));
+        assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace));
 
         zc->initialized = 1;
 
@@ -2049,7 +2287,8 @@
         }
 
         params.cParams = ZSTD_adjustCParams_internal(adjusted_cdict_cParams, pledgedSrcSize,
-                                                     cdict->dictContentSize, ZSTD_cpm_attachDict);
+                                                     cdict->dictContentSize, ZSTD_cpm_attachDict,
+                                                     params.useRowMatchFinder);
         params.cParams.windowLog = windowLog;
         params.useRowMatchFinder = cdict->useRowMatchFinder;    /* cdict overrides */
         FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, &params, pledgedSrcSize,
@@ -2088,6 +2327,22 @@
     return 0;
 }
 
+static void ZSTD_copyCDictTableIntoCCtx(U32* dst, U32 const* src, size_t tableSize,
+                                        ZSTD_compressionParameters const* cParams) {
+    if (ZSTD_CDictIndicesAreTagged(cParams)){
+        /* Remove tags from the CDict table if they are present.
+         * See docs on "short cache" in zstd_compress_internal.h for context. */
+        size_t i;
+        for (i = 0; i < tableSize; i++) {
+            U32 const taggedIndex = src[i];
+            U32 const index = taggedIndex >> ZSTD_SHORT_CACHE_TAG_BITS;
+            dst[i] = index;
+        }
+    } else {
+        ZSTD_memcpy(dst, src, tableSize * sizeof(U32));
+    }
+}
+
 static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,
                             const ZSTD_CDict* cdict,
                             ZSTD_CCtx_params params,
@@ -2115,7 +2370,7 @@
     }
 
     ZSTD_cwksp_mark_tables_dirty(&cctx->workspace);
-    assert(params.useRowMatchFinder != ZSTD_urm_auto);
+    assert(params.useRowMatchFinder != ZSTD_ps_auto);
 
     /* copy tables */
     {   size_t const chainSize = ZSTD_allocateChainTable(cdict_cParams->strategy, cdict->useRowMatchFinder, 0 /* DDS guaranteed disabled */)
@@ -2123,21 +2378,23 @@
                                                             : 0;
         size_t const hSize =  (size_t)1 << cdict_cParams->hashLog;
 
-        ZSTD_memcpy(cctx->blockState.matchState.hashTable,
-               cdict->matchState.hashTable,
-               hSize * sizeof(U32));
+        ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.hashTable,
+                                cdict->matchState.hashTable,
+                                hSize, cdict_cParams);
+
         /* Do not copy cdict's chainTable if cctx has parameters such that it would not use chainTable */
         if (ZSTD_allocateChainTable(cctx->appliedParams.cParams.strategy, cctx->appliedParams.useRowMatchFinder, 0 /* forDDSDict */)) {
-            ZSTD_memcpy(cctx->blockState.matchState.chainTable,
-               cdict->matchState.chainTable,
-               chainSize * sizeof(U32));
+            ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.chainTable,
+                                    cdict->matchState.chainTable,
+                                    chainSize, cdict_cParams);
         }
         /* copy tag table */
         if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) {
-            size_t const tagTableSize = hSize*sizeof(U16);
+            size_t const tagTableSize = hSize;
             ZSTD_memcpy(cctx->blockState.matchState.tagTable,
-                cdict->matchState.tagTable,
-                tagTableSize);
+                        cdict->matchState.tagTable,
+                        tagTableSize);
+            cctx->blockState.matchState.hashSalt = cdict->matchState.hashSalt;
         }
     }
 
@@ -2209,9 +2466,14 @@
     {   ZSTD_CCtx_params params = dstCCtx->requestedParams;
         /* Copy only compression parameters related to tables. */
         params.cParams = srcCCtx->appliedParams.cParams;
-        assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_urm_auto);
+        assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_ps_auto);
+        assert(srcCCtx->appliedParams.useBlockSplitter != ZSTD_ps_auto);
+        assert(srcCCtx->appliedParams.ldmParams.enableLdm != ZSTD_ps_auto);
         params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder;
+        params.useBlockSplitter = srcCCtx->appliedParams.useBlockSplitter;
+        params.ldmParams = srcCCtx->appliedParams.ldmParams;
         params.fParams = fParams;
+        params.maxBlockSize = srcCCtx->appliedParams.maxBlockSize;
         ZSTD_resetCCtx_internal(dstCCtx, &params, pledgedSrcSize,
                                 /* loadedDictSize */ 0,
                                 ZSTDcrp_leaveDirty, zbuff);
@@ -2296,6 +2558,8 @@
     int const nbRows = (int)size / ZSTD_ROWSIZE;
     int cellNb = 0;
     int rowNb;
+    /* Protect special index values < ZSTD_WINDOW_START_INDEX. */
+    U32 const reducerThreshold = reducerValue + ZSTD_WINDOW_START_INDEX;
     assert((size & (ZSTD_ROWSIZE-1)) == 0);  /* multiple of ZSTD_ROWSIZE */
     assert(size < (1U<<31));   /* can be casted to int */
 
@@ -2315,12 +2579,17 @@
     for (rowNb=0 ; rowNb < nbRows ; rowNb++) {
         int column;
         for (column=0; column<ZSTD_ROWSIZE; column++) {
-            if (preserveMark) {
-                U32 const adder = (table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) ? reducerValue : 0;
-                table[cellNb] += adder;
+            U32 newVal;
+            if (preserveMark && table[cellNb] == ZSTD_DUBT_UNSORTED_MARK) {
+                /* This write is pointless, but is required(?) for the compiler
+                 * to auto-vectorize the loop. */
+                newVal = ZSTD_DUBT_UNSORTED_MARK;
+            } else if (table[cellNb] < reducerThreshold) {
+                newVal = 0;
+            } else {
+                newVal = table[cellNb] - reducerValue;
             }
-            if (table[cellNb] < reducerValue) table[cellNb] = 0;
-            else table[cellNb] -= reducerValue;
+            table[cellNb] = newVal;
             cellNb++;
     }   }
 }
@@ -2364,7 +2633,7 @@
 
 /* See doc/zstd_compression_format.md for detailed format description */
 
-void ZSTD_seqToCodes(const seqStore_t* seqStorePtr)
+int ZSTD_seqToCodes(const seqStore_t* seqStorePtr)
 {
     const seqDef* const sequences = seqStorePtr->sequencesStart;
     BYTE* const llCodeTable = seqStorePtr->llCode;
@@ -2372,18 +2641,24 @@
     BYTE* const mlCodeTable = seqStorePtr->mlCode;
     U32 const nbSeq = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
     U32 u;
+    int longOffsets = 0;
     assert(nbSeq <= seqStorePtr->maxNbSeq);
     for (u=0; u<nbSeq; u++) {
         U32 const llv = sequences[u].litLength;
-        U32 const mlv = sequences[u].matchLength;
+        U32 const ofCode = ZSTD_highbit32(sequences[u].offBase);
+        U32 const mlv = sequences[u].mlBase;
         llCodeTable[u] = (BYTE)ZSTD_LLcode(llv);
-        ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset);
+        ofCodeTable[u] = (BYTE)ofCode;
         mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv);
+        assert(!(MEM_64bits() && ofCode >= STREAM_ACCUMULATOR_MIN));
+        if (MEM_32bits() && ofCode >= STREAM_ACCUMULATOR_MIN)
+            longOffsets = 1;
     }
     if (seqStorePtr->longLengthType==ZSTD_llt_literalLength)
         llCodeTable[seqStorePtr->longLengthPos] = MaxLL;
     if (seqStorePtr->longLengthType==ZSTD_llt_matchLength)
         mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
+    return longOffsets;
 }
 
 /* ZSTD_useTargetCBlockSize():
@@ -2399,11 +2674,13 @@
 /* ZSTD_blockSplitterEnabled():
  * Returns if block splitting param is being used
  * If used, compression will do best effort to split a block in order to improve compression ratio.
+ * At the time this function is called, the parameter must be finalized.
  * Returns 1 if true, 0 otherwise. */
 static int ZSTD_blockSplitterEnabled(ZSTD_CCtx_params* cctxParams)
 {
-    DEBUGLOG(5, "ZSTD_blockSplitterEnabled(splitBlocks=%d)", cctxParams->splitBlocks);
-    return (cctxParams->splitBlocks != 0);
+    DEBUGLOG(5, "ZSTD_blockSplitterEnabled (useBlockSplitter=%d)", cctxParams->useBlockSplitter);
+    assert(cctxParams->useBlockSplitter != ZSTD_ps_auto);
+    return (cctxParams->useBlockSplitter == ZSTD_ps_enable);
 }
 
 /* Type returned by ZSTD_buildSequencesStatistics containing finalized symbol encoding types
@@ -2415,6 +2692,7 @@
     U32 MLtype;
     size_t size;
     size_t lastCountSize; /* Accounts for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */
+    int longOffsets;
 } ZSTD_symbolEncodingTypeStats_t;
 
 /* ZSTD_buildSequencesStatistics():
@@ -2425,11 +2703,13 @@
  * entropyWkspSize must be of size at least ENTROPY_WORKSPACE_SIZE - (MaxSeq + 1)*sizeof(U32)
  */
 static ZSTD_symbolEncodingTypeStats_t
-ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq,
-                        const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy,
-                              BYTE* dst, const BYTE* const dstEnd,
-                              ZSTD_strategy strategy, unsigned* countWorkspace,
-                              void* entropyWorkspace, size_t entropyWkspSize) {
+ZSTD_buildSequencesStatistics(
+                const seqStore_t* seqStorePtr, size_t nbSeq,
+                const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy,
+                      BYTE* dst, const BYTE* const dstEnd,
+                      ZSTD_strategy strategy, unsigned* countWorkspace,
+                      void* entropyWorkspace, size_t entropyWkspSize)
+{
     BYTE* const ostart = dst;
     const BYTE* const oend = dstEnd;
     BYTE* op = ostart;
@@ -2443,7 +2723,7 @@
 
     stats.lastCountSize = 0;
     /* convert length/distances into codes */
-    ZSTD_seqToCodes(seqStorePtr);
+    stats.longOffsets = ZSTD_seqToCodes(seqStorePtr);
     assert(op <= oend);
     assert(nbSeq != 0); /* ZSTD_selectEncodingType() divides by nbSeq */
     /* build CTable for Literal Lengths */
@@ -2546,23 +2826,24 @@
  * compresses both literals and sequences
  * Returns compressed size of block, or a zstd error.
  */
+#define SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO 20
 MEM_STATIC size_t
-ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
-                          const ZSTD_entropyCTables_t* prevEntropy,
-                                ZSTD_entropyCTables_t* nextEntropy,
-                          const ZSTD_CCtx_params* cctxParams,
-                                void* dst, size_t dstCapacity,
-                                void* entropyWorkspace, size_t entropyWkspSize,
-                          const int bmi2)
+ZSTD_entropyCompressSeqStore_internal(
+                        const seqStore_t* seqStorePtr,
+                        const ZSTD_entropyCTables_t* prevEntropy,
+                              ZSTD_entropyCTables_t* nextEntropy,
+                        const ZSTD_CCtx_params* cctxParams,
+                              void* dst, size_t dstCapacity,
+                              void* entropyWorkspace, size_t entropyWkspSize,
+                        const int bmi2)
 {
-    const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
     ZSTD_strategy const strategy = cctxParams->cParams.strategy;
     unsigned* count = (unsigned*)entropyWorkspace;
     FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable;
     FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable;
     FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable;
     const seqDef* const sequences = seqStorePtr->sequencesStart;
-    const size_t nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
+    const size_t nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
     const BYTE* const ofCodeTable = seqStorePtr->ofCode;
     const BYTE* const llCodeTable = seqStorePtr->llCode;
     const BYTE* const mlCodeTable = seqStorePtr->mlCode;
@@ -2570,25 +2851,31 @@
     BYTE* const oend = ostart + dstCapacity;
     BYTE* op = ostart;
     size_t lastCountSize;
+    int longOffsets = 0;
 
     entropyWorkspace = count + (MaxSeq + 1);
     entropyWkspSize -= (MaxSeq + 1) * sizeof(*count);
 
-    DEBUGLOG(4, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq);
+    DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu, dstCapacity=%zu)", nbSeq, dstCapacity);
     ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
     assert(entropyWkspSize >= HUF_WORKSPACE_SIZE);
 
     /* Compress literals */
     {   const BYTE* const literals = seqStorePtr->litStart;
+        size_t const numSequences = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+        size_t const numLiterals = (size_t)(seqStorePtr->lit - seqStorePtr->litStart);
+        /* Base suspicion of uncompressibility on ratio of literals to sequences */
+        unsigned const suspectUncompressible = (numSequences == 0) || (numLiterals / numSequences >= SUSPECT_UNCOMPRESSIBLE_LITERAL_RATIO);
         size_t const litSize = (size_t)(seqStorePtr->lit - literals);
+
         size_t const cSize = ZSTD_compressLiterals(
-                                    &prevEntropy->huf, &nextEntropy->huf,
-                                    cctxParams->cParams.strategy,
-                                    ZSTD_disableLiteralsCompression(cctxParams),
                                     op, dstCapacity,
                                     literals, litSize,
                                     entropyWorkspace, entropyWkspSize,
-                                    bmi2);
+                                    &prevEntropy->huf, &nextEntropy->huf,
+                                    cctxParams->cParams.strategy,
+                                    ZSTD_literalsCompressionIsDisabled(cctxParams),
+                                    suspectUncompressible, bmi2);
         FORWARD_IF_ERROR(cSize, "ZSTD_compressLiterals failed");
         assert(cSize <= dstCapacity);
         op += cSize;
@@ -2614,11 +2901,10 @@
         ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
         return (size_t)(op - ostart);
     }
-    {
-        ZSTD_symbolEncodingTypeStats_t stats;
-        BYTE* seqHead = op++;
+    {   BYTE* const seqHead = op++;
         /* build stats for sequences */
-        stats = ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
+        const ZSTD_symbolEncodingTypeStats_t stats =
+                ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
                                              &prevEntropy->fse, &nextEntropy->fse,
                                               op, oend,
                                               strategy, count,
@@ -2627,6 +2913,7 @@
         *seqHead = (BYTE)((stats.LLtype<<6) + (stats.Offtype<<4) + (stats.MLtype<<2));
         lastCountSize = stats.lastCountSize;
         op += stats.size;
+        longOffsets = stats.longOffsets;
     }
 
     {   size_t const bitstreamSize = ZSTD_encodeSequences(
@@ -2661,14 +2948,15 @@
 }
 
 MEM_STATIC size_t
-ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr,
-                       const ZSTD_entropyCTables_t* prevEntropy,
-                             ZSTD_entropyCTables_t* nextEntropy,
-                       const ZSTD_CCtx_params* cctxParams,
-                             void* dst, size_t dstCapacity,
-                             size_t srcSize,
-                             void* entropyWorkspace, size_t entropyWkspSize,
-                             int bmi2)
+ZSTD_entropyCompressSeqStore(
+                    const seqStore_t* seqStorePtr,
+                    const ZSTD_entropyCTables_t* prevEntropy,
+                          ZSTD_entropyCTables_t* nextEntropy,
+                    const ZSTD_CCtx_params* cctxParams,
+                          void* dst, size_t dstCapacity,
+                          size_t srcSize,
+                          void* entropyWorkspace, size_t entropyWkspSize,
+                          int bmi2)
 {
     size_t const cSize = ZSTD_entropyCompressSeqStore_internal(
                             seqStorePtr, prevEntropy, nextEntropy, cctxParams,
@@ -2678,22 +2966,28 @@
     /* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block.
      * Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block.
      */
-    if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity))
+    if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) {
+        DEBUGLOG(4, "not enough dstCapacity (%zu) for ZSTD_entropyCompressSeqStore_internal()=> do not compress block", dstCapacity);
         return 0;  /* block not compressed */
+    }
     FORWARD_IF_ERROR(cSize, "ZSTD_entropyCompressSeqStore_internal failed");
 
     /* Check compressibility */
     {   size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);
         if (cSize >= maxCSize) return 0;  /* block not compressed */
     }
-    DEBUGLOG(4, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize);
+    DEBUGLOG(5, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize);
+    /* libzstd decoder before  > v1.5.4 is not compatible with compressed blocks of size ZSTD_BLOCKSIZE_MAX exactly.
+     * This restriction is indirectly already fulfilled by respecting ZSTD_minGain() condition above.
+     */
+    assert(cSize < ZSTD_BLOCKSIZE_MAX);
     return cSize;
 }
 
 /* ZSTD_selectBlockCompressor() :
  * Not static, but internal use only (used by long distance matcher)
  * assumption : strat is a valid strategy */
-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e useRowMatchFinder, ZSTD_dictMode_e dictMode)
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_paramSwitch_e useRowMatchFinder, ZSTD_dictMode_e dictMode)
 {
     static const ZSTD_blockCompressor blockCompressor[4][ZSTD_STRATEGY_MAX+1] = {
         { ZSTD_compressBlock_fast  /* default for 0 */,
@@ -2758,7 +3052,7 @@
             ZSTD_compressBlock_lazy2_dedicatedDictSearch_row }
         };
         DEBUGLOG(4, "Selecting a row-based matchfinder");
-        assert(useRowMatchFinder != ZSTD_urm_auto);
+        assert(useRowMatchFinder != ZSTD_ps_auto);
         selectedCompressor = rowBasedBlockCompressors[(int)dictMode][(int)strat - (int)ZSTD_greedy];
     } else {
         selectedCompressor = blockCompressor[(int)dictMode][(int)strat];
@@ -2781,6 +3075,72 @@
     ssPtr->longLengthType = ZSTD_llt_none;
 }
 
+/* ZSTD_postProcessSequenceProducerResult() :
+ * Validates and post-processes sequences obtained through the external matchfinder API:
+ *   - Checks whether nbExternalSeqs represents an error condition.
+ *   - Appends a block delimiter to outSeqs if one is not already present.
+ *     See zstd.h for context regarding block delimiters.
+ * Returns the number of sequences after post-processing, or an error code. */
+static size_t ZSTD_postProcessSequenceProducerResult(
+    ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize
+) {
+    RETURN_ERROR_IF(
+        nbExternalSeqs > outSeqsCapacity,
+        sequenceProducer_failed,
+        "External sequence producer returned error code %lu",
+        (unsigned long)nbExternalSeqs
+    );
+
+    RETURN_ERROR_IF(
+        nbExternalSeqs == 0 && srcSize > 0,
+        sequenceProducer_failed,
+        "Got zero sequences from external sequence producer for a non-empty src buffer!"
+    );
+
+    if (srcSize == 0) {
+        ZSTD_memset(&outSeqs[0], 0, sizeof(ZSTD_Sequence));
+        return 1;
+    }
+
+    {
+        ZSTD_Sequence const lastSeq = outSeqs[nbExternalSeqs - 1];
+
+        /* We can return early if lastSeq is already a block delimiter. */
+        if (lastSeq.offset == 0 && lastSeq.matchLength == 0) {
+            return nbExternalSeqs;
+        }
+
+        /* This error condition is only possible if the external matchfinder
+         * produced an invalid parse, by definition of ZSTD_sequenceBound(). */
+        RETURN_ERROR_IF(
+            nbExternalSeqs == outSeqsCapacity,
+            sequenceProducer_failed,
+            "nbExternalSeqs == outSeqsCapacity but lastSeq is not a block delimiter!"
+        );
+
+        /* lastSeq is not a block delimiter, so we need to append one. */
+        ZSTD_memset(&outSeqs[nbExternalSeqs], 0, sizeof(ZSTD_Sequence));
+        return nbExternalSeqs + 1;
+    }
+}
+
+/* ZSTD_fastSequenceLengthSum() :
+ * Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*.
+ * Similar to another function in zstd_compress.c (determine_blockSize),
+ * except it doesn't check for a block delimiter to end summation.
+ * Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P).
+ * This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */
+static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seqBufSize) {
+    size_t matchLenSum, litLenSum, i;
+    matchLenSum = 0;
+    litLenSum = 0;
+    for (i = 0; i < seqBufSize; i++) {
+        litLenSum += seqBuf[i].litLength;
+        matchLenSum += seqBuf[i].matchLength;
+    }
+    return litLenSum + matchLenSum;
+}
+
 typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e;
 
 static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
@@ -2790,7 +3150,9 @@
     assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
     /* Assert that we have correctly flushed the ctx params into the ms's copy */
     ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
-    if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+    /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+     * additional 1. We need to revisit and change this logic to be more consistent */
+    if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) {
         if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) {
             ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize);
         } else {
@@ -2825,7 +3187,16 @@
                 zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];
         }
         if (zc->externSeqStore.pos < zc->externSeqStore.size) {
-            assert(!zc->appliedParams.ldmParams.enableLdm);
+            assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable);
+
+            /* External matchfinder + LDM is technically possible, just not implemented yet.
+             * We need to revisit soon and implement it. */
+            RETURN_ERROR_IF(
+                zc->appliedParams.useSequenceProducer,
+                parameter_combination_unsupported,
+                "Long-distance matching with external sequence producer enabled is not currently supported."
+            );
+
             /* Updates ldmSeqStore.pos */
             lastLLSize =
                 ZSTD_ldm_blockCompress(&zc->externSeqStore,
@@ -2834,9 +3205,17 @@
                                        zc->appliedParams.useRowMatchFinder,
                                        src, srcSize);
             assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
-        } else if (zc->appliedParams.ldmParams.enableLdm) {
+        } else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
             rawSeqStore_t ldmSeqStore = kNullRawSeqStore;
 
+            /* External matchfinder + LDM is technically possible, just not implemented yet.
+             * We need to revisit soon and implement it. */
+            RETURN_ERROR_IF(
+                zc->appliedParams.useSequenceProducer,
+                parameter_combination_unsupported,
+                "Long-distance matching with external sequence producer enabled is not currently supported."
+            );
+
             ldmSeqStore.seq = zc->ldmSequences;
             ldmSeqStore.capacity = zc->maxNbLdmSequences;
             /* Updates ldmSeqStore.size */
@@ -2851,7 +3230,68 @@
                                        zc->appliedParams.useRowMatchFinder,
                                        src, srcSize);
             assert(ldmSeqStore.pos == ldmSeqStore.size);
-        } else {   /* not long range mode */
+        } else if (zc->appliedParams.useSequenceProducer) {
+            assert(
+                zc->externalMatchCtx.seqBufferCapacity >= ZSTD_sequenceBound(srcSize)
+            );
+            assert(zc->externalMatchCtx.mFinder != NULL);
+
+            {   U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
+
+                size_t const nbExternalSeqs = (zc->externalMatchCtx.mFinder)(
+                    zc->externalMatchCtx.mState,
+                    zc->externalMatchCtx.seqBuffer,
+                    zc->externalMatchCtx.seqBufferCapacity,
+                    src, srcSize,
+                    NULL, 0,  /* dict and dictSize, currently not supported */
+                    zc->appliedParams.compressionLevel,
+                    windowSize
+                );
+
+                size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
+                    zc->externalMatchCtx.seqBuffer,
+                    nbExternalSeqs,
+                    zc->externalMatchCtx.seqBufferCapacity,
+                    srcSize
+                );
+
+                /* Return early if there is no error, since we don't need to worry about last literals */
+                if (!ZSTD_isError(nbPostProcessedSeqs)) {
+                    ZSTD_sequencePosition seqPos = {0,0,0};
+                    size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs);
+                    RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid, "External sequences imply too large a block!");
+                    FORWARD_IF_ERROR(
+                        ZSTD_copySequencesToSeqStoreExplicitBlockDelim(
+                            zc, &seqPos,
+                            zc->externalMatchCtx.seqBuffer, nbPostProcessedSeqs,
+                            src, srcSize,
+                            zc->appliedParams.searchForExternalRepcodes
+                        ),
+                        "Failed to copy external sequences to seqStore!"
+                    );
+                    ms->ldmSeqStore = NULL;
+                    DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.", (unsigned long)nbExternalSeqs);
+                    return ZSTDbss_compress;
+                }
+
+                /* Propagate the error if fallback is disabled */
+                if (!zc->appliedParams.enableMatchFinderFallback) {
+                    return nbPostProcessedSeqs;
+                }
+
+                /* Fallback to software matchfinder */
+                {   ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy,
+                                                                                            zc->appliedParams.useRowMatchFinder,
+                                                                                            dictMode);
+                    ms->ldmSeqStore = NULL;
+                    DEBUGLOG(
+                        5,
+                        "External sequence producer returned error code %lu. Falling back to internal parser.",
+                        (unsigned long)nbExternalSeqs
+                    );
+                    lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
+            }   }
+        } else {   /* not long range mode and no external matchfinder */
             ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy,
                                                                                     zc->appliedParams.useRowMatchFinder,
                                                                                     dictMode);
@@ -2882,9 +3322,9 @@
     assert(zc->seqCollector.maxSequences >= seqStoreSeqSize + 1);
     ZSTD_memcpy(updatedRepcodes.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
     for (i = 0; i < seqStoreSeqSize; ++i) {
-        U32 rawOffset = seqStoreSeqs[i].offset - ZSTD_REP_NUM;
+        U32 rawOffset = seqStoreSeqs[i].offBase - ZSTD_REP_NUM;
         outSeqs[i].litLength = seqStoreSeqs[i].litLength;
-        outSeqs[i].matchLength = seqStoreSeqs[i].matchLength + MINMATCH;
+        outSeqs[i].matchLength = seqStoreSeqs[i].mlBase + MINMATCH;
         outSeqs[i].rep = 0;
 
         if (i == seqStore->longLengthPos) {
@@ -2895,9 +3335,9 @@
             }
         }
 
-        if (seqStoreSeqs[i].offset <= ZSTD_REP_NUM) {
+        if (seqStoreSeqs[i].offBase <= ZSTD_REP_NUM) {
             /* Derive the correct offset corresponding to a repcode */
-            outSeqs[i].rep = seqStoreSeqs[i].offset;
+            outSeqs[i].rep = seqStoreSeqs[i].offBase;
             if (outSeqs[i].litLength != 0) {
                 rawOffset = updatedRepcodes.rep[outSeqs[i].rep - 1];
             } else {
@@ -2911,9 +3351,9 @@
         outSeqs[i].offset = rawOffset;
         /* seqStoreSeqs[i].offset == offCode+1, and ZSTD_updateRep() expects offCode
            so we provide seqStoreSeqs[i].offset - 1 */
-        updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep,
-                                         seqStoreSeqs[i].offset - 1,
-                                         seqStoreSeqs[i].litLength == 0);
+        ZSTD_updateRep(updatedRepcodes.rep,
+                       seqStoreSeqs[i].offBase,
+                       seqStoreSeqs[i].litLength == 0);
         literalsRead += outSeqs[i].litLength;
     }
     /* Insert last literals (if any exist) in the block as a sequence with ml == off == 0.
@@ -2928,6 +3368,10 @@
     zc->seqCollector.seqIndex += seqStoreSeqSize;
 }
 
+size_t ZSTD_sequenceBound(size_t srcSize) {
+    return (srcSize / ZSTD_MINMATCH_MIN) + 1;
+}
+
 size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
                               size_t outSeqsSize, const void* src, size_t srcSize)
 {
@@ -2973,19 +3417,17 @@
     const size_t unrollMask = unrollSize - 1;
     const size_t prefixLength = length & unrollMask;
     size_t i;
-    size_t u;
     if (length == 1) return 1;
     /* Check if prefix is RLE first before using unrolled loop */
     if (prefixLength && ZSTD_count(ip+1, ip, ip+prefixLength) != prefixLength-1) {
         return 0;
     }
     for (i = prefixLength; i != length; i += unrollSize) {
+        size_t u;
         for (u = 0; u < unrollSize; u += sizeof(size_t)) {
             if (MEM_readST(ip + i + u) != valueST) {
                 return 0;
-            }
-        }
-    }
+    }   }   }
     return 1;
 }
 
@@ -3001,7 +3443,8 @@
     return nbSeqs < 4 && nbLits < 10;
 }
 
-static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
+static void
+ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
 {
     ZSTD_compressedBlockState_t* const tmp = bs->prevCBlock;
     bs->prevCBlock = bs->nextCBlock;
@@ -3009,7 +3452,9 @@
 }
 
 /* Writes the block header */
-static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) {
+static void
+writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock)
+{
     U32 const cBlockHeader = cSize == 1 ?
                         lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) :
                         lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);
@@ -3022,13 +3467,16 @@
  *  Stores literals block type (raw, rle, compressed, repeat) and
  *  huffman description table to hufMetadata.
  *  Requires ENTROPY_WORKSPACE_SIZE workspace
- *  @return : size of huffman description table or error code */
-static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize,
-                                            const ZSTD_hufCTables_t* prevHuf,
-                                                  ZSTD_hufCTables_t* nextHuf,
-                                                  ZSTD_hufCTablesMetadata_t* hufMetadata,
-                                                  const int disableLiteralsCompression,
-                                                  void* workspace, size_t wkspSize)
+ * @return : size of huffman description table, or an error code
+ */
+static size_t
+ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize,
+                               const ZSTD_hufCTables_t* prevHuf,
+                                     ZSTD_hufCTables_t* nextHuf,
+                                     ZSTD_hufCTablesMetadata_t* hufMetadata,
+                               const int literalsCompressionIsDisabled,
+                                     void* workspace, size_t wkspSize,
+                                     int hufFlags)
 {
     BYTE* const wkspStart = (BYTE*)workspace;
     BYTE* const wkspEnd = wkspStart + wkspSize;
@@ -3036,16 +3484,16 @@
     unsigned* const countWksp = (unsigned*)workspace;
     const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned);
     BYTE* const nodeWksp = countWkspStart + countWkspSize;
-    const size_t nodeWkspSize = wkspEnd-nodeWksp;
+    const size_t nodeWkspSize = (size_t)(wkspEnd - nodeWksp);
     unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
-    unsigned huffLog = HUF_TABLELOG_DEFAULT;
+    unsigned huffLog = LitHufLog;
     HUF_repeat repeat = prevHuf->repeatMode;
     DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_literals (srcSize=%zu)", srcSize);
 
     /* Prepare nextEntropy assuming reusing the existing table */
     ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
 
-    if (disableLiteralsCompression) {
+    if (literalsCompressionIsDisabled) {
         DEBUGLOG(5, "set_basic - disabled");
         hufMetadata->hType = set_basic;
         return 0;
@@ -3053,73 +3501,77 @@
 
     /* small ? don't even attempt compression (speed opt) */
 #ifndef COMPRESS_LITERALS_SIZE_MIN
-#define COMPRESS_LITERALS_SIZE_MIN 63
+# define COMPRESS_LITERALS_SIZE_MIN 63  /* heuristic */
 #endif
     {   size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
         if (srcSize <= minLitSize) {
             DEBUGLOG(5, "set_basic - too small");
             hufMetadata->hType = set_basic;
             return 0;
-        }
-    }
+    }   }
 
     /* Scan input and build symbol stats */
-    {   size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize);
+    {   size_t const largest =
+            HIST_count_wksp (countWksp, &maxSymbolValue,
+                            (const BYTE*)src, srcSize,
+                            workspace, wkspSize);
         FORWARD_IF_ERROR(largest, "HIST_count_wksp failed");
         if (largest == srcSize) {
+            /* only one literal symbol */
             DEBUGLOG(5, "set_rle");
             hufMetadata->hType = set_rle;
             return 0;
         }
         if (largest <= (srcSize >> 7)+4) {
+            /* heuristic: likely not compressible */
             DEBUGLOG(5, "set_basic - no gain");
             hufMetadata->hType = set_basic;
             return 0;
-        }
-    }
+    }   }
 
     /* Validate the previous Huffman table */
-    if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) {
+    if (repeat == HUF_repeat_check
+      && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) {
         repeat = HUF_repeat_none;
     }
 
     /* Build Huffman Tree */
     ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable));
-    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
+    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue, nodeWksp, nodeWkspSize, nextHuf->CTable, countWksp, hufFlags);
+    assert(huffLog <= LitHufLog);
     {   size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp,
                                                     maxSymbolValue, huffLog,
                                                     nodeWksp, nodeWkspSize);
         FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp");
         huffLog = (U32)maxBits;
-        {   /* Build and write the CTable */
-            size_t const newCSize = HUF_estimateCompressedSize(
-                    (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue);
-            size_t const hSize = HUF_writeCTable_wksp(
-                    hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer),
-                    (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog,
-                    nodeWksp, nodeWkspSize);
-            /* Check against repeating the previous CTable */
-            if (repeat != HUF_repeat_none) {
-                size_t const oldCSize = HUF_estimateCompressedSize(
-                        (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue);
-                if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) {
-                    DEBUGLOG(5, "set_repeat - smaller");
-                    ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-                    hufMetadata->hType = set_repeat;
-                    return 0;
-                }
-            }
-            if (newCSize + hSize >= srcSize) {
-                DEBUGLOG(5, "set_basic - no gains");
+    }
+    {   /* Build and write the CTable */
+        size_t const newCSize = HUF_estimateCompressedSize(
+                (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue);
+        size_t const hSize = HUF_writeCTable_wksp(
+                hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer),
+                (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog,
+                nodeWksp, nodeWkspSize);
+        /* Check against repeating the previous CTable */
+        if (repeat != HUF_repeat_none) {
+            size_t const oldCSize = HUF_estimateCompressedSize(
+                    (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue);
+            if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) {
+                DEBUGLOG(5, "set_repeat - smaller");
                 ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-                hufMetadata->hType = set_basic;
+                hufMetadata->hType = set_repeat;
                 return 0;
-            }
-            DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize);
-            hufMetadata->hType = set_compressed;
-            nextHuf->repeatMode = HUF_repeat_check;
-            return hSize;
+        }   }
+        if (newCSize + hSize >= srcSize) {
+            DEBUGLOG(5, "set_basic - no gains");
+            ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+            hufMetadata->hType = set_basic;
+            return 0;
         }
+        DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize);
+        hufMetadata->hType = set_compressed;
+        nextHuf->repeatMode = HUF_repeat_check;
+        return hSize;
     }
 }
 
@@ -3129,8 +3581,9 @@
  * and updates nextEntropy to the appropriate repeatMode.
  */
 static ZSTD_symbolEncodingTypeStats_t
-ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) {
-    ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0};
+ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy)
+{
+    ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0, 0};
     nextEntropy->litlength_repeatMode = FSE_repeat_none;
     nextEntropy->offcode_repeatMode = FSE_repeat_none;
     nextEntropy->matchlength_repeatMode = FSE_repeat_none;
@@ -3141,16 +3594,18 @@
  *  Builds entropy for the sequences.
  *  Stores symbol compression modes and fse table to fseMetadata.
  *  Requires ENTROPY_WORKSPACE_SIZE wksp.
- *  @return : size of fse tables or error code */
-static size_t ZSTD_buildBlockEntropyStats_sequences(seqStore_t* seqStorePtr,
-                                              const ZSTD_fseCTables_t* prevEntropy,
-                                                    ZSTD_fseCTables_t* nextEntropy,
-                                              const ZSTD_CCtx_params* cctxParams,
-                                                    ZSTD_fseCTablesMetadata_t* fseMetadata,
-                                                    void* workspace, size_t wkspSize)
+ * @return : size of fse tables or error code */
+static size_t
+ZSTD_buildBlockEntropyStats_sequences(
+                const seqStore_t* seqStorePtr,
+                const ZSTD_fseCTables_t* prevEntropy,
+                      ZSTD_fseCTables_t* nextEntropy,
+                const ZSTD_CCtx_params* cctxParams,
+                      ZSTD_fseCTablesMetadata_t* fseMetadata,
+                      void* workspace, size_t wkspSize)
 {
     ZSTD_strategy const strategy = cctxParams->cParams.strategy;
-    size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
+    size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
     BYTE* const ostart = fseMetadata->fseTablesBuffer;
     BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer);
     BYTE* op = ostart;
@@ -3177,23 +3632,28 @@
 /** ZSTD_buildBlockEntropyStats() :
  *  Builds entropy for the block.
  *  Requires workspace size ENTROPY_WORKSPACE_SIZE
- *
- *  @return : 0 on success or error code
+ * @return : 0 on success, or an error code
+ *  Note : also employed in superblock
  */
-size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr,
-                             const ZSTD_entropyCTables_t* prevEntropy,
-                                   ZSTD_entropyCTables_t* nextEntropy,
-                             const ZSTD_CCtx_params* cctxParams,
-                                   ZSTD_entropyCTablesMetadata_t* entropyMetadata,
-                                   void* workspace, size_t wkspSize)
+size_t ZSTD_buildBlockEntropyStats(
+            const seqStore_t* seqStorePtr,
+            const ZSTD_entropyCTables_t* prevEntropy,
+                  ZSTD_entropyCTables_t* nextEntropy,
+            const ZSTD_CCtx_params* cctxParams,
+                  ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                  void* workspace, size_t wkspSize)
 {
-    size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart;
+    size_t const litSize = (size_t)(seqStorePtr->lit - seqStorePtr->litStart);
+    int const huf_useOptDepth = (cctxParams->cParams.strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD);
+    int const hufFlags = huf_useOptDepth ? HUF_flags_optimalDepth : 0;
+
     entropyMetadata->hufMetadata.hufDesSize =
         ZSTD_buildBlockEntropyStats_literals(seqStorePtr->litStart, litSize,
                                             &prevEntropy->huf, &nextEntropy->huf,
                                             &entropyMetadata->hufMetadata,
-                                            ZSTD_disableLiteralsCompression(cctxParams),
-                                            workspace, wkspSize);
+                                            ZSTD_literalsCompressionIsDisabled(cctxParams),
+                                            workspace, wkspSize, hufFlags);
+
     FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed");
     entropyMetadata->fseMetadata.fseTablesSize =
         ZSTD_buildBlockEntropyStats_sequences(seqStorePtr,
@@ -3206,11 +3666,12 @@
 }
 
 /* Returns the size estimate for the literals section (header + content) of a block */
-static size_t ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize,
-                                                const ZSTD_hufCTables_t* huf,
-                                                const ZSTD_hufCTablesMetadata_t* hufMetadata,
-                                                void* workspace, size_t wkspSize,
-                                                int writeEntropy)
+static size_t
+ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize,
+                               const ZSTD_hufCTables_t* huf,
+                               const ZSTD_hufCTablesMetadata_t* hufMetadata,
+                               void* workspace, size_t wkspSize,
+                               int writeEntropy)
 {
     unsigned* const countWksp = (unsigned*)workspace;
     unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
@@ -3232,12 +3693,13 @@
 }
 
 /* Returns the size estimate for the FSE-compressed symbols (of, ml, ll) of a block */
-static size_t ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type,
-                        const BYTE* codeTable, size_t nbSeq, unsigned maxCode,
-                        const FSE_CTable* fseCTable,
-                        const U32* additionalBits,
-                        short const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
-                        void* workspace, size_t wkspSize)
+static size_t
+ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type,
+                    const BYTE* codeTable, size_t nbSeq, unsigned maxCode,
+                    const FSE_CTable* fseCTable,
+                    const U8* additionalBits,
+                    short const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
+                    void* workspace, size_t wkspSize)
 {
     unsigned* const countWksp = (unsigned*)workspace;
     const BYTE* ctp = codeTable;
@@ -3269,98 +3731,107 @@
 }
 
 /* Returns the size estimate for the sequences section (header + content) of a block */
-static size_t ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable,
-                                                  const BYTE* llCodeTable,
-                                                  const BYTE* mlCodeTable,
-                                                  size_t nbSeq,
-                                                  const ZSTD_fseCTables_t* fseTables,
-                                                  const ZSTD_fseCTablesMetadata_t* fseMetadata,
-                                                  void* workspace, size_t wkspSize,
-                                                  int writeEntropy)
+static size_t
+ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable,
+                                 const BYTE* llCodeTable,
+                                 const BYTE* mlCodeTable,
+                                 size_t nbSeq,
+                                 const ZSTD_fseCTables_t* fseTables,
+                                 const ZSTD_fseCTablesMetadata_t* fseMetadata,
+                                 void* workspace, size_t wkspSize,
+                                 int writeEntropy)
 {
     size_t sequencesSectionHeaderSize = 1 /* seqHead */ + 1 /* min seqSize size */ + (nbSeq >= 128) + (nbSeq >= LONGNBSEQ);
     size_t cSeqSizeEstimate = 0;
     cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, nbSeq, MaxOff,
-                                         fseTables->offcodeCTable, NULL,
-                                         OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
-                                         workspace, wkspSize);
+                                    fseTables->offcodeCTable, NULL,
+                                    OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
+                                    workspace, wkspSize);
     cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->llType, llCodeTable, nbSeq, MaxLL,
-                                         fseTables->litlengthCTable, LL_bits,
-                                         LL_defaultNorm, LL_defaultNormLog, MaxLL,
-                                         workspace, wkspSize);
+                                    fseTables->litlengthCTable, LL_bits,
+                                    LL_defaultNorm, LL_defaultNormLog, MaxLL,
+                                    workspace, wkspSize);
     cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, nbSeq, MaxML,
-                                         fseTables->matchlengthCTable, ML_bits,
-                                         ML_defaultNorm, ML_defaultNormLog, MaxML,
-                                         workspace, wkspSize);
+                                    fseTables->matchlengthCTable, ML_bits,
+                                    ML_defaultNorm, ML_defaultNormLog, MaxML,
+                                    workspace, wkspSize);
     if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize;
     return cSeqSizeEstimate + sequencesSectionHeaderSize;
 }
 
 /* Returns the size estimate for a given stream of literals, of, ll, ml */
-static size_t ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize,
-                                     const BYTE* ofCodeTable,
-                                     const BYTE* llCodeTable,
-                                     const BYTE* mlCodeTable,
-                                     size_t nbSeq,
-                                     const ZSTD_entropyCTables_t* entropy,
-                                     const ZSTD_entropyCTablesMetadata_t* entropyMetadata,
-                                     void* workspace, size_t wkspSize,
-                                     int writeLitEntropy, int writeSeqEntropy) {
+static size_t
+ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize,
+                       const BYTE* ofCodeTable,
+                       const BYTE* llCodeTable,
+                       const BYTE* mlCodeTable,
+                       size_t nbSeq,
+                       const ZSTD_entropyCTables_t* entropy,
+                       const ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                       void* workspace, size_t wkspSize,
+                       int writeLitEntropy, int writeSeqEntropy)
+{
     size_t const literalsSize = ZSTD_estimateBlockSize_literal(literals, litSize,
-                                                         &entropy->huf, &entropyMetadata->hufMetadata,
-                                                         workspace, wkspSize, writeLitEntropy);
+                                    &entropy->huf, &entropyMetadata->hufMetadata,
+                                    workspace, wkspSize, writeLitEntropy);
     size_t const seqSize = ZSTD_estimateBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable,
-                                                         nbSeq, &entropy->fse, &entropyMetadata->fseMetadata,
-                                                         workspace, wkspSize, writeSeqEntropy);
+                                    nbSeq, &entropy->fse, &entropyMetadata->fseMetadata,
+                                    workspace, wkspSize, writeSeqEntropy);
     return seqSize + literalsSize + ZSTD_blockHeaderSize;
 }
 
 /* Builds entropy statistics and uses them for blocksize estimation.
  *
- * Returns the estimated compressed size of the seqStore, or a zstd error.
+ * @return: estimated compressed size of the seqStore, or a zstd error.
  */
-static size_t ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, const ZSTD_CCtx* zc) {
-    ZSTD_entropyCTablesMetadata_t entropyMetadata;
+static size_t
+ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, ZSTD_CCtx* zc)
+{
+    ZSTD_entropyCTablesMetadata_t* const entropyMetadata = &zc->blockSplitCtx.entropyMetadata;
+    DEBUGLOG(6, "ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize()");
     FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(seqStore,
                     &zc->blockState.prevCBlock->entropy,
                     &zc->blockState.nextCBlock->entropy,
                     &zc->appliedParams,
-                    &entropyMetadata,
-                    zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), "");
-    return ZSTD_estimateBlockSize(seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart),
+                    entropyMetadata,
+                    zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE), "");
+    return ZSTD_estimateBlockSize(
+                    seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart),
                     seqStore->ofCode, seqStore->llCode, seqStore->mlCode,
                     (size_t)(seqStore->sequences - seqStore->sequencesStart),
-                    &zc->blockState.nextCBlock->entropy, &entropyMetadata, zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE,
-                    (int)(entropyMetadata.hufMetadata.hType == set_compressed), 1);
+                    &zc->blockState.nextCBlock->entropy,
+                    entropyMetadata,
+                    zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE,
+                    (int)(entropyMetadata->hufMetadata.hType == set_compressed), 1);
 }
 
 /* Returns literals bytes represented in a seqStore */
-static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) {
+static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore)
+{
     size_t literalsBytes = 0;
-    size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart;
+    size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart);
     size_t i;
     for (i = 0; i < nbSeqs; ++i) {
-        seqDef seq = seqStore->sequencesStart[i];
+        seqDef const seq = seqStore->sequencesStart[i];
         literalsBytes += seq.litLength;
         if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_literalLength) {
             literalsBytes += 0x10000;
-        }
-    }
+    }   }
     return literalsBytes;
 }
 
 /* Returns match bytes represented in a seqStore */
-static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) {
+static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore)
+{
     size_t matchBytes = 0;
-    size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart;
+    size_t const nbSeqs = (size_t)(seqStore->sequences - seqStore->sequencesStart);
     size_t i;
     for (i = 0; i < nbSeqs; ++i) {
         seqDef seq = seqStore->sequencesStart[i];
-        matchBytes += seq.matchLength + MINMATCH;
+        matchBytes += seq.mlBase + MINMATCH;
         if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_matchLength) {
             matchBytes += 0x10000;
-        }
-    }
+    }   }
     return matchBytes;
 }
 
@@ -3369,15 +3840,12 @@
  */
 static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore,
                                const seqStore_t* originalSeqStore,
-                                     size_t startIdx, size_t endIdx) {
-    BYTE* const litEnd = originalSeqStore->lit;
-    size_t literalsBytes;
-    size_t literalsBytesPreceding = 0;
-
+                                     size_t startIdx, size_t endIdx)
+{
     *resultSeqStore = *originalSeqStore;
     if (startIdx > 0) {
         resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx;
-        literalsBytesPreceding = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+        resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
     }
 
     /* Move longLengthPos into the correct position if necessary */
@@ -3390,13 +3858,12 @@
     }
     resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx;
     resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx;
-    literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
-    resultSeqStore->litStart += literalsBytesPreceding;
     if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) {
         /* This accounts for possible last literals if the derived chunk reaches the end of the block */
-        resultSeqStore->lit = litEnd;
+        assert(resultSeqStore->lit == originalSeqStore->lit);
     } else {
-        resultSeqStore->lit = resultSeqStore->litStart+literalsBytes;
+        size_t const literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+        resultSeqStore->lit = resultSeqStore->litStart + literalsBytes;
     }
     resultSeqStore->llCode += startIdx;
     resultSeqStore->mlCode += startIdx;
@@ -3404,52 +3871,68 @@
 }
 
 /**
- * Returns the raw offset represented by the combination of offCode, ll0, and repcode history.
- * offCode must be an offCode representing a repcode, therefore in the range of [0, 2].
+ * Returns the raw offset represented by the combination of offBase, ll0, and repcode history.
+ * offBase must represent a repcode in the numeric representation of ZSTD_storeSeq().
  */
-static U32 ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, const U32 ll0) {
-    U32 const adjustedOffCode = offCode + ll0;
-    assert(offCode < ZSTD_REP_NUM);
-    if (adjustedOffCode == ZSTD_REP_NUM) {
-        /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 */
-        assert(rep[0] > 0);
+static U32
+ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0)
+{
+    U32 const adjustedRepCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0;  /* [ 0 - 3 ] */
+    assert(OFFBASE_IS_REPCODE(offBase));
+    if (adjustedRepCode == ZSTD_REP_NUM) {
+        assert(ll0);
+        /* litlength == 0 and offCode == 2 implies selection of first repcode - 1
+         * This is only valid if it results in a valid offset value, aka > 0.
+         * Note : it may happen that `rep[0]==1` in exceptional circumstances.
+         * In which case this function will return 0, which is an invalid offset.
+         * It's not an issue though, since this value will be
+         * compared and discarded within ZSTD_seqStore_resolveOffCodes().
+         */
         return rep[0] - 1;
     }
-    return rep[adjustedOffCode];
+    return rep[adjustedRepCode];
 }
 
 /**
  * ZSTD_seqStore_resolveOffCodes() reconciles any possible divergences in offset history that may arise
- * due to emission of RLE/raw blocks that disturb the offset history, and replaces any repcodes within
- * the seqStore that may be invalid.
+ * due to emission of RLE/raw blocks that disturb the offset history,
+ * and replaces any repcodes within the seqStore that may be invalid.
  *
- * dRepcodes are updated as would be on the decompression side. cRepcodes are updated exactly in
- * accordance with the seqStore.
+ * dRepcodes are updated as would be on the decompression side.
+ * cRepcodes are updated exactly in accordance with the seqStore.
+ *
+ * Note : this function assumes seq->offBase respects the following numbering scheme :
+ *        0 : invalid
+ *        1-3 : repcode 1-3
+ *        4+ : real_offset+3
  */
-static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes,
-                                          seqStore_t* const seqStore, U32 const nbSeq) {
+static void
+ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes,
+                        const seqStore_t* const seqStore, U32 const nbSeq)
+{
     U32 idx = 0;
+    U32 const longLitLenIdx = seqStore->longLengthType == ZSTD_llt_literalLength ? seqStore->longLengthPos : nbSeq;
     for (; idx < nbSeq; ++idx) {
         seqDef* const seq = seqStore->sequencesStart + idx;
-        U32 const ll0 = (seq->litLength == 0);
-        U32 offCode = seq->offset - 1;
-        assert(seq->offset > 0);
-        if (offCode <= ZSTD_REP_MOVE) {
-            U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offCode, ll0);
-            U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offCode, ll0);
+        U32 const ll0 = (seq->litLength == 0) && (idx != longLitLenIdx);
+        U32 const offBase = seq->offBase;
+        assert(offBase > 0);
+        if (OFFBASE_IS_REPCODE(offBase)) {
+            U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offBase, ll0);
+            U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offBase, ll0);
             /* Adjust simulated decompression repcode history if we come across a mismatch. Replace
              * the repcode with the offset it actually references, determined by the compression
              * repcode history.
              */
             if (dRawOffset != cRawOffset) {
-                seq->offset = cRawOffset + ZSTD_REP_NUM;
+                seq->offBase = OFFSET_TO_OFFBASE(cRawOffset);
             }
         }
         /* Compression repcode history is always updated with values directly from the unmodified seqStore.
          * Decompression repcode history may use modified seq->offset value taken from compression repcode history.
          */
-        *dRepcodes = ZSTD_updateRep(dRepcodes->rep, seq->offset - 1, ll0);
-        *cRepcodes = ZSTD_updateRep(cRepcodes->rep, offCode, ll0);
+        ZSTD_updateRep(dRepcodes->rep, seq->offBase, ll0);
+        ZSTD_updateRep(cRepcodes->rep, offBase, ll0);
     }
 }
 
@@ -3458,11 +3941,14 @@
  *
  * Returns the total size of that block (including header) or a ZSTD error code.
  */
-static size_t ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, seqStore_t* const seqStore,
-                                                repcodes_t* const dRep, repcodes_t* const cRep,
-                                                void* dst, size_t dstCapacity,
-                                                const void* src, size_t srcSize,
-                                                U32 lastBlock, U32 isPartition) {
+static size_t
+ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc,
+                            const seqStore_t* const seqStore,
+                                  repcodes_t* const dRep, repcodes_t* const cRep,
+                                  void* dst, size_t dstCapacity,
+                            const void* src, size_t srcSize,
+                                  U32 lastBlock, U32 isPartition)
+{
     const U32 rleMaxLength = 25;
     BYTE* op = (BYTE*)dst;
     const BYTE* ip = (const BYTE*)src;
@@ -3471,9 +3957,11 @@
 
     /* In case of an RLE or raw block, the simulated decompression repcode history must be reset */
     repcodes_t const dRepOriginal = *dRep;
+    DEBUGLOG(5, "ZSTD_compressSeqStore_singleBlock");
     if (isPartition)
         ZSTD_seqStore_resolveOffCodes(dRep, cRep, seqStore, (U32)(seqStore->sequences - seqStore->sequencesStart));
 
+    RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "Block header doesn't fit");
     cSeqsSize = ZSTD_entropyCompressSeqStore(seqStore,
                 &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
                 &zc->appliedParams,
@@ -3499,9 +3987,6 @@
         return 0;
     }
 
-    if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
-        zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
-
     if (cSeqsSize == 0) {
         cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock);
         FORWARD_IF_ERROR(cSize, "Nocompress block failed");
@@ -3518,6 +4003,10 @@
         cSize = ZSTD_blockHeaderSize + cSeqsSize;
         DEBUGLOG(4, "Writing out compressed block, size: %zu", cSize);
     }
+
+    if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
+        zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
+
     return cSize;
 }
 
@@ -3528,45 +4017,52 @@
 } seqStoreSplits;
 
 #define MIN_SEQUENCES_BLOCK_SPLITTING 300
-#define MAX_NB_SPLITS 196
 
 /* Helper function to perform the recursive search for block splits.
  * Estimates the cost of seqStore prior to split, and estimates the cost of splitting the sequences in half.
- * If advantageous to split, then we recurse down the two sub-blocks. If not, or if an error occurred in estimation, then
- * we do not recurse.
+ * If advantageous to split, then we recurse down the two sub-blocks.
+ * If not, or if an error occurred in estimation, then we do not recurse.
  *
- * Note: The recursion depth is capped by a heuristic minimum number of sequences, defined by MIN_SEQUENCES_BLOCK_SPLITTING.
+ * Note: The recursion depth is capped by a heuristic minimum number of sequences,
+ * defined by MIN_SEQUENCES_BLOCK_SPLITTING.
  * In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING).
  * In practice, recursion depth usually doesn't go beyond 4.
  *
- * Furthermore, the number of splits is capped by MAX_NB_SPLITS. At MAX_NB_SPLITS == 196 with the current existing blockSize
+ * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS.
+ * At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize
  * maximum of 128 KB, this value is actually impossible to reach.
  */
-static void ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx,
-                                         const ZSTD_CCtx* zc, const seqStore_t* origSeqStore) {
-    seqStore_t fullSeqStoreChunk;
-    seqStore_t firstHalfSeqStore;
-    seqStore_t secondHalfSeqStore;
+static void
+ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx,
+                             ZSTD_CCtx* zc, const seqStore_t* origSeqStore)
+{
+    seqStore_t* const fullSeqStoreChunk = &zc->blockSplitCtx.fullSeqStoreChunk;
+    seqStore_t* const firstHalfSeqStore = &zc->blockSplitCtx.firstHalfSeqStore;
+    seqStore_t* const secondHalfSeqStore = &zc->blockSplitCtx.secondHalfSeqStore;
     size_t estimatedOriginalSize;
     size_t estimatedFirstHalfSize;
     size_t estimatedSecondHalfSize;
     size_t midIdx = (startIdx + endIdx)/2;
 
-    if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= MAX_NB_SPLITS) {
+    DEBUGLOG(5, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx);
+    assert(endIdx >= startIdx);
+    if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= ZSTD_MAX_NB_BLOCK_SPLITS) {
+        DEBUGLOG(6, "ZSTD_deriveBlockSplitsHelper: Too few sequences (%zu)", endIdx - startIdx);
         return;
     }
-    ZSTD_deriveSeqStoreChunk(&fullSeqStoreChunk, origSeqStore, startIdx, endIdx);
-    ZSTD_deriveSeqStoreChunk(&firstHalfSeqStore, origSeqStore, startIdx, midIdx);
-    ZSTD_deriveSeqStoreChunk(&secondHalfSeqStore, origSeqStore, midIdx, endIdx);
-    estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&fullSeqStoreChunk, zc);
-    estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&firstHalfSeqStore, zc);
-    estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&secondHalfSeqStore, zc);
+    ZSTD_deriveSeqStoreChunk(fullSeqStoreChunk, origSeqStore, startIdx, endIdx);
+    ZSTD_deriveSeqStoreChunk(firstHalfSeqStore, origSeqStore, startIdx, midIdx);
+    ZSTD_deriveSeqStoreChunk(secondHalfSeqStore, origSeqStore, midIdx, endIdx);
+    estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(fullSeqStoreChunk, zc);
+    estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(firstHalfSeqStore, zc);
+    estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(secondHalfSeqStore, zc);
     DEBUGLOG(5, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu",
              estimatedOriginalSize, estimatedFirstHalfSize, estimatedSecondHalfSize);
     if (ZSTD_isError(estimatedOriginalSize) || ZSTD_isError(estimatedFirstHalfSize) || ZSTD_isError(estimatedSecondHalfSize)) {
         return;
     }
     if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) {
+        DEBUGLOG(5, "split decided at seqNb:%zu", midIdx);
         ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore);
         splits->splitLocations[splits->idx] = (U32)midIdx;
         splits->idx++;
@@ -3574,14 +4070,18 @@
     }
 }
 
-/* Base recursive function. Populates a table with intra-block partition indices that can improve compression ratio.
+/* Base recursive function.
+ * Populates a table with intra-block partition indices that can improve compression ratio.
  *
- * Returns the number of splits made (which equals the size of the partition table - 1).
+ * @return: number of splits made (which equals the size of the partition table - 1).
  */
-static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) {
-    seqStoreSplits splits = {partitions, 0};
+static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq)
+{
+    seqStoreSplits splits;
+    splits.splitLocations = partitions;
+    splits.idx = 0;
     if (nbSeq <= 4) {
-        DEBUGLOG(4, "ZSTD_deriveBlockSplits: Too few sequences to split");
+        DEBUGLOG(5, "ZSTD_deriveBlockSplits: Too few sequences to split (%u <= 4)", nbSeq);
         /* Refuse to try and split anything with less than 4 sequences */
         return 0;
     }
@@ -3596,17 +4096,21 @@
  *
  * Returns combined size of all blocks (which includes headers), or a ZSTD error code.
  */
-static size_t ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity,
-                                                     const void* src, size_t blockSize, U32 lastBlock, U32 nbSeq) {
+static size_t
+ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc,
+                                    void* dst, size_t dstCapacity,
+                              const void* src, size_t blockSize,
+                                    U32 lastBlock, U32 nbSeq)
+{
     size_t cSize = 0;
     const BYTE* ip = (const BYTE*)src;
     BYTE* op = (BYTE*)dst;
-    U32 partitions[MAX_NB_SPLITS];
     size_t i = 0;
     size_t srcBytesTotal = 0;
-    size_t numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq);
-    seqStore_t nextSeqStore;
-    seqStore_t currSeqStore;
+    U32* const partitions = zc->blockSplitCtx.partitions; /* size == ZSTD_MAX_NB_BLOCK_SPLITS */
+    seqStore_t* const nextSeqStore = &zc->blockSplitCtx.nextSeqStore;
+    seqStore_t* const currSeqStore = &zc->blockSplitCtx.currSeqStore;
+    size_t const numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq);
 
     /* If a block is split and some partitions are emitted as RLE/uncompressed, then repcode history
      * may become invalid. In order to reconcile potentially invalid repcodes, we keep track of two
@@ -3626,77 +4130,81 @@
     repcodes_t cRep;
     ZSTD_memcpy(dRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
     ZSTD_memcpy(cRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
+    ZSTD_memset(nextSeqStore, 0, sizeof(seqStore_t));
 
-    DEBUGLOG(4, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
+    DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
                 (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,
                 (unsigned)zc->blockState.matchState.nextToUpdate);
 
     if (numSplits == 0) {
-        size_t cSizeSingleBlock = ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore,
-                                                                   &dRep, &cRep,
-                                                                    op, dstCapacity,
-                                                                    ip, blockSize,
-                                                                    lastBlock, 0 /* isPartition */);
+        size_t cSizeSingleBlock =
+            ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore,
+                                            &dRep, &cRep,
+                                            op, dstCapacity,
+                                            ip, blockSize,
+                                            lastBlock, 0 /* isPartition */);
         FORWARD_IF_ERROR(cSizeSingleBlock, "Compressing single block from splitBlock_internal() failed!");
         DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal: No splits");
-        assert(cSizeSingleBlock <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize);
+        assert(zc->blockSize <= ZSTD_BLOCKSIZE_MAX);
+        assert(cSizeSingleBlock <= zc->blockSize + ZSTD_blockHeaderSize);
         return cSizeSingleBlock;
     }
 
-    ZSTD_deriveSeqStoreChunk(&currSeqStore, &zc->seqStore, 0, partitions[0]);
+    ZSTD_deriveSeqStoreChunk(currSeqStore, &zc->seqStore, 0, partitions[0]);
     for (i = 0; i <= numSplits; ++i) {
-        size_t srcBytes;
         size_t cSizeChunk;
         U32 const lastPartition = (i == numSplits);
         U32 lastBlockEntireSrc = 0;
 
-        srcBytes = ZSTD_countSeqStoreLiteralsBytes(&currSeqStore) + ZSTD_countSeqStoreMatchBytes(&currSeqStore);
+        size_t srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore);
         srcBytesTotal += srcBytes;
         if (lastPartition) {
             /* This is the final partition, need to account for possible last literals */
             srcBytes += blockSize - srcBytesTotal;
             lastBlockEntireSrc = lastBlock;
         } else {
-            ZSTD_deriveSeqStoreChunk(&nextSeqStore, &zc->seqStore, partitions[i], partitions[i+1]);
+            ZSTD_deriveSeqStoreChunk(nextSeqStore, &zc->seqStore, partitions[i], partitions[i+1]);
         }
 
-        cSizeChunk = ZSTD_compressSeqStore_singleBlock(zc, &currSeqStore,
+        cSizeChunk = ZSTD_compressSeqStore_singleBlock(zc, currSeqStore,
                                                       &dRep, &cRep,
                                                        op, dstCapacity,
                                                        ip, srcBytes,
                                                        lastBlockEntireSrc, 1 /* isPartition */);
-        DEBUGLOG(5, "Estimated size: %zu actual size: %zu", ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&currSeqStore, zc), cSizeChunk);
+        DEBUGLOG(5, "Estimated size: %zu vs %zu : actual size",
+                    ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(currSeqStore, zc), cSizeChunk);
         FORWARD_IF_ERROR(cSizeChunk, "Compressing chunk failed!");
 
         ip += srcBytes;
         op += cSizeChunk;
         dstCapacity -= cSizeChunk;
         cSize += cSizeChunk;
-        currSeqStore = nextSeqStore;
-        assert(cSizeChunk <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize);
+        *currSeqStore = *nextSeqStore;
+        assert(cSizeChunk <= zc->blockSize + ZSTD_blockHeaderSize);
     }
-    /* cRep and dRep may have diverged during the compression. If so, we use the dRep repcodes
-     * for the next block.
+    /* cRep and dRep may have diverged during the compression.
+     * If so, we use the dRep repcodes for the next block.
      */
     ZSTD_memcpy(zc->blockState.prevCBlock->rep, dRep.rep, sizeof(repcodes_t));
     return cSize;
 }
 
-static size_t ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc,
-                                        void* dst, size_t dstCapacity,
-                                        const void* src, size_t srcSize, U32 lastBlock) {
-    const BYTE* ip = (const BYTE*)src;
-    BYTE* op = (BYTE*)dst;
+static size_t
+ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc,
+                              void* dst, size_t dstCapacity,
+                              const void* src, size_t srcSize, U32 lastBlock)
+{
     U32 nbSeq;
     size_t cSize;
     DEBUGLOG(4, "ZSTD_compressBlock_splitBlock");
+    assert(zc->appliedParams.useBlockSplitter == ZSTD_ps_enable);
 
     {   const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
         FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
         if (bss == ZSTDbss_noCompress) {
             if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
                 zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
-            cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock);
+            cSize = ZSTD_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock);
             FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed");
             DEBUGLOG(4, "ZSTD_compressBlock_splitBlock: Nocompress block");
             return cSize;
@@ -3704,19 +4212,19 @@
         nbSeq = (U32)(zc->seqStore.sequences - zc->seqStore.sequencesStart);
     }
 
-    assert(zc->appliedParams.splitBlocks == 1);
     cSize = ZSTD_compressBlock_splitBlock_internal(zc, dst, dstCapacity, src, srcSize, lastBlock, nbSeq);
     FORWARD_IF_ERROR(cSize, "Splitting blocks failed!");
     return cSize;
 }
 
-static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
-                                        void* dst, size_t dstCapacity,
-                                        const void* src, size_t srcSize, U32 frame)
+static size_t
+ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
+                            void* dst, size_t dstCapacity,
+                            const void* src, size_t srcSize, U32 frame)
 {
-    /* This the upper bound for the length of an rle block.
-     * This isn't the actual upper bound. Finding the real threshold
-     * needs further investigation.
+    /* This is an estimated upper bound for the length of an rle block.
+     * This isn't the actual upper bound.
+     * Finding the real threshold needs further investigation.
      */
     const U32 rleMaxLength = 25;
     size_t cSize;
@@ -3746,12 +4254,6 @@
             zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
             zc->bmi2);
 
-    if (zc->seqCollector.collectSequences) {
-        ZSTD_copyBlockSequences(zc);
-        return 0;
-    }
-
-
     if (frame &&
         /* We don't want to emit our first block as a RLE even if it qualifies because
          * doing so will cause the decoder (cli only) to throw a "should consume all input error."
@@ -3814,10 +4316,11 @@
          *   * cSize >= blockBound(srcSize): We have expanded the block too much so
          *     emit an uncompressed block.
          */
-        {
-            size_t const cSize = ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock);
+        {   size_t const cSize =
+                ZSTD_compressSuperBlock(zc, dst, dstCapacity, src, srcSize, lastBlock);
             if (cSize != ERROR(dstSize_tooSmall)) {
-                size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy);
+                size_t const maxCSize =
+                    srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy);
                 FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed");
                 if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) {
                     ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
@@ -3825,7 +4328,7 @@
                 }
             }
         }
-    }
+    } /* if (bss == ZSTDbss_compress)*/
 
     DEBUGLOG(6, "Resorting to ZSTD_noCompressBlock()");
     /* Superblock compression failed, attempt to emit a single no compress block.
@@ -3883,7 +4386,7 @@
 *   All blocks will be terminated, all input will be consumed.
 *   Function will issue an error if there is not enough `dstCapacity` to hold the compressed content.
 *   Frame is supposed already started (header already produced)
-*   @return : compressed size, or an error code
+*  @return : compressed size, or an error code
 */
 static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx,
                                      void* dst, size_t dstCapacity,
@@ -3907,7 +4410,9 @@
         ZSTD_matchState_t* const ms = &cctx->blockState.matchState;
         U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);
 
-        RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE,
+        /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+         * additional 1. We need to revisit and change this logic to be more consistent */
+        RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE + 1,
                         dstSize_tooSmall,
                         "not enough space to store compressed block");
         if (remaining < blockSize) blockSize = remaining;
@@ -3915,6 +4420,7 @@
         ZSTD_overflowCorrectIfNeeded(
             ms, &cctx->workspace, &cctx->appliedParams, ip, ip + blockSize);
         ZSTD_checkDictValidity(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);
+        ZSTD_window_enforceMaxDist(&ms->window, ip, maxDist, &ms->loadedDictEnd, &ms->dictMatchState);
 
         /* Ensure hash/chain table insertion resumes no sooner than lowlimit */
         if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit;
@@ -3945,7 +4451,7 @@
                     MEM_writeLE24(op, cBlockHeader);
                     cSize += ZSTD_blockHeaderSize;
                 }
-            }
+            }  /* if (ZSTD_useTargetCBlockSize(&cctx->appliedParams))*/
 
 
             ip += blockSize;
@@ -3991,7 +4497,9 @@
     if (!singleSegment) op[pos++] = windowLogByte;
     switch(dictIDSizeCode)
     {
-        default:  assert(0); /* impossible */
+        default:
+            assert(0); /* impossible */
+            ZSTD_FALLTHROUGH;
         case 0 : break;
         case 1 : op[pos] = (BYTE)(dictID); pos++; break;
         case 2 : MEM_writeLE16(op+pos, (U16)dictID); pos+=2; break;
@@ -3999,7 +4507,9 @@
     }
     switch(fcsCode)
     {
-        default:  assert(0); /* impossible */
+        default:
+            assert(0); /* impossible */
+            ZSTD_FALLTHROUGH;
         case 0 : if (singleSegment) op[pos++] = (BYTE)(pledgedSrcSize); break;
         case 1 : MEM_writeLE16(op+pos, (U16)(pledgedSrcSize-256)); pos+=2; break;
         case 2 : MEM_writeLE32(op+pos, (U32)(pledgedSrcSize)); pos+=4; break;
@@ -4047,7 +4557,7 @@
 {
     RETURN_ERROR_IF(cctx->stage != ZSTDcs_init, stage_wrong,
                     "wrong cctx stage");
-    RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm,
+    RETURN_ERROR_IF(cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable,
                     parameter_unsupported,
                     "incompatible with ldm");
     cctx->externSeqStore.seq = seq;
@@ -4088,7 +4598,7 @@
         ms->forceNonContiguous = 0;
         ms->nextToUpdate = ms->window.dictLimit;
     }
-    if (cctx->appliedParams.ldmParams.enableLdm) {
+    if (cctx->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
         ZSTD_window_update(&cctx->ldmState.window, src, srcSize, /* forceNonContiguous */ 0);
     }
 
@@ -4120,31 +4630,51 @@
     }
 }
 
-size_t ZSTD_compressContinue (ZSTD_CCtx* cctx,
-                              void* dst, size_t dstCapacity,
-                        const void* src, size_t srcSize)
+size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx,
+                                        void* dst, size_t dstCapacity,
+                                  const void* src, size_t srcSize)
 {
     DEBUGLOG(5, "ZSTD_compressContinue (srcSize=%u)", (unsigned)srcSize);
     return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 1 /* frame mode */, 0 /* last chunk */);
 }
 
+/* NOTE: Must just wrap ZSTD_compressContinue_public() */
+size_t ZSTD_compressContinue(ZSTD_CCtx* cctx,
+                             void* dst, size_t dstCapacity,
+                       const void* src, size_t srcSize)
+{
+    return ZSTD_compressContinue_public(cctx, dst, dstCapacity, src, srcSize);
+}
 
-size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
+static size_t ZSTD_getBlockSize_deprecated(const ZSTD_CCtx* cctx)
 {
     ZSTD_compressionParameters const cParams = cctx->appliedParams.cParams;
     assert(!ZSTD_checkCParams(cParams));
-    return MIN (ZSTD_BLOCKSIZE_MAX, (U32)1 << cParams.windowLog);
+    return MIN(cctx->appliedParams.maxBlockSize, (size_t)1 << cParams.windowLog);
 }
 
-size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+/* NOTE: Must just wrap ZSTD_getBlockSize_deprecated() */
+size_t ZSTD_getBlockSize(const ZSTD_CCtx* cctx)
+{
+    return ZSTD_getBlockSize_deprecated(cctx);
+}
+
+/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */
+size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
     DEBUGLOG(5, "ZSTD_compressBlock: srcSize = %u", (unsigned)srcSize);
-    { size_t const blockSizeMax = ZSTD_getBlockSize(cctx);
+    { size_t const blockSizeMax = ZSTD_getBlockSize_deprecated(cctx);
       RETURN_ERROR_IF(srcSize > blockSizeMax, srcSize_wrong, "input is larger than a block"); }
 
     return ZSTD_compressContinue_internal(cctx, dst, dstCapacity, src, srcSize, 0 /* frame mode */, 0 /* last chunk */);
 }
 
+/* NOTE: Must just wrap ZSTD_compressBlock_deprecated() */
+size_t ZSTD_compressBlock(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_deprecated(cctx, dst, dstCapacity, src, srcSize);
+}
+
 /*! ZSTD_loadDictionaryContent() :
  *  @return : 0, or an error code
  */
@@ -4153,25 +4683,36 @@
                                          ZSTD_cwksp* ws,
                                          ZSTD_CCtx_params const* params,
                                          const void* src, size_t srcSize,
-                                         ZSTD_dictTableLoadMethod_e dtlm)
+                                         ZSTD_dictTableLoadMethod_e dtlm,
+                                         ZSTD_tableFillPurpose_e tfp)
 {
     const BYTE* ip = (const BYTE*) src;
     const BYTE* const iend = ip + srcSize;
-    int const loadLdmDict = params->ldmParams.enableLdm && ls != NULL;
+    int const loadLdmDict = params->ldmParams.enableLdm == ZSTD_ps_enable && ls != NULL;
 
-    /* Assert that we the ms params match the params we're being given */
+    /* Assert that the ms params match the params we're being given */
     ZSTD_assertEqualCParams(params->cParams, ms->cParams);
 
-    if (srcSize > ZSTD_CHUNKSIZE_MAX) {
+    {   /* Ensure large dictionaries can't cause index overflow */
+
         /* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX.
          * Dictionaries right at the edge will immediately trigger overflow
          * correction, but I don't want to insert extra constraints here.
          */
-        U32 const maxDictSize = ZSTD_CURRENT_MAX - 1;
-        /* We must have cleared our windows when our source is this large. */
-        assert(ZSTD_window_isEmpty(ms->window));
-        if (loadLdmDict)
-            assert(ZSTD_window_isEmpty(ls->window));
+        U32 maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX;
+
+        int const CDictTaggedIndices = ZSTD_CDictIndicesAreTagged(&params->cParams);
+        if (CDictTaggedIndices && tfp == ZSTD_tfp_forCDict) {
+            /* Some dictionary matchfinders in zstd use "short cache",
+             * which treats the lower ZSTD_SHORT_CACHE_TAG_BITS of each
+             * CDict hashtable entry as a tag rather than as part of an index.
+             * When short cache is used, we need to truncate the dictionary
+             * so that its indices don't overlap with the tag. */
+            U32 const shortCacheMaxDictSize = (1u << (32 - ZSTD_SHORT_CACHE_TAG_BITS)) - ZSTD_WINDOW_START_INDEX;
+            maxDictSize = MIN(maxDictSize, shortCacheMaxDictSize);
+            assert(!loadLdmDict);
+        }
+
         /* If the dictionary is too large, only load the suffix of the dictionary. */
         if (srcSize > maxDictSize) {
             ip = iend - maxDictSize;
@@ -4180,30 +4721,46 @@
         }
     }
 
-    DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder);
+    if (srcSize > ZSTD_CHUNKSIZE_MAX) {
+        /* We must have cleared our windows when our source is this large. */
+        assert(ZSTD_window_isEmpty(ms->window));
+        if (loadLdmDict) assert(ZSTD_window_isEmpty(ls->window));
+    }
     ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0);
-    ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
-    ms->forceNonContiguous = params->deterministicRefPrefix;
 
-    if (loadLdmDict) {
+    DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder);
+
+    if (loadLdmDict) { /* Load the entire dict into LDM matchfinders. */
         ZSTD_window_update(&ls->window, src, srcSize, /* forceNonContiguous */ 0);
         ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base);
+        ZSTD_ldm_fillHashTable(ls, ip, iend, &params->ldmParams);
     }
 
+    /* If the dict is larger than we can reasonably index in our tables, only load the suffix. */
+    if (params->cParams.strategy < ZSTD_btultra) {
+        U32 maxDictSize = 8U << MIN(MAX(params->cParams.hashLog, params->cParams.chainLog), 28);
+        if (srcSize > maxDictSize) {
+            ip = iend - maxDictSize;
+            /* src = ip; deadcode.DeadStores */
+            srcSize = maxDictSize;
+        }
+    }
+
+    ms->nextToUpdate = (U32)(ip - ms->window.base);
+    ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
+    ms->forceNonContiguous = params->deterministicRefPrefix;
+
     if (srcSize <= HASH_READ_SIZE) return 0;
 
     ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, iend);
 
-    if (loadLdmDict)
-        ZSTD_ldm_fillHashTable(ls, ip, iend, &params->ldmParams);
-
     switch(params->cParams.strategy)
     {
     case ZSTD_fast:
-        ZSTD_fillHashTable(ms, iend, dtlm);
+        ZSTD_fillHashTable(ms, iend, dtlm, tfp);
         break;
     case ZSTD_dfast:
-        ZSTD_fillDoubleHashTable(ms, iend, dtlm);
+        ZSTD_fillDoubleHashTable(ms, iend, dtlm, tfp);
         break;
 
     case ZSTD_greedy:
@@ -4214,9 +4771,9 @@
             assert(ms->chainTable != NULL);
             ZSTD_dedicatedDictSearch_lazy_loadDictionary(ms, iend-HASH_READ_SIZE);
         } else {
-            assert(params->useRowMatchFinder != ZSTD_urm_auto);
-            if (params->useRowMatchFinder == ZSTD_urm_enableRowMatchFinder) {
-                size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog) * sizeof(U16);
+            assert(params->useRowMatchFinder != ZSTD_ps_auto);
+            if (params->useRowMatchFinder == ZSTD_ps_enable) {
+                size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog);
                 ZSTD_memset(ms->tagTable, 0, tagTableSize);
                 ZSTD_row_update(ms, iend-HASH_READ_SIZE);
                 DEBUGLOG(4, "Using row-based hash table for lazy dict");
@@ -4369,6 +4926,7 @@
                                       ZSTD_CCtx_params const* params,
                                       const void* dict, size_t dictSize,
                                       ZSTD_dictTableLoadMethod_e dtlm,
+                                      ZSTD_tableFillPurpose_e tfp,
                                       void* workspace)
 {
     const BYTE* dictPtr = (const BYTE*)dict;
@@ -4387,7 +4945,7 @@
     {
         size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
         FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(
-            ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), "");
+            ms, NULL, ws, params, dictPtr, dictContentSize, dtlm, tfp), "");
     }
     return dictID;
 }
@@ -4403,6 +4961,7 @@
                          const void* dict, size_t dictSize,
                                ZSTD_dictContentType_e dictContentType,
                                ZSTD_dictTableLoadMethod_e dtlm,
+                               ZSTD_tableFillPurpose_e tfp,
                                void* workspace)
 {
     DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);
@@ -4415,13 +4974,13 @@
 
     /* dict restricted modes */
     if (dictContentType == ZSTD_dct_rawContent)
-        return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm);
+        return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm, tfp);
 
     if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) {
         if (dictContentType == ZSTD_dct_auto) {
             DEBUGLOG(4, "raw content dictionary detected");
             return ZSTD_loadDictionaryContent(
-                ms, ls, ws, params, dict, dictSize, dtlm);
+                ms, ls, ws, params, dict, dictSize, dtlm, tfp);
         }
         RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, "");
         assert(0);   /* impossible */
@@ -4429,13 +4988,14 @@
 
     /* dict as full zstd dictionary */
     return ZSTD_loadZstdDictionary(
-        bs, ms, ws, params, dict, dictSize, dtlm, workspace);
+        bs, ms, ws, params, dict, dictSize, dtlm, tfp, workspace);
 }
 
 #define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB)
 #define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6ULL)
 
 /*! ZSTD_compressBegin_internal() :
+ * Assumption : either @dict OR @cdict (or none) is non-NULL, never both
  * @return : 0, or an error code */
 static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
                                     const void* dict, size_t dictSize,
@@ -4471,11 +5031,11 @@
                         cctx->blockState.prevCBlock, &cctx->blockState.matchState,
                         &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent,
                         cdict->dictContentSize, cdict->dictContentType, dtlm,
-                        cctx->entropyWorkspace)
+                        ZSTD_tfp_forCCtx, cctx->entropyWorkspace)
               : ZSTD_compress_insertDictionary(
                         cctx->blockState.prevCBlock, &cctx->blockState.matchState,
                         &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize,
-                        dictContentType, dtlm, cctx->entropyWorkspace);
+                        dictContentType, dtlm, ZSTD_tfp_forCCtx, cctx->entropyWorkspace);
         FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");
         assert(dictID <= UINT_MAX);
         cctx->dictID = (U32)dictID;
@@ -4516,11 +5076,11 @@
                                             &cctxParams, pledgedSrcSize);
 }
 
-size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
+static size_t
+ZSTD_compressBegin_usingDict_deprecated(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
 {
     ZSTD_CCtx_params cctxParams;
-    {
-        ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict);
+    {   ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict);
         ZSTD_CCtxParams_init_internal(&cctxParams, &params, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel);
     }
     DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);
@@ -4528,9 +5088,15 @@
                                        &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);
 }
 
+size_t
+ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
+{
+    return ZSTD_compressBegin_usingDict_deprecated(cctx, dict, dictSize, compressionLevel);
+}
+
 size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel)
 {
-    return ZSTD_compressBegin_usingDict(cctx, NULL, 0, compressionLevel);
+    return ZSTD_compressBegin_usingDict_deprecated(cctx, NULL, 0, compressionLevel);
 }
 
 
@@ -4600,9 +5166,9 @@
 #endif
 }
 
-size_t ZSTD_compressEnd (ZSTD_CCtx* cctx,
-                         void* dst, size_t dstCapacity,
-                   const void* src, size_t srcSize)
+size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx,
+                               void* dst, size_t dstCapacity,
+                         const void* src, size_t srcSize)
 {
     size_t endResult;
     size_t const cSize = ZSTD_compressContinue_internal(cctx,
@@ -4626,6 +5192,14 @@
     return cSize + endResult;
 }
 
+/* NOTE: Must just wrap ZSTD_compressEnd_public() */
+size_t ZSTD_compressEnd(ZSTD_CCtx* cctx,
+                        void* dst, size_t dstCapacity,
+                  const void* src, size_t srcSize)
+{
+    return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize);
+}
+
 size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx,
                                void* dst, size_t dstCapacity,
                          const void* src, size_t srcSize,
@@ -4654,7 +5228,7 @@
     FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,
                          dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,
                          params, srcSize, ZSTDb_not_buffered) , "");
-    return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+    return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize);
 }
 
 size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx,
@@ -4715,7 +5289,7 @@
          + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)
          /* enableDedicatedDictSearch == 1 ensures that CDict estimation will not be too small
           * in case we are using DDS with row-hash. */
-         + ZSTD_sizeof_matchState(&cParams, ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams),
+         + ZSTD_sizeof_matchState(&cParams, ZSTD_resolveRowMatchFinderMode(ZSTD_ps_auto, &cParams),
                                   /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0)
          + (dictLoadMethod == ZSTD_dlm_byRef ? 0
             : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void *))));
@@ -4779,7 +5353,7 @@
         {   size_t const dictID = ZSTD_compress_insertDictionary(
                     &cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace,
                     &params, cdict->dictContent, cdict->dictContentSize,
-                    dictContentType, ZSTD_dtlm_full, cdict->entropyWorkspace);
+                    dictContentType, ZSTD_dtlm_full, ZSTD_tfp_forCDict, cdict->entropyWorkspace);
             FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");
             assert(dictID <= (size_t)(U32)-1);
             cdict->dictID = (U32)dictID;
@@ -4792,7 +5366,7 @@
 static ZSTD_CDict* ZSTD_createCDict_advanced_internal(size_t dictSize,
                                       ZSTD_dictLoadMethod_e dictLoadMethod,
                                       ZSTD_compressionParameters cParams,
-                                      ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                                      ZSTD_paramSwitch_e useRowMatchFinder,
                                       U32 enableDedicatedDictSearch,
                                       ZSTD_customMem customMem)
 {
@@ -4842,7 +5416,7 @@
         &cctxParams, customMem);
 }
 
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2(
+ZSTD_CDict* ZSTD_createCDict_advanced2(
         const void* dict, size_t dictSize,
         ZSTD_dictLoadMethod_e dictLoadMethod,
         ZSTD_dictContentType_e dictContentType,
@@ -4947,7 +5521,7 @@
                                  ZSTD_dictContentType_e dictContentType,
                                  ZSTD_compressionParameters cParams)
 {
-    ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams);
+    ZSTD_paramSwitch_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(ZSTD_ps_auto, &cParams);
     /* enableDedicatedDictSearch == 1 ensures matchstate is not too small in case this CDict will be used for DDS + row hash */
     size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0);
     size_t const neededSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict))
@@ -4976,6 +5550,7 @@
     params.cParams = cParams;
     params.useRowMatchFinder = useRowMatchFinder;
     cdict->useRowMatchFinder = useRowMatchFinder;
+    cdict->compressionLevel = ZSTD_NO_CLEVEL;
 
     if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
                                               dict, dictSize,
@@ -5055,12 +5630,17 @@
 
 /* ZSTD_compressBegin_usingCDict() :
  * cdict must be != NULL */
-size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
+size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
 {
     ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
     return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);
 }
 
+size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
+{
+    return ZSTD_compressBegin_usingCDict_deprecated(cctx, cdict);
+}
+
 /*! ZSTD_compress_usingCDict_internal():
  * Implementation of various ZSTD_compress_usingCDict* functions.
  */
@@ -5070,7 +5650,7 @@
                                 const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)
 {
     FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */
-    return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+    return ZSTD_compressEnd_public(cctx, dst, dstCapacity, src, srcSize);
 }
 
 /*! ZSTD_compress_usingCDict_advanced():
@@ -5267,30 +5847,41 @@
 
 static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)
 {
-    size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;
-    if (hintInSize==0) hintInSize = cctx->blockSize;
-    return hintInSize;
+    if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
+        return cctx->blockSize - cctx->stableIn_notConsumed;
+    }
+    assert(cctx->appliedParams.inBufferMode == ZSTD_bm_buffered);
+    {   size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;
+        if (hintInSize==0) hintInSize = cctx->blockSize;
+        return hintInSize;
+    }
 }
 
 /** ZSTD_compressStream_generic():
  *  internal function for all *compressStream*() variants
- *  non-static, because can be called from zstdmt_compress.c
- * @return : hint size for next input */
+ * @return : hint size for next input to complete ongoing block */
 static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
                                           ZSTD_outBuffer* output,
                                           ZSTD_inBuffer* input,
                                           ZSTD_EndDirective const flushMode)
 {
-    const char* const istart = (const char*)input->src;
-    const char* const iend = input->size != 0 ? istart + input->size : istart;
-    const char* ip = input->pos != 0 ? istart + input->pos : istart;
-    char* const ostart = (char*)output->dst;
-    char* const oend = output->size != 0 ? ostart + output->size : ostart;
-    char* op = output->pos != 0 ? ostart + output->pos : ostart;
+    const char* const istart = (assert(input != NULL), (const char*)input->src);
+    const char* const iend = (istart != NULL) ? istart + input->size : istart;
+    const char* ip = (istart != NULL) ? istart + input->pos : istart;
+    char* const ostart = (assert(output != NULL), (char*)output->dst);
+    char* const oend = (ostart != NULL) ? ostart + output->size : ostart;
+    char* op = (ostart != NULL) ? ostart + output->pos : ostart;
     U32 someMoreWork = 1;
 
     /* check expectations */
-    DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);
+    DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i, srcSize = %zu", (int)flushMode, input->size - input->pos);
+    assert(zcs != NULL);
+    if (zcs->appliedParams.inBufferMode == ZSTD_bm_stable) {
+        assert(input->pos >= zcs->stableIn_notConsumed);
+        input->pos -= zcs->stableIn_notConsumed;
+        ip -= zcs->stableIn_notConsumed;
+        zcs->stableIn_notConsumed = 0;
+    }
     if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
         assert(zcs->inBuff != NULL);
         assert(zcs->inBuffSize > 0);
@@ -5299,8 +5890,10 @@
         assert(zcs->outBuff !=  NULL);
         assert(zcs->outBuffSize > 0);
     }
-    assert(output->pos <= output->size);
+    if (input->src == NULL) assert(input->size == 0);
     assert(input->pos <= input->size);
+    if (output->dst == NULL) assert(output->size == 0);
+    assert(output->pos <= output->size);
     assert((U32)flushMode <= (U32)ZSTD_e_end);
 
     while (someMoreWork) {
@@ -5315,7 +5908,7 @@
                 || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)  /* OR we are allowed to return dstSizeTooSmall */
               && (zcs->inBuffPos == 0) ) {
                 /* shortcut to compression pass directly into output buffer */
-                size_t const cSize = ZSTD_compressEnd(zcs,
+                size_t const cSize = ZSTD_compressEnd_public(zcs,
                                                 op, oend-op, ip, iend-ip);
                 DEBUGLOG(4, "ZSTD_compressEnd : cSize=%u", (unsigned)cSize);
                 FORWARD_IF_ERROR(cSize, "ZSTD_compressEnd failed");
@@ -5332,8 +5925,7 @@
                                         zcs->inBuff + zcs->inBuffPos, toLoad,
                                         ip, iend-ip);
                 zcs->inBuffPos += loaded;
-                if (loaded != 0)
-                    ip += loaded;
+                if (ip) ip += loaded;
                 if ( (flushMode == ZSTD_e_continue)
                   && (zcs->inBuffPos < zcs->inBuffTarget) ) {
                     /* not enough input to fill full block : stop here */
@@ -5344,6 +5936,20 @@
                     /* empty */
                     someMoreWork = 0; break;
                 }
+            } else {
+                assert(zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+                if ( (flushMode == ZSTD_e_continue)
+                  && ( (size_t)(iend - ip) < zcs->blockSize) ) {
+                    /* can't compress a full block : stop here */
+                    zcs->stableIn_notConsumed = (size_t)(iend - ip);
+                    ip = iend;  /* pretend to have consumed input */
+                    someMoreWork = 0; break;
+                }
+                if ( (flushMode == ZSTD_e_flush)
+                  && (ip == iend) ) {
+                    /* empty */
+                    someMoreWork = 0; break;
+                }
             }
             /* compress current block (note : this stage cannot be stopped in the middle) */
             DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);
@@ -5351,9 +5957,8 @@
                 void* cDst;
                 size_t cSize;
                 size_t oSize = oend-op;
-                size_t const iSize = inputBuffered
-                    ? zcs->inBuffPos - zcs->inToCompress
-                    : MIN((size_t)(iend - ip), zcs->blockSize);
+                size_t const iSize = inputBuffered ? zcs->inBuffPos - zcs->inToCompress
+                                                   : MIN((size_t)(iend - ip), zcs->blockSize);
                 if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)
                     cDst = op;   /* compress into output buffer, to skip flush stage */
                 else
@@ -5361,9 +5966,9 @@
                 if (inputBuffered) {
                     unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);
                     cSize = lastBlock ?
-                            ZSTD_compressEnd(zcs, cDst, oSize,
+                            ZSTD_compressEnd_public(zcs, cDst, oSize,
                                         zcs->inBuff + zcs->inToCompress, iSize) :
-                            ZSTD_compressContinue(zcs, cDst, oSize,
+                            ZSTD_compressContinue_public(zcs, cDst, oSize,
                                         zcs->inBuff + zcs->inToCompress, iSize);
                     FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
                     zcs->frameEnded = lastBlock;
@@ -5376,19 +5981,16 @@
                     if (!lastBlock)
                         assert(zcs->inBuffTarget <= zcs->inBuffSize);
                     zcs->inToCompress = zcs->inBuffPos;
-                } else {
-                    unsigned const lastBlock = (ip + iSize == iend);
-                    assert(flushMode == ZSTD_e_end /* Already validated */);
+                } else { /* !inputBuffered, hence ZSTD_bm_stable */
+                    unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend);
                     cSize = lastBlock ?
-                            ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) :
-                            ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize);
+                            ZSTD_compressEnd_public(zcs, cDst, oSize, ip, iSize) :
+                            ZSTD_compressContinue_public(zcs, cDst, oSize, ip, iSize);
                     /* Consume the input prior to error checking to mirror buffered mode. */
-                    if (iSize > 0)
-                        ip += iSize;
+                    if (ip) ip += iSize;
                     FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
                     zcs->frameEnded = lastBlock;
-                    if (lastBlock)
-                        assert(ip == iend);
+                    if (lastBlock) assert(ip == iend);
                 }
                 if (cDst == op) {  /* no need to flush */
                     op += cSize;
@@ -5403,7 +6005,7 @@
                 zcs->outBuffFlushedSize = 0;
                 zcs->streamStage = zcss_flush; /* pass-through to flush stage */
             }
-	    /* fall-through */
+	    ZSTD_FALLTHROUGH;
         case zcss_flush:
             DEBUGLOG(5, "flush stage");
             assert(zcs->appliedParams.outBufferMode == ZSTD_bm_buffered);
@@ -5464,8 +6066,10 @@
 /* After a compression call set the expected input/output buffer.
  * This is validated at the start of the next compression call.
  */
-static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, ZSTD_outBuffer const* output, ZSTD_inBuffer const* input)
+static void
+ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input)
 {
+    DEBUGLOG(5, "ZSTD_setBufferExpectations (for advanced stable in/out modes)");
     if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
         cctx->expectedInBuffer = *input;
     }
@@ -5484,22 +6088,22 @@
 {
     if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
         ZSTD_inBuffer const expect = cctx->expectedInBuffer;
-        if (expect.src != input->src || expect.pos != input->pos || expect.size != input->size)
-            RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!");
-        if (endOp != ZSTD_e_end)
-            RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer can only be used with ZSTD_e_end!");
+        if (expect.src != input->src || expect.pos != input->pos)
+            RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableInBuffer enabled but input differs!");
     }
+    (void)endOp;
     if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
         size_t const outBufferSize = output->size - output->pos;
         if (cctx->expectedOutBufferSize != outBufferSize)
-            RETURN_ERROR(dstBuffer_wrong, "ZSTD_c_stableOutBuffer enabled but output size differs!");
+            RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableOutBuffer enabled but output size differs!");
     }
     return 0;
 }
 
 static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
                                              ZSTD_EndDirective endOp,
-                                             size_t inSize) {
+                                             size_t inSize)
+{
     ZSTD_CCtx_params params = cctx->requestedParams;
     ZSTD_prefixDict const prefixDict = cctx->prefixDict;
     FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */
@@ -5513,9 +6117,9 @@
         params.compressionLevel = cctx->cdict->compressionLevel;
     }
     DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");
-    if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1;  /* auto-fix pledgedSrcSize */
-    {
-        size_t const dictSize = prefixDict.dict
+    if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1;  /* auto-determine pledgedSrcSize */
+
+    {   size_t const dictSize = prefixDict.dict
                 ? prefixDict.dictSize
                 : (cctx->cdict ? cctx->cdict->dictContentSize : 0);
         ZSTD_cParamMode_e const mode = ZSTD_getCParamMode(cctx->cdict, &params, cctx->pledgedSrcSizePlusOne - 1);
@@ -5524,20 +6128,21 @@
                 dictSize, mode);
     }
 
-    if (ZSTD_CParams_shouldEnableLdm(&params.cParams)) {
-        /* Enable LDM by default for optimal parser and window size >= 128MB */
-        DEBUGLOG(4, "LDM enabled by default (window size >= 128MB, strategy >= btopt)");
-        params.ldmParams.enableLdm = 1;
-    }
-
-    if (ZSTD_CParams_useBlockSplitter(&params.cParams)) {
-        DEBUGLOG(4, "Block splitter enabled by default (window size >= 128K, strategy >= btopt)");
-        params.splitBlocks = 1;
-    }
-
+    params.useBlockSplitter = ZSTD_resolveBlockSplitterMode(params.useBlockSplitter, &params.cParams);
+    params.ldmParams.enableLdm = ZSTD_resolveEnableLdm(params.ldmParams.enableLdm, &params.cParams);
     params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params.useRowMatchFinder, &params.cParams);
+    params.validateSequences = ZSTD_resolveExternalSequenceValidation(params.validateSequences);
+    params.maxBlockSize = ZSTD_resolveMaxBlockSize(params.maxBlockSize);
+    params.searchForExternalRepcodes = ZSTD_resolveExternalRepcodeSearch(params.searchForExternalRepcodes, params.compressionLevel);
 
 #ifdef ZSTD_MULTITHREAD
+    /* If external matchfinder is enabled, make sure to fail before checking job size (for consistency) */
+    RETURN_ERROR_IF(
+        params.useSequenceProducer == 1 && params.nbWorkers >= 1,
+        parameter_combination_unsupported,
+        "External sequence producer isn't supported with nbWorkers >= 1"
+    );
+
     if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
         params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */
     }
@@ -5565,7 +6170,7 @@
         cctx->streamStage = zcss_load;
         cctx->appliedParams = params;
     } else
-#endif
+#endif  /* ZSTD_MULTITHREAD */
     {   U64 const pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1;
         assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
         FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,
@@ -5591,6 +6196,8 @@
     return 0;
 }
 
+/* @return provides a minimum amount of data remaining to be flushed from internal buffers
+ */
 size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
                              ZSTD_outBuffer* output,
                              ZSTD_inBuffer* input,
@@ -5605,8 +6212,27 @@
 
     /* transparent initialization stage */
     if (cctx->streamStage == zcss_init) {
-        FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed");
-        ZSTD_setBufferExpectations(cctx, output, input);    /* Set initial buffer expectations now that we've initialized */
+        size_t const inputSize = input->size - input->pos;  /* no obligation to start from pos==0 */
+        size_t const totalInputSize = inputSize + cctx->stableIn_notConsumed;
+        if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */
+          && (endOp == ZSTD_e_continue)                             /* no flush requested, more input to come */
+          && (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) {              /* not even reached one block yet */
+            if (cctx->stableIn_notConsumed) {  /* not the first time */
+                /* check stable source guarantees */
+                RETURN_ERROR_IF(input->src != cctx->expectedInBuffer.src, stabilityCondition_notRespected, "stableInBuffer condition not respected: wrong src pointer");
+                RETURN_ERROR_IF(input->pos != cctx->expectedInBuffer.size, stabilityCondition_notRespected, "stableInBuffer condition not respected: externally modified pos");
+            }
+            /* pretend input was consumed, to give a sense forward progress */
+            input->pos = input->size;
+            /* save stable inBuffer, for later control, and flush/end */
+            cctx->expectedInBuffer = *input;
+            /* but actually input wasn't consumed, so keep track of position from where compression shall resume */
+            cctx->stableIn_notConsumed += inputSize;
+            /* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */
+            return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format);  /* at least some header to produce */
+        }
+        FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize), "compressStream2 initialization failed");
+        ZSTD_setBufferExpectations(cctx, output, input);   /* Set initial buffer expectations now that we've initialized */
     }
     /* end of transparent initialization stage */
 
@@ -5619,6 +6245,13 @@
             ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
             cctx->cParamsChanged = 0;
         }
+        if (cctx->stableIn_notConsumed) {
+            assert(cctx->appliedParams.inBufferMode == ZSTD_bm_stable);
+            /* some early data was skipped - make it available for consumption */
+            assert(input->pos >= cctx->stableIn_notConsumed);
+            input->pos -= cctx->stableIn_notConsumed;
+            cctx->stableIn_notConsumed = 0;
+        }
         for (;;) {
             size_t const ipos = input->pos;
             size_t const opos = output->pos;
@@ -5657,7 +6290,7 @@
         ZSTD_setBufferExpectations(cctx, output, input);
         return flushMin;
     }
-#endif
+#endif /* ZSTD_MULTITHREAD */
     FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , "");
     DEBUGLOG(5, "completed ZSTD_compressStream2");
     ZSTD_setBufferExpectations(cctx, output, input);
@@ -5670,13 +6303,20 @@
                       const void* src, size_t srcSize, size_t* srcPos,
                             ZSTD_EndDirective endOp)
 {
-    ZSTD_outBuffer output = { dst, dstCapacity, *dstPos };
-    ZSTD_inBuffer  input  = { src, srcSize, *srcPos };
+    ZSTD_outBuffer output;
+    ZSTD_inBuffer  input;
+    output.dst = dst;
+    output.size = dstCapacity;
+    output.pos = *dstPos;
+    input.src = src;
+    input.size = srcSize;
+    input.pos = *srcPos;
     /* ZSTD_compressStream2() will check validity of dstPos and srcPos */
-    size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp);
-    *dstPos = output.pos;
-    *srcPos = input.pos;
-    return cErr;
+    {   size_t const cErr = ZSTD_compressStream2(cctx, &output, &input, endOp);
+        *dstPos = output.pos;
+        *srcPos = input.pos;
+        return cErr;
+    }
 }
 
 size_t ZSTD_compress2(ZSTD_CCtx* cctx,
@@ -5699,6 +6339,7 @@
         /* Reset to the original values. */
         cctx->requestedParams.inBufferMode = originalInBufferMode;
         cctx->requestedParams.outBufferMode = originalOutBufferMode;
+
         FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed");
         if (result != 0) {  /* compression not completed, due to lack of output space */
             assert(oPos == dstCapacity);
@@ -5709,64 +6350,60 @@
     }
 }
 
-typedef struct {
-    U32 idx;             /* Index in array of ZSTD_Sequence */
-    U32 posInSequence;   /* Position within sequence at idx */
-    size_t posInSrc;        /* Number of bytes given by sequences provided so far */
-} ZSTD_sequencePosition;
-
-/* Returns a ZSTD error code if sequence is not valid */
-static size_t ZSTD_validateSequence(U32 offCode, U32 matchLength,
-                                    size_t posInSrc, U32 windowLog, size_t dictSize, U32 minMatch) {
-    size_t offsetBound;
-    U32 windowSize = 1 << windowLog;
-    /* posInSrc represents the amount of data the the decoder would decode up to this point.
+/* ZSTD_validateSequence() :
+ * @offCode : is presumed to follow format required by ZSTD_storeSeq()
+ * @returns a ZSTD error code if sequence is not valid
+ */
+static size_t
+ZSTD_validateSequence(U32 offCode, U32 matchLength, U32 minMatch,
+                      size_t posInSrc, U32 windowLog, size_t dictSize, int useSequenceProducer)
+{
+    U32 const windowSize = 1u << windowLog;
+    /* posInSrc represents the amount of data the decoder would decode up to this point.
      * As long as the amount of data decoded is less than or equal to window size, offsets may be
      * larger than the total length of output decoded in order to reference the dict, even larger than
      * window size. After output surpasses windowSize, we're limited to windowSize offsets again.
      */
-    offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize;
-    RETURN_ERROR_IF(offCode > offsetBound + ZSTD_REP_MOVE, corruption_detected, "Offset too large!");
-    RETURN_ERROR_IF(matchLength < minMatch, corruption_detected, "Matchlength too small");
+    size_t const offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize;
+    size_t const matchLenLowerBound = (minMatch == 3 || useSequenceProducer) ? 3 : 4;
+    RETURN_ERROR_IF(offCode > OFFSET_TO_OFFBASE(offsetBound), externalSequences_invalid, "Offset too large!");
+    /* Validate maxNbSeq is large enough for the given matchLength and minMatch */
+    RETURN_ERROR_IF(matchLength < matchLenLowerBound, externalSequences_invalid, "Matchlength too small for the minMatch");
     return 0;
 }
 
 /* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
-static U32 ZSTD_finalizeOffCode(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) {
-    U32 offCode = rawOffset + ZSTD_REP_MOVE;
-    U32 repCode = 0;
+static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0)
+{
+    U32 offBase = OFFSET_TO_OFFBASE(rawOffset);
 
     if (!ll0 && rawOffset == rep[0]) {
-        repCode = 1;
+        offBase = REPCODE1_TO_OFFBASE;
     } else if (rawOffset == rep[1]) {
-        repCode = 2 - ll0;
+        offBase = REPCODE_TO_OFFBASE(2 - ll0);
     } else if (rawOffset == rep[2]) {
-        repCode = 3 - ll0;
+        offBase = REPCODE_TO_OFFBASE(3 - ll0);
     } else if (ll0 && rawOffset == rep[0] - 1) {
-        repCode = 3;
+        offBase = REPCODE3_TO_OFFBASE;
     }
-    if (repCode) {
-        /* ZSTD_storeSeq expects a number in the range [0, 2] to represent a repcode */
-        offCode = repCode - 1;
-    }
-    return offCode;
+    return offBase;
 }
 
-/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of
- * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter.
- */
-static size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
-                                                             const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
-                                                             const void* src, size_t blockSize) {
+size_t
+ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
+                                              ZSTD_sequencePosition* seqPos,
+                                        const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                        const void* src, size_t blockSize,
+                                        ZSTD_paramSwitch_e externalRepSearch)
+{
     U32 idx = seqPos->idx;
+    U32 const startIdx = idx;
     BYTE const* ip = (BYTE const*)(src);
     const BYTE* const iend = ip + blockSize;
     repcodes_t updatedRepcodes;
     U32 dictSize;
-    U32 litLength;
-    U32 matchLength;
-    U32 ll0;
-    U32 offCode;
+
+    DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreExplicitBlockDelim (blockSize = %zu)", blockSize);
 
     if (cctx->cdict) {
         dictSize = (U32)cctx->cdict->dictContentSize;
@@ -5776,26 +6413,55 @@
         dictSize = 0;
     }
     ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
-    for (; (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0) && idx < inSeqsSize; ++idx) {
-        litLength = inSeqs[idx].litLength;
-        matchLength = inSeqs[idx].matchLength;
-        ll0 = litLength == 0;
-        offCode = ZSTD_finalizeOffCode(inSeqs[idx].offset, updatedRepcodes.rep, ll0);
-        updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+    for (; idx < inSeqsSize && (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0); ++idx) {
+        U32 const litLength = inSeqs[idx].litLength;
+        U32 const matchLength = inSeqs[idx].matchLength;
+        U32 offBase;
 
-        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
+        if (externalRepSearch == ZSTD_ps_disable) {
+            offBase = OFFSET_TO_OFFBASE(inSeqs[idx].offset);
+        } else {
+            U32 const ll0 = (litLength == 0);
+            offBase = ZSTD_finalizeOffBase(inSeqs[idx].offset, updatedRepcodes.rep, ll0);
+            ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
+        }
+
+        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
         if (cctx->appliedParams.validateSequences) {
             seqPos->posInSrc += litLength + matchLength;
-            FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
-                                                cctx->appliedParams.cParams.windowLog, dictSize,
-                                                cctx->appliedParams.cParams.minMatch),
+            FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc,
+                                                cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer),
                                                 "Sequence validation failed");
         }
-        RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
+        RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid,
                         "Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
-        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH);
+        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength);
         ip += matchLength + litLength;
     }
+
+    /* If we skipped repcode search while parsing, we need to update repcodes now */
+    assert(externalRepSearch != ZSTD_ps_auto);
+    assert(idx >= startIdx);
+    if (externalRepSearch == ZSTD_ps_disable && idx != startIdx) {
+        U32* const rep = updatedRepcodes.rep;
+        U32 lastSeqIdx = idx - 1; /* index of last non-block-delimiter sequence */
+
+        if (lastSeqIdx >= startIdx + 2) {
+            rep[2] = inSeqs[lastSeqIdx - 2].offset;
+            rep[1] = inSeqs[lastSeqIdx - 1].offset;
+            rep[0] = inSeqs[lastSeqIdx].offset;
+        } else if (lastSeqIdx == startIdx + 1) {
+            rep[2] = rep[0];
+            rep[1] = inSeqs[lastSeqIdx - 1].offset;
+            rep[0] = inSeqs[lastSeqIdx].offset;
+        } else {
+            assert(lastSeqIdx == startIdx);
+            rep[2] = rep[1];
+            rep[1] = rep[0];
+            rep[0] = inSeqs[lastSeqIdx].offset;
+        }
+    }
+
     ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t));
 
     if (inSeqs[idx].litLength) {
@@ -5804,25 +6470,16 @@
         ip += inSeqs[idx].litLength;
         seqPos->posInSrc += inSeqs[idx].litLength;
     }
-    RETURN_ERROR_IF(ip != iend, corruption_detected, "Blocksize doesn't agree with block delimiter!");
+    RETURN_ERROR_IF(ip != iend, externalSequences_invalid, "Blocksize doesn't agree with block delimiter!");
     seqPos->idx = idx+1;
     return 0;
 }
 
-/* Returns the number of bytes to move the current read position back by. Only non-zero
- * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something
- * went wrong.
- *
- * This function will attempt to scan through blockSize bytes represented by the sequences
- * in inSeqs, storing any (partial) sequences.
- *
- * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to
- * avoid splitting a match, or to avoid splitting a match such that it would produce a match
- * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block.
- */
-static size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
-                                                       const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
-                                                       const void* src, size_t blockSize) {
+size_t
+ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
+                                   const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                   const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch)
+{
     U32 idx = seqPos->idx;
     U32 startPosInSequence = seqPos->posInSequence;
     U32 endPosInSequence = seqPos->posInSequence + (U32)blockSize;
@@ -5832,10 +6489,9 @@
     repcodes_t updatedRepcodes;
     U32 bytesAdjustment = 0;
     U32 finalMatchSplit = 0;
-    U32 litLength;
-    U32 matchLength;
-    U32 rawOffset;
-    U32 offCode;
+
+    /* TODO(embg) support fast parsing mode in noBlockDelim mode */
+    (void)externalRepSearch;
 
     if (cctx->cdict) {
         dictSize = cctx->cdict->dictContentSize;
@@ -5844,14 +6500,15 @@
     } else {
         dictSize = 0;
     }
-    DEBUGLOG(5, "ZSTD_copySequencesToSeqStore: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
+    DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreNoBlockDelim: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
     DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
     ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
     while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) {
         const ZSTD_Sequence currSeq = inSeqs[idx];
-        litLength = currSeq.litLength;
-        matchLength = currSeq.matchLength;
-        rawOffset = currSeq.offset;
+        U32 litLength = currSeq.litLength;
+        U32 matchLength = currSeq.matchLength;
+        U32 const rawOffset = currSeq.offset;
+        U32 offBase;
 
         /* Modify the sequence depending on where endPosInSequence lies */
         if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) {
@@ -5865,7 +6522,6 @@
             /* Move to the next sequence */
             endPosInSequence -= currSeq.litLength + currSeq.matchLength;
             startPosInSequence = 0;
-            idx++;
         } else {
             /* This is the final (partial) sequence we're adding from inSeqs, and endPosInSequence
                does not reach the end of the match. So, we have to split the sequence */
@@ -5904,23 +6560,24 @@
             }
         }
         /* Check if this offset can be represented with a repcode */
-        {   U32 ll0 = (litLength == 0);
-            offCode = ZSTD_finalizeOffCode(rawOffset, updatedRepcodes.rep, ll0);
-            updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+        {   U32 const ll0 = (litLength == 0);
+            offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0);
+            ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
         }
 
         if (cctx->appliedParams.validateSequences) {
             seqPos->posInSrc += litLength + matchLength;
-            FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
-                                                   cctx->appliedParams.cParams.windowLog, dictSize,
-                                                   cctx->appliedParams.cParams.minMatch),
+            FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, cctx->appliedParams.cParams.minMatch, seqPos->posInSrc,
+                                                   cctx->appliedParams.cParams.windowLog, dictSize, cctx->appliedParams.useSequenceProducer),
                                                    "Sequence validation failed");
         }
-        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
-        RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
+        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
+        RETURN_ERROR_IF(idx - seqPos->idx >= cctx->seqStore.maxNbSeq, externalSequences_invalid,
                         "Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
-        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH);
+        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength);
         ip += matchLength + litLength;
+        if (!finalMatchSplit)
+            idx++; /* Next Sequence */
     }
     DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
     assert(idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength);
@@ -5943,8 +6600,9 @@
 
 typedef size_t (*ZSTD_sequenceCopier) (ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
                                        const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
-                                       const void* src, size_t blockSize);
-static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) {
+                                       const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch);
+static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode)
+{
     ZSTD_sequenceCopier sequenceCopier = NULL;
     assert(ZSTD_cParam_withinBounds(ZSTD_c_blockDelimiters, mode));
     if (mode == ZSTD_sf_explicitBlockDelimiters) {
@@ -5956,24 +6614,75 @@
     return sequenceCopier;
 }
 
+/* Discover the size of next block by searching for the delimiter.
+ * Note that a block delimiter **must** exist in this mode,
+ * otherwise it's an input error.
+ * The block size retrieved will be later compared to ensure it remains within bounds */
+static size_t
+blockSize_explicitDelimiter(const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos)
+{
+    int end = 0;
+    size_t blockSize = 0;
+    size_t spos = seqPos.idx;
+    DEBUGLOG(6, "blockSize_explicitDelimiter : seq %zu / %zu", spos, inSeqsSize);
+    assert(spos <= inSeqsSize);
+    while (spos < inSeqsSize) {
+        end = (inSeqs[spos].offset == 0);
+        blockSize += inSeqs[spos].litLength + inSeqs[spos].matchLength;
+        if (end) {
+            if (inSeqs[spos].matchLength != 0)
+                RETURN_ERROR(externalSequences_invalid, "delimiter format error : both matchlength and offset must be == 0");
+            break;
+        }
+        spos++;
+    }
+    if (!end)
+        RETURN_ERROR(externalSequences_invalid, "Reached end of sequences without finding a block delimiter");
+    return blockSize;
+}
+
+/* More a "target" block size */
+static size_t blockSize_noDelimiter(size_t blockSize, size_t remaining)
+{
+    int const lastBlock = (remaining <= blockSize);
+    return lastBlock ? remaining : blockSize;
+}
+
+static size_t determine_blockSize(ZSTD_sequenceFormat_e mode,
+                           size_t blockSize, size_t remaining,
+                     const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos)
+{
+    DEBUGLOG(6, "determine_blockSize : remainingSize = %zu", remaining);
+    if (mode == ZSTD_sf_noBlockDelimiters)
+        return blockSize_noDelimiter(blockSize, remaining);
+    {   size_t const explicitBlockSize = blockSize_explicitDelimiter(inSeqs, inSeqsSize, seqPos);
+        FORWARD_IF_ERROR(explicitBlockSize, "Error while determining block size with explicit delimiters");
+        if (explicitBlockSize > blockSize)
+            RETURN_ERROR(externalSequences_invalid, "sequences incorrectly define a too large block");
+        if (explicitBlockSize > remaining)
+            RETURN_ERROR(externalSequences_invalid, "sequences define a frame longer than source");
+        return explicitBlockSize;
+    }
+}
+
 /* Compress, block-by-block, all of the sequences given.
  *
- * Returns the cumulative size of all compressed blocks (including their headers), otherwise a ZSTD error.
+ * Returns the cumulative size of all compressed blocks (including their headers),
+ * otherwise a ZSTD error.
  */
-static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
-                                              void* dst, size_t dstCapacity,
-                                              const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
-                                              const void* src, size_t srcSize) {
+static size_t
+ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
+                                void* dst, size_t dstCapacity,
+                          const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+                          const void* src, size_t srcSize)
+{
     size_t cSize = 0;
-    U32 lastBlock;
-    size_t blockSize;
-    size_t compressedSeqsSize;
     size_t remaining = srcSize;
     ZSTD_sequencePosition seqPos = {0, 0, 0};
 
     BYTE const* ip = (BYTE const*)src;
     BYTE* op = (BYTE*)dst;
-    ZSTD_sequenceCopier sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters);
+    ZSTD_sequenceCopier const sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters);
 
     DEBUGLOG(4, "ZSTD_compressSequences_internal srcSize: %zu, inSeqsSize: %zu", srcSize, inSeqsSize);
     /* Special case: empty frame */
@@ -5987,22 +6696,29 @@
     }
 
     while (remaining) {
+        size_t compressedSeqsSize;
         size_t cBlockSize;
         size_t additionalByteAdjustment;
-        lastBlock = remaining <= cctx->blockSize;
-        blockSize = lastBlock ? (U32)remaining : (U32)cctx->blockSize;
+        size_t blockSize = determine_blockSize(cctx->appliedParams.blockDelimiters,
+                                        cctx->blockSize, remaining,
+                                        inSeqs, inSeqsSize, seqPos);
+        U32 const lastBlock = (blockSize == remaining);
+        FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size");
+        assert(blockSize <= remaining);
         ZSTD_resetSeqStore(&cctx->seqStore);
-        DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize);
+        DEBUGLOG(5, "Working on new block. Blocksize: %zu (total:%zu)", blockSize, (ip - (const BYTE*)src) + blockSize);
 
-        additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize);
+        additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize, cctx->appliedParams.searchForExternalRepcodes);
         FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy");
         blockSize -= additionalByteAdjustment;
 
         /* If blocks are too small, emit as a nocompress block */
-        if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+        /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+         * additional 1. We need to revisit and change this logic to be more consistent */
+        if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) {
             cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
             FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
-            DEBUGLOG(4, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
+            DEBUGLOG(5, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
             cSize += cBlockSize;
             ip += blockSize;
             op += cBlockSize;
@@ -6011,6 +6727,7 @@
             continue;
         }
 
+        RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "not enough dstCapacity to write a new compressed block");
         compressedSeqsSize = ZSTD_entropyCompressSeqStore(&cctx->seqStore,
                                 &cctx->blockState.prevCBlock->entropy, &cctx->blockState.nextCBlock->entropy,
                                 &cctx->appliedParams,
@@ -6019,11 +6736,11 @@
                                 cctx->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
                                 cctx->bmi2);
         FORWARD_IF_ERROR(compressedSeqsSize, "Compressing sequences of block failed");
-        DEBUGLOG(4, "Compressed sequences size: %zu", compressedSeqsSize);
+        DEBUGLOG(5, "Compressed sequences size: %zu", compressedSeqsSize);
 
         if (!cctx->isFirstBlock &&
             ZSTD_maybeRLE(&cctx->seqStore) &&
-            ZSTD_isRLE((BYTE const*)src, srcSize)) {
+            ZSTD_isRLE(ip, blockSize)) {
             /* We don't want to emit our first block as a RLE even if it qualifies because
             * doing so will cause the decoder (cli only) to throw a "should consume all input error."
             * This is only an issue for zstd <= v1.4.3
@@ -6034,12 +6751,12 @@
         if (compressedSeqsSize == 0) {
             /* ZSTD_noCompressBlock writes the block header as well */
             cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
-            FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
-            DEBUGLOG(4, "Writing out nocompress block, size: %zu", cBlockSize);
+            FORWARD_IF_ERROR(cBlockSize, "ZSTD_noCompressBlock failed");
+            DEBUGLOG(5, "Writing out nocompress block, size: %zu", cBlockSize);
         } else if (compressedSeqsSize == 1) {
             cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock);
-            FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed");
-            DEBUGLOG(4, "Writing out RLE block, size: %zu", cBlockSize);
+            FORWARD_IF_ERROR(cBlockSize, "ZSTD_rleCompressBlock failed");
+            DEBUGLOG(5, "Writing out RLE block, size: %zu", cBlockSize);
         } else {
             U32 cBlockHeader;
             /* Error checking and repcodes update */
@@ -6051,11 +6768,10 @@
             cBlockHeader = lastBlock + (((U32)bt_compressed)<<1) + (U32)(compressedSeqsSize << 3);
             MEM_writeLE24(op, cBlockHeader);
             cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize;
-            DEBUGLOG(4, "Writing out compressed block, size: %zu", cBlockSize);
+            DEBUGLOG(5, "Writing out compressed block, size: %zu", cBlockSize);
         }
 
         cSize += cBlockSize;
-        DEBUGLOG(4, "cSize running total: %zu", cSize);
 
         if (lastBlock) {
             break;
@@ -6066,21 +6782,25 @@
             dstCapacity -= cBlockSize;
             cctx->isFirstBlock = 0;
         }
+        DEBUGLOG(5, "cSize running total: %zu (remaining dstCapacity=%zu)", cSize, dstCapacity);
     }
 
+    DEBUGLOG(4, "cSize final total: %zu", cSize);
     return cSize;
 }
 
-size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapacity,
+size_t ZSTD_compressSequences(ZSTD_CCtx* cctx,
+                              void* dst, size_t dstCapacity,
                               const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
-                              const void* src, size_t srcSize) {
+                              const void* src, size_t srcSize)
+{
     BYTE* op = (BYTE*)dst;
     size_t cSize = 0;
     size_t compressedBlocksSize = 0;
     size_t frameHeaderSize = 0;
 
     /* Transparent initialization stage, same as compressStream2() */
-    DEBUGLOG(3, "ZSTD_compressSequences()");
+    DEBUGLOG(4, "ZSTD_compressSequences (dstCapacity=%zu)", dstCapacity);
     assert(cctx != NULL);
     FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, ZSTD_e_end, srcSize), "CCtx initialization failed");
     /* Begin writing output, starting with frame header */
@@ -6108,26 +6828,34 @@
         cSize += 4;
     }
 
-    DEBUGLOG(3, "Final compressed size: %zu", cSize);
+    DEBUGLOG(4, "Final compressed size: %zu", cSize);
     return cSize;
 }
 
 /*======   Finalize   ======*/
 
+static ZSTD_inBuffer inBuffer_forEndFlush(const ZSTD_CStream* zcs)
+{
+    const ZSTD_inBuffer nullInput = { NULL, 0, 0 };
+    const int stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+    return stableInput ? zcs->expectedInBuffer : nullInput;
+}
+
 /*! ZSTD_flushStream() :
  * @return : amount of data remaining to flush */
 size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
 {
-    ZSTD_inBuffer input = { NULL, 0, 0 };
+    ZSTD_inBuffer input = inBuffer_forEndFlush(zcs);
+    input.size = input.pos; /* do not ingest more input during flush */
     return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush);
 }
 
 
 size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
 {
-    ZSTD_inBuffer input = { NULL, 0, 0 };
+    ZSTD_inBuffer input = inBuffer_forEndFlush(zcs);
     size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);
-    FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed");
+    FORWARD_IF_ERROR(remainingToFlush , "ZSTD_compressStream2(,,ZSTD_e_end) failed");
     if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush;   /* minimal estimation */
     /* single thread mode : attempt to calculate remaining to flush more precisely */
     {   size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE;
@@ -6140,119 +6868,12 @@
 
 
 /*-=====  Pre-defined compression levels  =====-*/
+#include "clevels.h"
 
-#define ZSTD_MAX_CLEVEL     22
 int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }
 int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; }
 int ZSTD_defaultCLevel(void) { return ZSTD_CLEVEL_DEFAULT; }
 
-static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {
-{   /* "default" - for any srcSize > 256 KB */
-    /* W,  C,  H,  S,  L, TL, strat */
-    { 19, 12, 13,  1,  6,  1, ZSTD_fast    },  /* base for negative levels */
-    { 19, 13, 14,  1,  7,  0, ZSTD_fast    },  /* level  1 */
-    { 20, 15, 16,  1,  6,  0, ZSTD_fast    },  /* level  2 */
-    { 21, 16, 17,  1,  5,  0, ZSTD_dfast   },  /* level  3 */
-    { 21, 18, 18,  1,  5,  0, ZSTD_dfast   },  /* level  4 */
-    { 21, 18, 19,  2,  5,  2, ZSTD_greedy  },  /* level  5 */
-    { 21, 19, 19,  3,  5,  4, ZSTD_greedy  },  /* level  6 */
-    { 21, 19, 19,  3,  5,  8, ZSTD_lazy    },  /* level  7 */
-    { 21, 19, 19,  3,  5, 16, ZSTD_lazy2   },  /* level  8 */
-    { 21, 19, 20,  4,  5, 16, ZSTD_lazy2   },  /* level  9 */
-    { 22, 20, 21,  4,  5, 16, ZSTD_lazy2   },  /* level 10 */
-    { 22, 21, 22,  4,  5, 16, ZSTD_lazy2   },  /* level 11 */
-    { 22, 21, 22,  5,  5, 16, ZSTD_lazy2   },  /* level 12 */
-    { 22, 21, 22,  5,  5, 32, ZSTD_btlazy2 },  /* level 13 */
-    { 22, 22, 23,  5,  5, 32, ZSTD_btlazy2 },  /* level 14 */
-    { 22, 23, 23,  6,  5, 32, ZSTD_btlazy2 },  /* level 15 */
-    { 22, 22, 22,  5,  5, 48, ZSTD_btopt   },  /* level 16 */
-    { 23, 23, 22,  5,  4, 64, ZSTD_btopt   },  /* level 17 */
-    { 23, 23, 22,  6,  3, 64, ZSTD_btultra },  /* level 18 */
-    { 23, 24, 22,  7,  3,256, ZSTD_btultra2},  /* level 19 */
-    { 25, 25, 23,  7,  3,256, ZSTD_btultra2},  /* level 20 */
-    { 26, 26, 24,  7,  3,512, ZSTD_btultra2},  /* level 21 */
-    { 27, 27, 25,  9,  3,999, ZSTD_btultra2},  /* level 22 */
-},
-{   /* for srcSize <= 256 KB */
-    /* W,  C,  H,  S,  L,  T, strat */
-    { 18, 12, 13,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
-    { 18, 13, 14,  1,  6,  0, ZSTD_fast    },  /* level  1 */
-    { 18, 14, 14,  1,  5,  0, ZSTD_dfast   },  /* level  2 */
-    { 18, 16, 16,  1,  4,  0, ZSTD_dfast   },  /* level  3 */
-    { 18, 16, 17,  2,  5,  2, ZSTD_greedy  },  /* level  4.*/
-    { 18, 18, 18,  3,  5,  2, ZSTD_greedy  },  /* level  5.*/
-    { 18, 18, 19,  3,  5,  4, ZSTD_lazy    },  /* level  6.*/
-    { 18, 18, 19,  4,  4,  4, ZSTD_lazy    },  /* level  7 */
-    { 18, 18, 19,  4,  4,  8, ZSTD_lazy2   },  /* level  8 */
-    { 18, 18, 19,  5,  4,  8, ZSTD_lazy2   },  /* level  9 */
-    { 18, 18, 19,  6,  4,  8, ZSTD_lazy2   },  /* level 10 */
-    { 18, 18, 19,  5,  4, 12, ZSTD_btlazy2 },  /* level 11.*/
-    { 18, 19, 19,  7,  4, 12, ZSTD_btlazy2 },  /* level 12.*/
-    { 18, 18, 19,  4,  4, 16, ZSTD_btopt   },  /* level 13 */
-    { 18, 18, 19,  4,  3, 32, ZSTD_btopt   },  /* level 14.*/
-    { 18, 18, 19,  6,  3,128, ZSTD_btopt   },  /* level 15.*/
-    { 18, 19, 19,  6,  3,128, ZSTD_btultra },  /* level 16.*/
-    { 18, 19, 19,  8,  3,256, ZSTD_btultra },  /* level 17.*/
-    { 18, 19, 19,  6,  3,128, ZSTD_btultra2},  /* level 18.*/
-    { 18, 19, 19,  8,  3,256, ZSTD_btultra2},  /* level 19.*/
-    { 18, 19, 19, 10,  3,512, ZSTD_btultra2},  /* level 20.*/
-    { 18, 19, 19, 12,  3,512, ZSTD_btultra2},  /* level 21.*/
-    { 18, 19, 19, 13,  3,999, ZSTD_btultra2},  /* level 22.*/
-},
-{   /* for srcSize <= 128 KB */
-    /* W,  C,  H,  S,  L,  T, strat */
-    { 17, 12, 12,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
-    { 17, 12, 13,  1,  6,  0, ZSTD_fast    },  /* level  1 */
-    { 17, 13, 15,  1,  5,  0, ZSTD_fast    },  /* level  2 */
-    { 17, 15, 16,  2,  5,  0, ZSTD_dfast   },  /* level  3 */
-    { 17, 17, 17,  2,  4,  0, ZSTD_dfast   },  /* level  4 */
-    { 17, 16, 17,  3,  4,  2, ZSTD_greedy  },  /* level  5 */
-    { 17, 17, 17,  3,  4,  4, ZSTD_lazy    },  /* level  6 */
-    { 17, 17, 17,  3,  4,  8, ZSTD_lazy2   },  /* level  7 */
-    { 17, 17, 17,  4,  4,  8, ZSTD_lazy2   },  /* level  8 */
-    { 17, 17, 17,  5,  4,  8, ZSTD_lazy2   },  /* level  9 */
-    { 17, 17, 17,  6,  4,  8, ZSTD_lazy2   },  /* level 10 */
-    { 17, 17, 17,  5,  4,  8, ZSTD_btlazy2 },  /* level 11 */
-    { 17, 18, 17,  7,  4, 12, ZSTD_btlazy2 },  /* level 12 */
-    { 17, 18, 17,  3,  4, 12, ZSTD_btopt   },  /* level 13.*/
-    { 17, 18, 17,  4,  3, 32, ZSTD_btopt   },  /* level 14.*/
-    { 17, 18, 17,  6,  3,256, ZSTD_btopt   },  /* level 15.*/
-    { 17, 18, 17,  6,  3,128, ZSTD_btultra },  /* level 16.*/
-    { 17, 18, 17,  8,  3,256, ZSTD_btultra },  /* level 17.*/
-    { 17, 18, 17, 10,  3,512, ZSTD_btultra },  /* level 18.*/
-    { 17, 18, 17,  5,  3,256, ZSTD_btultra2},  /* level 19.*/
-    { 17, 18, 17,  7,  3,512, ZSTD_btultra2},  /* level 20.*/
-    { 17, 18, 17,  9,  3,512, ZSTD_btultra2},  /* level 21.*/
-    { 17, 18, 17, 11,  3,999, ZSTD_btultra2},  /* level 22.*/
-},
-{   /* for srcSize <= 16 KB */
-    /* W,  C,  H,  S,  L,  T, strat */
-    { 14, 12, 13,  1,  5,  1, ZSTD_fast    },  /* base for negative levels */
-    { 14, 14, 15,  1,  5,  0, ZSTD_fast    },  /* level  1 */
-    { 14, 14, 15,  1,  4,  0, ZSTD_fast    },  /* level  2 */
-    { 14, 14, 15,  2,  4,  0, ZSTD_dfast   },  /* level  3 */
-    { 14, 14, 14,  4,  4,  2, ZSTD_greedy  },  /* level  4 */
-    { 14, 14, 14,  3,  4,  4, ZSTD_lazy    },  /* level  5.*/
-    { 14, 14, 14,  4,  4,  8, ZSTD_lazy2   },  /* level  6 */
-    { 14, 14, 14,  6,  4,  8, ZSTD_lazy2   },  /* level  7 */
-    { 14, 14, 14,  8,  4,  8, ZSTD_lazy2   },  /* level  8.*/
-    { 14, 15, 14,  5,  4,  8, ZSTD_btlazy2 },  /* level  9.*/
-    { 14, 15, 14,  9,  4,  8, ZSTD_btlazy2 },  /* level 10.*/
-    { 14, 15, 14,  3,  4, 12, ZSTD_btopt   },  /* level 11.*/
-    { 14, 15, 14,  4,  3, 24, ZSTD_btopt   },  /* level 12.*/
-    { 14, 15, 14,  5,  3, 32, ZSTD_btultra },  /* level 13.*/
-    { 14, 15, 15,  6,  3, 64, ZSTD_btultra },  /* level 14.*/
-    { 14, 15, 15,  7,  3,256, ZSTD_btultra },  /* level 15.*/
-    { 14, 15, 15,  5,  3, 48, ZSTD_btultra2},  /* level 16.*/
-    { 14, 15, 15,  6,  3,128, ZSTD_btultra2},  /* level 17.*/
-    { 14, 15, 15,  7,  3,256, ZSTD_btultra2},  /* level 18.*/
-    { 14, 15, 15,  8,  3,256, ZSTD_btultra2},  /* level 19.*/
-    { 14, 15, 15,  8,  3,512, ZSTD_btultra2},  /* level 20.*/
-    { 14, 15, 15,  9,  3,512, ZSTD_btultra2},  /* level 21.*/
-    { 14, 15, 15, 10,  3,999, ZSTD_btultra2},  /* level 22.*/
-},
-};
-
 static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams(int const compressionLevel, size_t const dictSize)
 {
     ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, 0, dictSize, ZSTD_cpm_createCDict);
@@ -6356,7 +6977,7 @@
             cp.targetLength = (unsigned)(-clampedCompressionLevel);
         }
         /* refine parameters based on srcSize & dictSize */
-        return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode);
+        return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode, ZSTD_ps_auto);
     }
 }
 
@@ -6391,3 +7012,21 @@
     if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN;
     return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown);
 }
+
+void ZSTD_registerSequenceProducer(
+    ZSTD_CCtx* zc, void* mState,
+    ZSTD_sequenceProducer_F* mFinder
+) {
+    if (mFinder != NULL) {
+        ZSTD_externalMatchCtx emctx;
+        emctx.mState = mState;
+        emctx.mFinder = mFinder;
+        emctx.seqBuffer = NULL;
+        emctx.seqBufferCapacity = 0;
+        zc->externalMatchCtx = emctx;
+        zc->requestedParams.useSequenceProducer = 1;
+    } else {
+        ZSTD_memset(&zc->externalMatchCtx, 0, sizeof(zc->externalMatchCtx));
+        zc->requestedParams.useSequenceProducer = 0;
+    }
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
index 3b04fd0..10f68d0 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -23,6 +23,7 @@
 #ifdef ZSTD_MULTITHREAD
 #  include "zstdmt_compress.h"
 #endif
+#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_NbCommonBytes */
 
 #if defined (__cplusplus)
 extern "C" {
@@ -63,7 +64,7 @@
 } ZSTD_localDict;
 
 typedef struct {
-    HUF_CElt CTable[HUF_CTABLE_SIZE_U32(255)];
+    HUF_CElt CTable[HUF_CTABLE_SIZE_ST(255)];
     HUF_repeat repeatMode;
 } ZSTD_hufCTables_t;
 
@@ -117,19 +118,20 @@
 /** ZSTD_buildBlockEntropyStats() :
  *  Builds entropy for the block.
  *  @return : 0 on success or error code */
-size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr,
-                             const ZSTD_entropyCTables_t* prevEntropy,
-                                   ZSTD_entropyCTables_t* nextEntropy,
-                             const ZSTD_CCtx_params* cctxParams,
-                                   ZSTD_entropyCTablesMetadata_t* entropyMetadata,
-                                   void* workspace, size_t wkspSize);
+size_t ZSTD_buildBlockEntropyStats(
+                    const seqStore_t* seqStorePtr,
+                    const ZSTD_entropyCTables_t* prevEntropy,
+                          ZSTD_entropyCTables_t* nextEntropy,
+                    const ZSTD_CCtx_params* cctxParams,
+                          ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                          void* workspace, size_t wkspSize);
 
 /*********************************
 *  Compression internals structs *
 *********************************/
 
 typedef struct {
-    U32 off;            /* Offset code (offset + ZSTD_REP_MOVE) for the match */
+    U32 off;            /* Offset sumtype code for the match, using ZSTD_storeSeq() format */
     U32 len;            /* Raw length of match */
 } ZSTD_match_t;
 
@@ -148,6 +150,12 @@
   size_t capacity;      /* The capacity starting from `seq` pointer */
 } rawSeqStore_t;
 
+typedef struct {
+    U32 idx;            /* Index in array of ZSTD_Sequence */
+    U32 posInSequence;  /* Position within sequence at idx */
+    size_t posInSrc;    /* Number of bytes given by sequences provided so far */
+} ZSTD_sequencePosition;
+
 UNUSED_ATTR static const rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0, 0};
 
 typedef struct {
@@ -179,7 +187,7 @@
     U32  offCodeSumBasePrice;    /* to compare to log2(offreq)  */
     ZSTD_OptPrice_e priceType;   /* prices can be determined dynamically, or follow a pre-defined cost structure */
     const ZSTD_entropyCTables_t* symbolCosts;  /* pre-calculated dictionary statistics */
-    ZSTD_literalCompressionMode_e literalCompressionMode;
+    ZSTD_paramSwitch_e literalCompressionMode;
 } optState_t;
 
 typedef struct {
@@ -199,6 +207,8 @@
                                 */
 } ZSTD_window_t;
 
+#define ZSTD_WINDOW_START_INDEX 2
+
 typedef struct ZSTD_matchState_t ZSTD_matchState_t;
 
 #define ZSTD_ROW_HASH_CACHE_SIZE 8       /* Size of prefetching hash cache for row-based matchfinder */
@@ -216,8 +226,10 @@
     U32 hashLog3;           /* dispatch table for matches of len==3 : larger == faster, more memory */
 
     U32 rowHashLog;                          /* For row-based matchfinder: Hashlog based on nb of rows in the hashTable.*/
-    U16* tagTable;                           /* For row-based matchFinder: A row-based table containing the hashes and head index. */
+    BYTE* tagTable;                          /* For row-based matchFinder: A row-based table containing the hashes and head index. */
     U32 hashCache[ZSTD_ROW_HASH_CACHE_SIZE]; /* For row-based matchFinder: a cache of hashes to improve speed */
+    U64 hashSalt;                            /* For row-based matchFinder: salts the hash for re-use of tag table */
+    U32 hashSaltEntropy;                     /* For row-based matchFinder: collects entropy for salt generation */
 
     U32* hashTable;
     U32* hashTable3;
@@ -232,6 +244,18 @@
     const ZSTD_matchState_t* dictMatchState;
     ZSTD_compressionParameters cParams;
     const rawSeqStore_t* ldmSeqStore;
+
+    /* Controls prefetching in some dictMatchState matchfinders.
+     * This behavior is controlled from the cctx ms.
+     * This parameter has no effect in the cdict ms. */
+    int prefetchCDictTables;
+
+    /* When == 0, lazy match finders insert every position.
+     * When != 0, lazy match finders only insert positions they search.
+     * This allows them to skip much faster over incompressible data,
+     * at a small cost to compression ratio.
+     */
+    int lazySkipping;
 };
 
 typedef struct {
@@ -264,7 +288,7 @@
 } ldmState_t;
 
 typedef struct {
-    U32 enableLdm;          /* 1 if enable long distance matching */
+    ZSTD_paramSwitch_e enableLdm; /* ZSTD_ps_enable to enable LDM. ZSTD_ps_auto by default */
     U32 hashLog;            /* Log size of hashTable */
     U32 bucketSizeLog;      /* Log bucket size for collision resolution, at most 8 */
     U32 minMatchLength;     /* Minimum match length */
@@ -295,7 +319,7 @@
                                 * There is no guarantee that hint is close to actual source size */
 
     ZSTD_dictAttachPref_e attachDictPref;
-    ZSTD_literalCompressionMode_e literalCompressionMode;
+    ZSTD_paramSwitch_e literalCompressionMode;
 
     /* Multithreading: used to pass parameters to mtctx */
     int nbWorkers;
@@ -318,16 +342,34 @@
     int validateSequences;
 
     /* Block splitting */
-    int splitBlocks;
+    ZSTD_paramSwitch_e useBlockSplitter;
 
     /* Param for deciding whether to use row-based matchfinder */
-    ZSTD_useRowMatchFinderMode_e useRowMatchFinder;
+    ZSTD_paramSwitch_e useRowMatchFinder;
 
     /* Always load a dictionary in ext-dict mode (not prefix mode)? */
     int deterministicRefPrefix;
 
     /* Internal use, for createCCtxParams() and freeCCtxParams() only */
     ZSTD_customMem customMem;
+
+    /* Controls prefetching in some dictMatchState matchfinders */
+    ZSTD_paramSwitch_e prefetchCDictTables;
+
+    /* Controls whether zstd will fall back to an internal matchfinder
+     * if the external matchfinder returns an error code. */
+    int enableMatchFinderFallback;
+
+    /* Indicates whether an external matchfinder has been referenced.
+     * Users can't set this externally.
+     * It is set internally in ZSTD_registerSequenceProducer(). */
+    int useSequenceProducer;
+
+    /* Adjust the max block size*/
+    size_t maxBlockSize;
+
+    /* Controls repcode search in external sequence parsing */
+    ZSTD_paramSwitch_e searchForExternalRepcodes;
 };  /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
 
 #define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2))
@@ -343,6 +385,30 @@
     ZSTDb_buffered
 } ZSTD_buffered_policy_e;
 
+/**
+ * Struct that contains all elements of block splitter that should be allocated
+ * in a wksp.
+ */
+#define ZSTD_MAX_NB_BLOCK_SPLITS 196
+typedef struct {
+    seqStore_t fullSeqStoreChunk;
+    seqStore_t firstHalfSeqStore;
+    seqStore_t secondHalfSeqStore;
+    seqStore_t currSeqStore;
+    seqStore_t nextSeqStore;
+
+    U32 partitions[ZSTD_MAX_NB_BLOCK_SPLITS];
+    ZSTD_entropyCTablesMetadata_t entropyMetadata;
+} ZSTD_blockSplitCtx;
+
+/* Context for block-level external matchfinder API */
+typedef struct {
+  void* mState;
+  ZSTD_sequenceProducer_F* mFinder;
+  ZSTD_Sequence* seqBuffer;
+  size_t seqBufferCapacity;
+} ZSTD_externalMatchCtx;
+
 struct ZSTD_CCtx_s {
     ZSTD_compressionStage_e stage;
     int cParamsChanged;                  /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */
@@ -374,7 +440,7 @@
     ZSTD_blockState_t blockState;
     U32* entropyWorkspace;  /* entropy workspace of ENTROPY_WORKSPACE_SIZE bytes */
 
-    /* Wether we are streaming or not */
+    /* Whether we are streaming or not */
     ZSTD_buffered_policy_e bufferedPolicy;
 
     /* streaming */
@@ -392,6 +458,7 @@
 
     /* Stable in/out buffer verification */
     ZSTD_inBuffer expectedInBuffer;
+    size_t stableIn_notConsumed; /* nb bytes within stable input buffer that are said to be consumed but are not */
     size_t expectedOutBufferSize;
 
     /* Dictionary */
@@ -408,9 +475,16 @@
 #if ZSTD_TRACE
     ZSTD_TraceCtx traceCtx;
 #endif
+
+    /* Workspace for block splitter */
+    ZSTD_blockSplitCtx blockSplitCtx;
+
+    /* Workspace for external matchfinder */
+    ZSTD_externalMatchCtx externalMatchCtx;
 };
 
 typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e;
+typedef enum { ZSTD_tfp_forCCtx, ZSTD_tfp_forCDict } ZSTD_tableFillPurpose_e;
 
 typedef enum {
     ZSTD_noDict = 0,
@@ -432,7 +506,7 @@
                                  * In this mode we take both the source size and the dictionary size
                                  * into account when selecting and adjusting the parameters.
                                  */
-    ZSTD_cpm_unknown = 3,       /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams.
+    ZSTD_cpm_unknown = 3        /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams.
                                  * We don't know what these parameters are for. We default to the legacy
                                  * behavior of taking both the source size and the dict size into account
                                  * when selecting and adjusting parameters.
@@ -442,7 +516,7 @@
 typedef size_t (*ZSTD_blockCompressor) (
         ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e rowMatchfinderMode, ZSTD_dictMode_e dictMode);
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_paramSwitch_e rowMatchfinderMode, ZSTD_dictMode_e dictMode);
 
 
 MEM_STATIC U32 ZSTD_LLcode(U32 litLength)
@@ -476,31 +550,6 @@
     return (mlBase > 127) ? ZSTD_highbit32(mlBase) + ML_deltaCode : ML_Code[mlBase];
 }
 
-typedef struct repcodes_s {
-    U32 rep[3];
-} repcodes_t;
-
-MEM_STATIC repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0)
-{
-    repcodes_t newReps;
-    if (offset >= ZSTD_REP_NUM) {  /* full offset */
-        newReps.rep[2] = rep[1];
-        newReps.rep[1] = rep[0];
-        newReps.rep[0] = offset - ZSTD_REP_MOVE;
-    } else {   /* repcode */
-        U32 const repCode = offset + ll0;
-        if (repCode > 0) {  /* note : if repCode==0, no change */
-            U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
-            newReps.rep[2] = (repCode >= 2) ? rep[1] : rep[2];
-            newReps.rep[1] = rep[0];
-            newReps.rep[0] = currentOffset;
-        } else {   /* repCode == 0 */
-            ZSTD_memcpy(&newReps, rep, sizeof(newReps));
-        }
-    }
-    return newReps;
-}
-
 /* ZSTD_cParam_withinBounds:
  * @return 1 if value is within cParam bounds,
  * 0 otherwise */
@@ -516,9 +565,11 @@
 /* ZSTD_noCompressBlock() :
  * Writes uncompressed block to dst buffer from given src.
  * Returns the size of the block */
-MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock)
+MEM_STATIC size_t
+ZSTD_noCompressBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock)
 {
     U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3);
+    DEBUGLOG(5, "ZSTD_noCompressBlock (srcSize=%zu, dstCapacity=%zu)", srcSize, dstCapacity);
     RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity,
                     dstSize_tooSmall, "dst buf too small for uncompressed block");
     MEM_writeLE24(dst, cBlockHeader24);
@@ -526,7 +577,8 @@
     return ZSTD_blockHeaderSize + srcSize;
 }
 
-MEM_STATIC size_t ZSTD_rleCompressBlock (void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock)
+MEM_STATIC size_t
+ZSTD_rleCompressBlock(void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock)
 {
     BYTE* const op = (BYTE*)dst;
     U32 const cBlockHeader = lastBlock + (((U32)bt_rle)<<1) + (U32)(srcSize << 3);
@@ -545,21 +597,21 @@
 {
     U32 const minlog = (strat>=ZSTD_btultra) ? (U32)(strat) - 1 : 6;
     ZSTD_STATIC_ASSERT(ZSTD_btultra == 8);
-    assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));
+    assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, (int)strat));
     return (srcSize >> minlog) + 2;
 }
 
-MEM_STATIC int ZSTD_disableLiteralsCompression(const ZSTD_CCtx_params* cctxParams)
+MEM_STATIC int ZSTD_literalsCompressionIsDisabled(const ZSTD_CCtx_params* cctxParams)
 {
     switch (cctxParams->literalCompressionMode) {
-    case ZSTD_lcm_huffman:
+    case ZSTD_ps_enable:
         return 0;
-    case ZSTD_lcm_uncompressed:
+    case ZSTD_ps_disable:
         return 1;
     default:
         assert(0 /* impossible: pre-validated */);
-        /* fall-through */
-    case ZSTD_lcm_auto:
+        ZSTD_FALLTHROUGH;
+    case ZSTD_ps_auto:
         return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0);
     }
 }
@@ -569,7 +621,9 @@
  *  Only called when the sequence ends past ilimit_w, so it only needs to be optimized for single
  *  large copies.
  */
-static void ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w) {
+static void
+ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE const* ilimit_w)
+{
     assert(iend > ilimit_w);
     if (ip <= ilimit_w) {
         ZSTD_wildcopy(op, ip, ilimit_w - ip, ZSTD_no_overlap);
@@ -579,14 +633,28 @@
     while (ip < iend) *op++ = *ip++;
 }
 
+
+#define REPCODE1_TO_OFFBASE REPCODE_TO_OFFBASE(1)
+#define REPCODE2_TO_OFFBASE REPCODE_TO_OFFBASE(2)
+#define REPCODE3_TO_OFFBASE REPCODE_TO_OFFBASE(3)
+#define REPCODE_TO_OFFBASE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */
+#define OFFSET_TO_OFFBASE(o)  (assert((o)>0), o + ZSTD_REP_NUM)
+#define OFFBASE_IS_OFFSET(o)  ((o) > ZSTD_REP_NUM)
+#define OFFBASE_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM)
+#define OFFBASE_TO_OFFSET(o)  (assert(OFFBASE_IS_OFFSET(o)), (o) - ZSTD_REP_NUM)
+#define OFFBASE_TO_REPCODE(o) (assert(OFFBASE_IS_REPCODE(o)), (o))  /* returns ID 1,2,3 */
+
 /*! ZSTD_storeSeq() :
- *  Store a sequence (litlen, litPtr, offCode and mlBase) into seqStore_t.
- *  `offCode` : distance to match + ZSTD_REP_MOVE (values <= ZSTD_REP_MOVE are repCodes).
- *  `mlBase` : matchLength - MINMATCH
- *  Allowed to overread literals up to litLimit.
+ *  Store a sequence (litlen, litPtr, offBase and matchLength) into seqStore_t.
+ *  @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE().
+ *  @matchLength : must be >= MINMATCH
+ *  Allowed to over-read literals up to litLimit.
 */
-HINT_INLINE UNUSED_ATTR
-void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const BYTE* literals, const BYTE* litLimit, U32 offCode, size_t mlBase)
+HINT_INLINE UNUSED_ATTR void
+ZSTD_storeSeq(seqStore_t* seqStorePtr,
+              size_t litLength, const BYTE* literals, const BYTE* litLimit,
+              U32 offBase,
+              size_t matchLength)
 {
     BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH;
     BYTE const* const litEnd = literals + litLength;
@@ -594,8 +662,8 @@
     static const BYTE* g_start = NULL;
     if (g_start==NULL) g_start = (const BYTE*)literals;  /* note : index only works for compression within a single segment */
     {   U32 const pos = (U32)((const BYTE*)literals - g_start);
-        DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u",
-               pos, (U32)litLength, (U32)mlBase+MINMATCH, (U32)offCode);
+        DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offBase%7u",
+               pos, (U32)litLength, (U32)matchLength, (U32)offBase);
     }
 #endif
     assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq);
@@ -605,9 +673,9 @@
     assert(literals + litLength <= litLimit);
     if (litEnd <= litLimit_w) {
         /* Common case we can use wildcopy.
-	 * First copy 16 bytes, because literals are likely short.
-	 */
-        assert(WILDCOPY_OVERLENGTH >= 16);
+         * First copy 16 bytes, because literals are likely short.
+         */
+        ZSTD_STATIC_ASSERT(WILDCOPY_OVERLENGTH >= 16);
         ZSTD_copy16(seqStorePtr->lit, literals);
         if (litLength > 16) {
             ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap);
@@ -626,96 +694,63 @@
     seqStorePtr->sequences[0].litLength = (U16)litLength;
 
     /* match offset */
-    seqStorePtr->sequences[0].offset = offCode + 1;
+    seqStorePtr->sequences[0].offBase = offBase;
 
     /* match Length */
-    if (mlBase>0xFFFF) {
-        assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */
-        seqStorePtr->longLengthType = ZSTD_llt_matchLength;
-        seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+    assert(matchLength >= MINMATCH);
+    {   size_t const mlBase = matchLength - MINMATCH;
+        if (mlBase>0xFFFF) {
+            assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */
+            seqStorePtr->longLengthType = ZSTD_llt_matchLength;
+            seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
+        }
+        seqStorePtr->sequences[0].mlBase = (U16)mlBase;
     }
-    seqStorePtr->sequences[0].matchLength = (U16)mlBase;
 
     seqStorePtr->sequences++;
 }
 
+/* ZSTD_updateRep() :
+ * updates in-place @rep (array of repeat offsets)
+ * @offBase : sum-type, using numeric representation of ZSTD_storeSeq()
+ */
+MEM_STATIC void
+ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0)
+{
+    if (OFFBASE_IS_OFFSET(offBase)) {  /* full offset */
+        rep[2] = rep[1];
+        rep[1] = rep[0];
+        rep[0] = OFFBASE_TO_OFFSET(offBase);
+    } else {   /* repcode */
+        U32 const repCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0;
+        if (repCode > 0) {  /* note : if repCode==0, no change */
+            U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
+            rep[2] = (repCode >= 2) ? rep[1] : rep[2];
+            rep[1] = rep[0];
+            rep[0] = currentOffset;
+        } else {   /* repCode == 0 */
+            /* nothing to do */
+        }
+    }
+}
+
+typedef struct repcodes_s {
+    U32 rep[3];
+} repcodes_t;
+
+MEM_STATIC repcodes_t
+ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0)
+{
+    repcodes_t newReps;
+    ZSTD_memcpy(&newReps, rep, sizeof(newReps));
+    ZSTD_updateRep(newReps.rep, offBase, ll0);
+    return newReps;
+}
+
 
 /*-*************************************
 *  Match length counter
 ***************************************/
-static unsigned ZSTD_NbCommonBytes (size_t val)
-{
-    if (MEM_isLittleEndian()) {
-        if (MEM_64bits()) {
-#       if defined(_MSC_VER) && defined(_WIN64)
-#           if STATIC_BMI2
-                return _tzcnt_u64(val) >> 3;
-#           else
-                unsigned long r = 0;
-                return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0;
-#           endif
-#       elif defined(__GNUC__) && (__GNUC__ >= 4)
-            return (__builtin_ctzll((U64)val) >> 3);
-#       else
-            static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
-                                                     0, 3, 1, 3, 1, 4, 2, 7,
-                                                     0, 2, 3, 6, 1, 5, 3, 5,
-                                                     1, 3, 4, 4, 2, 5, 6, 7,
-                                                     7, 0, 1, 2, 3, 3, 4, 6,
-                                                     2, 6, 5, 5, 3, 4, 5, 6,
-                                                     7, 1, 2, 4, 6, 4, 4, 5,
-                                                     7, 2, 6, 5, 7, 6, 7, 7 };
-            return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-#       endif
-        } else { /* 32 bits */
-#       if defined(_MSC_VER)
-            unsigned long r=0;
-            return _BitScanForward( &r, (U32)val ) ? (unsigned)(r >> 3) : 0;
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_ctz((U32)val) >> 3);
-#       else
-            static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
-                                                     3, 2, 2, 1, 3, 2, 0, 1,
-                                                     3, 3, 1, 2, 2, 2, 2, 0,
-                                                     3, 1, 2, 0, 1, 0, 1, 1 };
-            return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-#       endif
-        }
-    } else {  /* Big Endian CPU */
-        if (MEM_64bits()) {
-#       if defined(_MSC_VER) && defined(_WIN64)
-#           if STATIC_BMI2
-			    return _lzcnt_u64(val) >> 3;
-#           else
-			    unsigned long r = 0;
-			    return _BitScanReverse64(&r, (U64)val) ? (unsigned)(r >> 3) : 0;
-#           endif
-#       elif defined(__GNUC__) && (__GNUC__ >= 4)
-            return (__builtin_clzll(val) >> 3);
-#       else
-            unsigned r;
-            const unsigned n32 = sizeof(size_t)*4;   /* calculate this way due to compiler complaining in 32-bits mode */
-            if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
-            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
-            r += (!val);
-            return r;
-#       endif
-        } else { /* 32 bits */
-#       if defined(_MSC_VER)
-            unsigned long r = 0;
-            return _BitScanReverse( &r, (unsigned long)val ) ? (unsigned)(r >> 3) : 0;
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_clz((U32)val) >> 3);
-#       else
-            unsigned r;
-            if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
-            r += (!val);
-            return r;
-#       endif
-    }   }
-}
-
-
 MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit)
 {
     const BYTE* const pStart = pIn;
@@ -761,32 +796,43 @@
  *  Hashes
  ***************************************/
 static const U32 prime3bytes = 506832829U;
-static U32    ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes)  >> (32-h) ; }
-MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */
+static U32    ZSTD_hash3(U32 u, U32 h, U32 s) { assert(h <= 32); return (((u << (32-24)) * prime3bytes) ^ s)  >> (32-h) ; }
+MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h, 0); } /* only in zstd_opt.h */
+MEM_STATIC size_t ZSTD_hash3PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash3(MEM_readLE32(ptr), h, s); }
 
 static const U32 prime4bytes = 2654435761U;
-static U32    ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; }
-static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); }
+static U32    ZSTD_hash4(U32 u, U32 h, U32 s) { assert(h <= 32); return ((u * prime4bytes) ^ s) >> (32-h) ; }
+static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h, 0); }
+static size_t ZSTD_hash4PtrS(const void* ptr, U32 h, U32 s) { return ZSTD_hash4(MEM_readLE32(ptr), h, s); }
 
 static const U64 prime5bytes = 889523592379ULL;
-static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u  << (64-40)) * prime5bytes) >> (64-h)) ; }
-static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); }
+static size_t ZSTD_hash5(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u  << (64-40)) * prime5bytes) ^ s) >> (64-h)) ; }
+static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h, 0); }
+static size_t ZSTD_hash5PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash5(MEM_readLE64(p), h, s); }
 
 static const U64 prime6bytes = 227718039650203ULL;
-static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u  << (64-48)) * prime6bytes) >> (64-h)) ; }
-static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); }
+static size_t ZSTD_hash6(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u  << (64-48)) * prime6bytes) ^ s) >> (64-h)) ; }
+static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h, 0); }
+static size_t ZSTD_hash6PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash6(MEM_readLE64(p), h, s); }
 
 static const U64 prime7bytes = 58295818150454627ULL;
-static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u  << (64-56)) * prime7bytes) >> (64-h)) ; }
-static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); }
+static size_t ZSTD_hash7(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u  << (64-56)) * prime7bytes) ^ s) >> (64-h)) ; }
+static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h, 0); }
+static size_t ZSTD_hash7PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash7(MEM_readLE64(p), h, s); }
 
 static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL;
-static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
-static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
+static size_t ZSTD_hash8(U64 u, U32 h, U64 s) { assert(h <= 64); return (size_t)((((u) * prime8bytes)  ^ s) >> (64-h)) ; }
+static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h, 0); }
+static size_t ZSTD_hash8PtrS(const void* p, U32 h, U64 s) { return ZSTD_hash8(MEM_readLE64(p), h, s); }
+
 
 MEM_STATIC FORCE_INLINE_ATTR
 size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
 {
+    /* Although some of these hashes do support hBits up to 64, some do not.
+     * To be on the safe side, always avoid hBits > 32. */
+    assert(hBits <= 32);
+
     switch(mls)
     {
     default:
@@ -798,6 +844,24 @@
     }
 }
 
+MEM_STATIC FORCE_INLINE_ATTR
+size_t ZSTD_hashPtrSalted(const void* p, U32 hBits, U32 mls, const U64 hashSalt) {
+    /* Although some of these hashes do support hBits up to 64, some do not.
+     * To be on the safe side, always avoid hBits > 32. */
+    assert(hBits <= 32);
+
+    switch(mls)
+    {
+        default:
+        case 4: return ZSTD_hash4PtrS(p, hBits, (U32)hashSalt);
+        case 5: return ZSTD_hash5PtrS(p, hBits, hashSalt);
+        case 6: return ZSTD_hash6PtrS(p, hBits, hashSalt);
+        case 7: return ZSTD_hash7PtrS(p, hBits, hashSalt);
+        case 8: return ZSTD_hash8PtrS(p, hBits, hashSalt);
+    }
+}
+
+
 /** ZSTD_ipow() :
  * Return base^exponent.
  */
@@ -884,9 +948,9 @@
 
 MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window)
 {
-    return window.dictLimit == 1 &&
-           window.lowLimit == 1 &&
-           (window.nextSrc - window.base) == 1;
+    return window.dictLimit == ZSTD_WINDOW_START_INDEX &&
+           window.lowLimit == ZSTD_WINDOW_START_INDEX &&
+           (window.nextSrc - window.base) == ZSTD_WINDOW_START_INDEX;
 }
 
 /**
@@ -937,7 +1001,9 @@
 {
     U32 const cycleSize = 1u << cycleLog;
     U32 const curr = (U32)((BYTE const*)src - window.base);
-    U32 const minIndexToOverflowCorrect = cycleSize + MAX(maxDist, cycleSize);
+    U32 const minIndexToOverflowCorrect = cycleSize
+                                        + MAX(maxDist, cycleSize)
+                                        + ZSTD_WINDOW_START_INDEX;
 
     /* Adjust the min index to backoff the overflow correction frequency,
      * so we don't waste too much CPU in overflow correction. If this
@@ -1012,10 +1078,14 @@
     U32 const cycleSize = 1u << cycleLog;
     U32 const cycleMask = cycleSize - 1;
     U32 const curr = (U32)((BYTE const*)src - window->base);
-    U32 const currentCycle0 = curr & cycleMask;
-    /* Exclude zero so that newCurrent - maxDist >= 1. */
-    U32 const currentCycle1 = currentCycle0 == 0 ? cycleSize : currentCycle0;
-    U32 const newCurrent = currentCycle1 + MAX(maxDist, cycleSize);
+    U32 const currentCycle = curr & cycleMask;
+    /* Ensure newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX. */
+    U32 const currentCycleCorrection = currentCycle < ZSTD_WINDOW_START_INDEX
+                                     ? MAX(cycleSize, ZSTD_WINDOW_START_INDEX)
+                                     : 0;
+    U32 const newCurrent = currentCycle
+                         + currentCycleCorrection
+                         + MAX(maxDist, cycleSize);
     U32 const correction = curr - newCurrent;
     /* maxDist must be a power of two so that:
      *   (newCurrent & cycleMask) == (curr & cycleMask)
@@ -1031,14 +1101,20 @@
 
     window->base += correction;
     window->dictBase += correction;
-    if (window->lowLimit <= correction) window->lowLimit = 1;
-    else window->lowLimit -= correction;
-    if (window->dictLimit <= correction) window->dictLimit = 1;
-    else window->dictLimit -= correction;
+    if (window->lowLimit < correction + ZSTD_WINDOW_START_INDEX) {
+        window->lowLimit = ZSTD_WINDOW_START_INDEX;
+    } else {
+        window->lowLimit -= correction;
+    }
+    if (window->dictLimit < correction + ZSTD_WINDOW_START_INDEX) {
+        window->dictLimit = ZSTD_WINDOW_START_INDEX;
+    } else {
+        window->dictLimit -= correction;
+    }
 
     /* Ensure we can still reference the full window. */
     assert(newCurrent >= maxDist);
-    assert(newCurrent - maxDist >= 1);
+    assert(newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX);
     /* Ensure that lowLimit and dictLimit didn't underflow. */
     assert(window->lowLimit <= newCurrent);
     assert(window->dictLimit <= newCurrent);
@@ -1133,10 +1209,15 @@
                     (unsigned)blockEndIdx, (unsigned)maxDist, (unsigned)loadedDictEnd);
         assert(blockEndIdx >= loadedDictEnd);
 
-        if (blockEndIdx > loadedDictEnd + maxDist) {
+        if (blockEndIdx > loadedDictEnd + maxDist || loadedDictEnd != window->dictLimit) {
             /* On reaching window size, dictionaries are invalidated.
              * For simplification, if window size is reached anywhere within next block,
              * the dictionary is invalidated for the full block.
+             *
+             * We also have to invalidate the dictionary if ZSTD_window_update() has detected
+             * non-contiguous segments, which means that loadedDictEnd != window->dictLimit.
+             * loadedDictEnd may be 0, if forceWindow is true, but in that case we never use
+             * dictMatchState, so setting it to NULL is not a problem.
              */
             DEBUGLOG(6, "invalidating dictionary for current block (distance > windowSize)");
             *loadedDictEndPtr = 0;
@@ -1149,11 +1230,12 @@
 
 MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) {
     ZSTD_memset(window, 0, sizeof(*window));
-    window->base = (BYTE const*)"";
-    window->dictBase = (BYTE const*)"";
-    window->dictLimit = 1;    /* start from 1, so that 1st position is valid */
-    window->lowLimit = 1;     /* it ensures first and later CCtx usages compress the same */
-    window->nextSrc = window->base + 1;   /* see issue #1241 */
+    window->base = (BYTE const*)" ";
+    window->dictBase = (BYTE const*)" ";
+    ZSTD_STATIC_ASSERT(ZSTD_DUBT_UNSORTED_MARK < ZSTD_WINDOW_START_INDEX); /* Start above ZSTD_DUBT_UNSORTED_MARK */
+    window->dictLimit = ZSTD_WINDOW_START_INDEX;    /* start from >0, so that 1st position is valid */
+    window->lowLimit = ZSTD_WINDOW_START_INDEX;     /* it ensures first and later CCtx usages compress the same */
+    window->nextSrc = window->base + ZSTD_WINDOW_START_INDEX;   /* see issue #1241 */
     window->nbOverflowCorrections = 0;
 }
 
@@ -1206,15 +1288,15 @@
  */
 MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 curr, unsigned windowLog)
 {
-    U32    const maxDistance = 1U << windowLog;
-    U32    const lowestValid = ms->window.lowLimit;
-    U32    const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
-    U32    const isDictionary = (ms->loadedDictEnd != 0);
+    U32 const maxDistance = 1U << windowLog;
+    U32 const lowestValid = ms->window.lowLimit;
+    U32 const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
+    U32 const isDictionary = (ms->loadedDictEnd != 0);
     /* When using a dictionary the entire dictionary is valid if a single byte of the dictionary
      * is within the window. We invalidate the dictionary (and set loadedDictEnd to 0) when it isn't
      * valid for the entire block. So this check is sufficient to find the lowest valid match index.
      */
-    U32    const matchLowest = isDictionary ? lowestValid : withinWindow;
+    U32 const matchLowest = isDictionary ? lowestValid : withinWindow;
     return matchLowest;
 }
 
@@ -1267,6 +1349,42 @@
 
 #endif
 
+/* Short Cache */
+
+/* Normally, zstd matchfinders follow this flow:
+ *     1. Compute hash at ip
+ *     2. Load index from hashTable[hash]
+ *     3. Check if *ip == *(base + index)
+ * In dictionary compression, loading *(base + index) is often an L2 or even L3 miss.
+ *
+ * Short cache is an optimization which allows us to avoid step 3 most of the time
+ * when the data doesn't actually match. With short cache, the flow becomes:
+ *     1. Compute (hash, currentTag) at ip. currentTag is an 8-bit independent hash at ip.
+ *     2. Load (index, matchTag) from hashTable[hash]. See ZSTD_writeTaggedIndex to understand how this works.
+ *     3. Only if currentTag == matchTag, check *ip == *(base + index). Otherwise, continue.
+ *
+ * Currently, short cache is only implemented in CDict hashtables. Thus, its use is limited to
+ * dictMatchState matchfinders.
+ */
+#define ZSTD_SHORT_CACHE_TAG_BITS 8
+#define ZSTD_SHORT_CACHE_TAG_MASK ((1u << ZSTD_SHORT_CACHE_TAG_BITS) - 1)
+
+/* Helper function for ZSTD_fillHashTable and ZSTD_fillDoubleHashTable.
+ * Unpacks hashAndTag into (hash, tag), then packs (index, tag) into hashTable[hash]. */
+MEM_STATIC void ZSTD_writeTaggedIndex(U32* const hashTable, size_t hashAndTag, U32 index) {
+    size_t const hash = hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS;
+    U32 const tag = (U32)(hashAndTag & ZSTD_SHORT_CACHE_TAG_MASK);
+    assert(index >> (32 - ZSTD_SHORT_CACHE_TAG_BITS) == 0);
+    hashTable[hash] = (index << ZSTD_SHORT_CACHE_TAG_BITS) | tag;
+}
+
+/* Helper function for short cache matchfinders.
+ * Unpacks tag1 and tag2 from lower bits of packedTag1 and packedTag2, then checks if the tags match. */
+MEM_STATIC int ZSTD_comparePackedTags(size_t packedTag1, size_t packedTag2) {
+    U32 const tag1 = packedTag1 & ZSTD_SHORT_CACHE_TAG_MASK;
+    U32 const tag2 = packedTag2 & ZSTD_SHORT_CACHE_TAG_MASK;
+    return tag1 == tag2;
+}
 
 #if defined (__cplusplus)
 }
@@ -1364,4 +1482,51 @@
  */
 void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize);
 
+/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of
+ * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter.
+ * Note that the block delimiter must include the last literals of the block.
+ */
+size_t
+ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
+                                              ZSTD_sequencePosition* seqPos,
+                                        const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                        const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch);
+
+/* Returns the number of bytes to move the current read position back by.
+ * Only non-zero if we ended up splitting a sequence.
+ * Otherwise, it may return a ZSTD error if something went wrong.
+ *
+ * This function will attempt to scan through blockSize bytes
+ * represented by the sequences in @inSeqs,
+ * storing any (partial) sequences.
+ *
+ * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to
+ * avoid splitting a match, or to avoid splitting a match such that it would produce a match
+ * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block.
+ */
+size_t
+ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
+                                   const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                   const void* src, size_t blockSize, ZSTD_paramSwitch_e externalRepSearch);
+
+
+/* ===============================================================
+ * Deprecated definitions that are still used internally to avoid
+ * deprecation warnings. These functions are exactly equivalent to
+ * their public variants, but avoid the deprecation warnings.
+ * =============================================================== */
+
+size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
+
+size_t ZSTD_compressContinue_public(ZSTD_CCtx* cctx,
+                                    void* dst, size_t dstCapacity,
+                              const void* src, size_t srcSize);
+
+size_t ZSTD_compressEnd_public(ZSTD_CCtx* cctx,
+                               void* dst, size_t dstCapacity,
+                         const void* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_deprecated(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+
+
 #endif /* ZSTD_COMPRESS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
index 008337b..bfd4f11 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -13,11 +13,36 @@
  ***************************************/
 #include "zstd_compress_literals.h"
 
+
+/* **************************************************************
+*  Debug Traces
+****************************************************************/
+#if DEBUGLEVEL >= 2
+
+static size_t showHexa(const void* src, size_t srcSize)
+{
+    const BYTE* const ip = (const BYTE*)src;
+    size_t u;
+    for (u=0; u<srcSize; u++) {
+        RAWLOG(5, " %02X", ip[u]); (void)ip;
+    }
+    RAWLOG(5, " \n");
+    return srcSize;
+}
+
+#endif
+
+
+/* **************************************************************
+*  Literals compression - special cases
+****************************************************************/
 size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
     BYTE* const ostart = (BYTE*)dst;
     U32   const flSize = 1 + (srcSize>31) + (srcSize>4095);
 
+    DEBUGLOG(5, "ZSTD_noCompressLiterals: srcSize=%zu, dstCapacity=%zu", srcSize, dstCapacity);
+
     RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, "");
 
     switch(flSize)
@@ -36,16 +61,30 @@
     }
 
     ZSTD_memcpy(ostart + flSize, src, srcSize);
-    DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize));
+    DEBUGLOG(5, "Raw (uncompressed) literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize));
     return srcSize + flSize;
 }
 
+static int allBytesIdentical(const void* src, size_t srcSize)
+{
+    assert(srcSize >= 1);
+    assert(src != NULL);
+    {   const BYTE b = ((const BYTE*)src)[0];
+        size_t p;
+        for (p=1; p<srcSize; p++) {
+            if (((const BYTE*)src)[p] != b) return 0;
+        }
+        return 1;
+    }
+}
+
 size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
     BYTE* const ostart = (BYTE*)dst;
     U32   const flSize = 1 + (srcSize>31) + (srcSize>4095);
 
-    (void)dstCapacity;  /* dstCapacity already guaranteed to be >=4, hence large enough */
+    assert(dstCapacity >= 4); (void)dstCapacity;
+    assert(allBytesIdentical(src, srcSize));
 
     switch(flSize)
     {
@@ -63,27 +102,51 @@
     }
 
     ostart[flSize] = *(const BYTE*)src;
-    DEBUGLOG(5, "RLE literals: %u -> %u", (U32)srcSize, (U32)flSize + 1);
+    DEBUGLOG(5, "RLE : Repeated Literal (%02X: %u times) -> %u bytes encoded", ((const BYTE*)src)[0], (U32)srcSize, (U32)flSize + 1);
     return flSize+1;
 }
 
-size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf,
-                              ZSTD_hufCTables_t* nextHuf,
-                              ZSTD_strategy strategy, int disableLiteralCompression,
-                              void* dst, size_t dstCapacity,
-                        const void* src, size_t srcSize,
-                              void* entropyWorkspace, size_t entropyWorkspaceSize,
-                        const int bmi2)
+/* ZSTD_minLiteralsToCompress() :
+ * returns minimal amount of literals
+ * for literal compression to even be attempted.
+ * Minimum is made tighter as compression strategy increases.
+ */
+static size_t
+ZSTD_minLiteralsToCompress(ZSTD_strategy strategy, HUF_repeat huf_repeat)
 {
-    size_t const minGain = ZSTD_minGain(srcSize, strategy);
+    assert((int)strategy >= 0);
+    assert((int)strategy <= 9);
+    /* btultra2 : min 8 bytes;
+     * then 2x larger for each successive compression strategy
+     * max threshold 64 bytes */
+    {   int const shift = MIN(9-(int)strategy, 3);
+        size_t const mintc = (huf_repeat == HUF_repeat_valid) ? 6 : (size_t)8 << shift;
+        DEBUGLOG(7, "minLiteralsToCompress = %zu", mintc);
+        return mintc;
+    }
+}
+
+size_t ZSTD_compressLiterals (
+                  void* dst, size_t dstCapacity,
+            const void* src, size_t srcSize,
+                  void* entropyWorkspace, size_t entropyWorkspaceSize,
+            const ZSTD_hufCTables_t* prevHuf,
+                  ZSTD_hufCTables_t* nextHuf,
+                  ZSTD_strategy strategy,
+                  int disableLiteralCompression,
+                  int suspectUncompressible,
+                  int bmi2)
+{
     size_t const lhSize = 3 + (srcSize >= 1 KB) + (srcSize >= 16 KB);
     BYTE*  const ostart = (BYTE*)dst;
     U32 singleStream = srcSize < 256;
     symbolEncodingType_e hType = set_compressed;
     size_t cLitSize;
 
-    DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i srcSize=%u)",
-                disableLiteralCompression, (U32)srcSize);
+    DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i, srcSize=%u, dstCapacity=%zu)",
+                disableLiteralCompression, (U32)srcSize, dstCapacity);
+
+    DEBUGLOG(6, "Completed literals listing (%zu bytes)", showHexa(src, srcSize));
 
     /* Prepare nextEntropy assuming reusing the existing table */
     ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
@@ -91,40 +154,51 @@
     if (disableLiteralCompression)
         return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
 
-    /* small ? don't even attempt compression (speed opt) */
-#   define COMPRESS_LITERALS_SIZE_MIN 63
-    {   size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
-        if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
-    }
+    /* if too small, don't even attempt compression (speed opt) */
+    if (srcSize < ZSTD_minLiteralsToCompress(strategy, prevHuf->repeatMode))
+        return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
 
     RETURN_ERROR_IF(dstCapacity < lhSize+1, dstSize_tooSmall, "not enough space for compression");
     {   HUF_repeat repeat = prevHuf->repeatMode;
-        int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0;
+        int const flags = 0
+            | (bmi2 ? HUF_flags_bmi2 : 0)
+            | (strategy < ZSTD_lazy && srcSize <= 1024 ? HUF_flags_preferRepeat : 0)
+            | (strategy >= HUF_OPTIMAL_DEPTH_THRESHOLD ? HUF_flags_optimalDepth : 0)
+            | (suspectUncompressible ? HUF_flags_suspectUncompressible : 0);
+
+        typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int);
+        huf_compress_f huf_compress;
         if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1;
-        cLitSize = singleStream ?
-            HUF_compress1X_repeat(
-                ostart+lhSize, dstCapacity-lhSize, src, srcSize,
-                HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize,
-                (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2) :
-            HUF_compress4X_repeat(
-                ostart+lhSize, dstCapacity-lhSize, src, srcSize,
-                HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize,
-                (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2);
+        huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat;
+        cLitSize = huf_compress(ostart+lhSize, dstCapacity-lhSize,
+                                src, srcSize,
+                                HUF_SYMBOLVALUE_MAX, LitHufLog,
+                                entropyWorkspace, entropyWorkspaceSize,
+                                (HUF_CElt*)nextHuf->CTable,
+                                &repeat, flags);
+        DEBUGLOG(5, "%zu literals compressed into %zu bytes (before header)", srcSize, cLitSize);
         if (repeat != HUF_repeat_none) {
             /* reused the existing table */
-            DEBUGLOG(5, "Reusing previous huffman table");
+            DEBUGLOG(5, "reusing statistics from previous huffman block");
             hType = set_repeat;
         }
     }
 
-    if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) {
-        ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-        return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
-    }
+    {   size_t const minGain = ZSTD_minGain(srcSize, strategy);
+        if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) {
+            ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+            return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
+    }   }
     if (cLitSize==1) {
-        ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-        return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
-    }
+        /* A return value of 1 signals that the alphabet consists of a single symbol.
+         * However, in some rare circumstances, it could be the compressed size (a single byte).
+         * For that outcome to have a chance to happen, it's necessary that `srcSize < 8`.
+         * (it's also necessary to not generate statistics).
+         * Therefore, in such a case, actively check that all bytes are identical. */
+        if ((srcSize >= 8) || allBytesIdentical(src, srcSize)) {
+            ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+            return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
+    }   }
 
     if (hType == set_compressed) {
         /* using a newly constructed table */
@@ -135,16 +209,19 @@
     switch(lhSize)
     {
     case 3: /* 2 - 2 - 10 - 10 */
-        {   U32 const lhc = hType + ((!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14);
+        if (!singleStream) assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS);
+        {   U32 const lhc = hType + ((U32)(!singleStream) << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<14);
             MEM_writeLE24(ostart, lhc);
             break;
         }
     case 4: /* 2 - 2 - 14 - 14 */
+        assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS);
         {   U32 const lhc = hType + (2 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<18);
             MEM_writeLE32(ostart, lhc);
             break;
         }
     case 5: /* 2 - 2 - 18 - 18 */
+        assert(srcSize >= MIN_LITERALS_FOR_4_STREAMS);
         {   U32 const lhc = hType + (3 << 2) + ((U32)srcSize<<4) + ((U32)cLitSize<<22);
             MEM_writeLE32(ostart, lhc);
             ostart[4] = (BYTE)(cLitSize >> 10);
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
index 9904c0c..b060c8a 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -16,14 +16,24 @@
 
 size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 
+/* ZSTD_compressRleLiteralsBlock() :
+ * Conditions :
+ * - All bytes in @src are identical
+ * - dstCapacity >= 4 */
 size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 
-size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf,
-                              ZSTD_hufCTables_t* nextHuf,
-                              ZSTD_strategy strategy, int disableLiteralCompression,
-                              void* dst, size_t dstCapacity,
+/* ZSTD_compressLiterals():
+ * @entropyWorkspace: must be aligned on 4-bytes boundaries
+ * @entropyWorkspaceSize : must be >= HUF_WORKSPACE_SIZE
+ * @suspectUncompressible: sampling checks, to potentially skip huffman coding
+ */
+size_t ZSTD_compressLiterals (void* dst, size_t dstCapacity,
                         const void* src, size_t srcSize,
                               void* entropyWorkspace, size_t entropyWorkspaceSize,
-                        const int bmi2);
+                        const ZSTD_hufCTables_t* prevHuf,
+                              ZSTD_hufCTables_t* nextHuf,
+                              ZSTD_strategy strategy, int disableLiteralCompression,
+                              int suspectUncompressible,
+                              int bmi2);
 
 #endif /* ZSTD_COMPRESS_LITERALS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
index 611eabd..8872d4d 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -58,7 +58,7 @@
 {
     /* Heuristic: This should cover most blocks <= 16K and
      * start to fade out after 16K to about 32K depending on
-     * comprssibility.
+     * compressibility.
      */
     return nbSeq >= 2048;
 }
@@ -166,7 +166,7 @@
     if (mostFrequent == nbSeq) {
         *repeatMode = FSE_repeat_none;
         if (isDefaultAllowed && nbSeq <= 2) {
-            /* Prefer set_basic over set_rle when there are 2 or less symbols,
+            /* Prefer set_basic over set_rle when there are 2 or fewer symbols,
              * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol.
              * If basic encoding isn't possible, always choose RLE.
              */
@@ -275,10 +275,11 @@
         assert(nbSeq_1 > 1);
         assert(entropyWorkspaceSize >= sizeof(ZSTD_BuildCTableWksp));
         (void)entropyWorkspaceSize;
-        FORWARD_IF_ERROR(FSE_normalizeCount(wksp->norm, tableLog, count, nbSeq_1, max, ZSTD_useLowProbCount(nbSeq_1)), "");
-        {   size_t const NCountSize = FSE_writeNCount(op, oend - op, wksp->norm, max, tableLog);   /* overflow protected */
+        FORWARD_IF_ERROR(FSE_normalizeCount(wksp->norm, tableLog, count, nbSeq_1, max, ZSTD_useLowProbCount(nbSeq_1)), "FSE_normalizeCount failed");
+        assert(oend >= op);
+        {   size_t const NCountSize = FSE_writeNCount(op, (size_t)(oend - op), wksp->norm, max, tableLog);   /* overflow protected */
             FORWARD_IF_ERROR(NCountSize, "FSE_writeNCount failed");
-            FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, wksp->norm, max, tableLog, wksp->wksp, sizeof(wksp->wksp)), "");
+            FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, wksp->norm, max, tableLog, wksp->wksp, sizeof(wksp->wksp)), "FSE_buildCTable_wksp failed");
             return NCountSize;
         }
     }
@@ -312,19 +313,19 @@
     FSE_initCState2(&stateLitLength,   CTable_LitLength,   llCodeTable[nbSeq-1]);
     BIT_addBits(&blockStream, sequences[nbSeq-1].litLength, LL_bits[llCodeTable[nbSeq-1]]);
     if (MEM_32bits()) BIT_flushBits(&blockStream);
-    BIT_addBits(&blockStream, sequences[nbSeq-1].matchLength, ML_bits[mlCodeTable[nbSeq-1]]);
+    BIT_addBits(&blockStream, sequences[nbSeq-1].mlBase, ML_bits[mlCodeTable[nbSeq-1]]);
     if (MEM_32bits()) BIT_flushBits(&blockStream);
     if (longOffsets) {
         U32 const ofBits = ofCodeTable[nbSeq-1];
         unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);
         if (extraBits) {
-            BIT_addBits(&blockStream, sequences[nbSeq-1].offset, extraBits);
+            BIT_addBits(&blockStream, sequences[nbSeq-1].offBase, extraBits);
             BIT_flushBits(&blockStream);
         }
-        BIT_addBits(&blockStream, sequences[nbSeq-1].offset >> extraBits,
+        BIT_addBits(&blockStream, sequences[nbSeq-1].offBase >> extraBits,
                     ofBits - extraBits);
     } else {
-        BIT_addBits(&blockStream, sequences[nbSeq-1].offset, ofCodeTable[nbSeq-1]);
+        BIT_addBits(&blockStream, sequences[nbSeq-1].offBase, ofCodeTable[nbSeq-1]);
     }
     BIT_flushBits(&blockStream);
 
@@ -338,8 +339,8 @@
             U32  const mlBits = ML_bits[mlCode];
             DEBUGLOG(6, "encoding: litlen:%2u - matchlen:%2u - offCode:%7u",
                         (unsigned)sequences[n].litLength,
-                        (unsigned)sequences[n].matchLength + MINMATCH,
-                        (unsigned)sequences[n].offset);
+                        (unsigned)sequences[n].mlBase + MINMATCH,
+                        (unsigned)sequences[n].offBase);
                                                                             /* 32b*/  /* 64b*/
                                                                             /* (7)*/  /* (7)*/
             FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode);       /* 15 */  /* 15 */
@@ -350,18 +351,18 @@
                 BIT_flushBits(&blockStream);                                /* (7)*/
             BIT_addBits(&blockStream, sequences[n].litLength, llBits);
             if (MEM_32bits() && ((llBits+mlBits)>24)) BIT_flushBits(&blockStream);
-            BIT_addBits(&blockStream, sequences[n].matchLength, mlBits);
+            BIT_addBits(&blockStream, sequences[n].mlBase, mlBits);
             if (MEM_32bits() || (ofBits+mlBits+llBits > 56)) BIT_flushBits(&blockStream);
             if (longOffsets) {
                 unsigned const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN-1);
                 if (extraBits) {
-                    BIT_addBits(&blockStream, sequences[n].offset, extraBits);
+                    BIT_addBits(&blockStream, sequences[n].offBase, extraBits);
                     BIT_flushBits(&blockStream);                            /* (7)*/
                 }
-                BIT_addBits(&blockStream, sequences[n].offset >> extraBits,
+                BIT_addBits(&blockStream, sequences[n].offBase >> extraBits,
                             ofBits - extraBits);                            /* 31 */
             } else {
-                BIT_addBits(&blockStream, sequences[n].offset, ofBits);     /* 31 */
+                BIT_addBits(&blockStream, sequences[n].offBase, ofBits);     /* 31 */
             }
             BIT_flushBits(&blockStream);                                    /* (7)*/
             DEBUGLOG(7, "remaining space : %i", (int)(blockStream.endPtr - blockStream.ptr));
@@ -398,7 +399,7 @@
 
 #if DYNAMIC_BMI2
 
-static TARGET_ATTRIBUTE("bmi2") size_t
+static BMI2_TARGET_ATTRIBUTE size_t
 ZSTD_encodeSequences_bmi2(
             void* dst, size_t dstCapacity,
             FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable,
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
index 7991364..4a3a05d 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
index e4e4506..638c4ac 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -36,13 +36,14 @@
  *      If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block
  *      and the following sub-blocks' literals sections will be Treeless_Literals_Block.
  *  @return : compressed size of literals section of a sub-block
- *            Or 0 if it unable to compress.
+ *            Or 0 if unable to compress.
  *            Or error code */
-static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
-                                    const ZSTD_hufCTablesMetadata_t* hufMetadata,
-                                    const BYTE* literals, size_t litSize,
-                                    void* dst, size_t dstSize,
-                                    const int bmi2, int writeEntropy, int* entropyWritten)
+static size_t
+ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
+                              const ZSTD_hufCTablesMetadata_t* hufMetadata,
+                              const BYTE* literals, size_t litSize,
+                              void* dst, size_t dstSize,
+                              const int bmi2, int writeEntropy, int* entropyWritten)
 {
     size_t const header = writeEntropy ? 200 : 0;
     size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header));
@@ -53,8 +54,6 @@
     symbolEncodingType_e hType = writeEntropy ? hufMetadata->hType : set_repeat;
     size_t cLitSize = 0;
 
-    (void)bmi2; /* TODO bmi2... */
-
     DEBUGLOG(5, "ZSTD_compressSubBlock_literal (litSize=%zu, lhSize=%zu, writeEntropy=%d)", litSize, lhSize, writeEntropy);
 
     *entropyWritten = 0;
@@ -76,9 +75,9 @@
         DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize);
     }
 
-    /* TODO bmi2 */
-    {   const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable)
-                                          : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable);
+    {   int const flags = bmi2 ? HUF_flags_bmi2 : 0;
+        const size_t cSize = singleStream ? HUF_compress1X_usingCTable(op, oend-op, literals, litSize, hufTable, flags)
+                                          : HUF_compress4X_usingCTable(op, oend-op, literals, litSize, hufTable, flags);
         op += cSize;
         cLitSize += cSize;
         if (cSize == 0 || ERR_isError(cSize)) {
@@ -126,12 +125,17 @@
     return op-ostart;
 }
 
-static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* sequences, size_t nbSeq, size_t litSize, int lastSequence) {
+static size_t
+ZSTD_seqDecompressedSize(seqStore_t const* seqStore,
+                   const seqDef* sequences, size_t nbSeq,
+                         size_t litSize, int lastSequence)
+{
     const seqDef* const sstart = sequences;
     const seqDef* const send = sequences + nbSeq;
     const seqDef* sp = sstart;
     size_t matchLengthSum = 0;
     size_t litLengthSum = 0;
+    (void)(litLengthSum); /* suppress unused variable warning on some environments */
     while (send-sp > 0) {
         ZSTD_sequenceLength const seqLen = ZSTD_getSequenceLength(seqStore, sp);
         litLengthSum += seqLen.litLength;
@@ -155,13 +159,14 @@
  *  @return : compressed size of sequences section of a sub-block
  *            Or 0 if it is unable to compress
  *            Or error code. */
-static size_t ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables,
-                                              const ZSTD_fseCTablesMetadata_t* fseMetadata,
-                                              const seqDef* sequences, size_t nbSeq,
-                                              const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode,
-                                              const ZSTD_CCtx_params* cctxParams,
-                                              void* dst, size_t dstCapacity,
-                                              const int bmi2, int writeEntropy, int* entropyWritten)
+static size_t
+ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables,
+                                const ZSTD_fseCTablesMetadata_t* fseMetadata,
+                                const seqDef* sequences, size_t nbSeq,
+                                const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode,
+                                const ZSTD_CCtx_params* cctxParams,
+                                void* dst, size_t dstCapacity,
+                                const int bmi2, int writeEntropy, int* entropyWritten)
 {
     const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
     BYTE* const ostart = (BYTE*)dst;
@@ -324,7 +329,7 @@
 static size_t ZSTD_estimateSubBlockSize_symbolType(symbolEncodingType_e type,
                         const BYTE* codeTable, unsigned maxCode,
                         size_t nbSeq, const FSE_CTable* fseCTable,
-                        const U32* additionalBits,
+                        const U8* additionalBits,
                         short const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
                         void* workspace, size_t wkspSize)
 {
@@ -474,7 +479,7 @@
         /* I think there is an optimization opportunity here.
          * Calling ZSTD_estimateSubBlockSize for every sequence can be wasteful
          * since it recalculates estimate from scratch.
-         * For example, it would recount literal distribution and symbol codes everytime.
+         * For example, it would recount literal distribution and symbol codes every time.
          */
         cBlockSizeEstimate = ZSTD_estimateSubBlockSize(lp, litSize, ofCodePtr, llCodePtr, mlCodePtr, seqCount,
                                                        &nextCBlock->entropy, entropyMetadata,
@@ -538,7 +543,7 @@
             repcodes_t rep;
             ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep));
             for (seq = sstart; seq < sp; ++seq) {
-                rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);
+                ZSTD_updateRep(rep.rep, seq->offBase, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);
             }
             ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep));
         }
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
index 176f9b1..8e494f0 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_cwksp.h b/Utilities/cmzstd/lib/compress/zstd_cwksp.h
index 2656d26..cc7fb1c 100644
--- a/Utilities/cmzstd/lib/compress/zstd_cwksp.h
+++ b/Utilities/cmzstd/lib/compress/zstd_cwksp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,7 +14,9 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
+#include "../common/allocations.h"  /* ZSTD_customMalloc, ZSTD_customFree */
 #include "../common/zstd_internal.h"
+#include "../common/portability_macros.h"
 
 #if defined (__cplusplus)
 extern "C" {
@@ -44,8 +46,9 @@
 ***************************************/
 typedef enum {
     ZSTD_cwksp_alloc_objects,
-    ZSTD_cwksp_alloc_buffers,
-    ZSTD_cwksp_alloc_aligned
+    ZSTD_cwksp_alloc_aligned_init_once,
+    ZSTD_cwksp_alloc_aligned,
+    ZSTD_cwksp_alloc_buffers
 } ZSTD_cwksp_alloc_phase_e;
 
 /**
@@ -98,8 +101,8 @@
  *
  * Workspace Layout:
  *
- * [                        ... workspace ...                         ]
- * [objects][tables ... ->] free space [<- ... aligned][<- ... buffers]
+ * [                        ... workspace ...                           ]
+ * [objects][tables ->] free space [<- buffers][<- aligned][<- init once]
  *
  * The various objects that live in the workspace are divided into the
  * following categories, and are allocated separately:
@@ -123,9 +126,18 @@
  *   uint32_t arrays, all of whose values are between 0 and (nextSrc - base).
  *   Their sizes depend on the cparams. These tables are 64-byte aligned.
  *
- * - Aligned: these buffers are used for various purposes that require 4 byte
- *   alignment, but don't require any initialization before they're used. These
- *   buffers are each aligned to 64 bytes.
+ * - Init once: these buffers require to be initialized at least once before
+ *   use. They should be used when we want to skip memory initialization
+ *   while not triggering memory checkers (like Valgrind) when reading from
+ *   from this memory without writing to it first.
+ *   These buffers should be used carefully as they might contain data
+ *   from previous compressions.
+ *   Buffers are aligned to 64 bytes.
+ *
+ * - Aligned: these buffers don't require any initialization before they're
+ *   used. The user of the buffer should make sure they write into a buffer
+ *   location before reading from it.
+ *   Buffers are aligned to 64 bytes.
  *
  * - Buffers: these buffers are used for various purposes that don't require
  *   any alignment or initialization before they're used. This means they can
@@ -137,8 +149,9 @@
  * correctly packed into the workspace buffer. That order is:
  *
  * 1. Objects
- * 2. Buffers
- * 3. Aligned/Tables
+ * 2. Init once / Tables
+ * 3. Aligned / Tables
+ * 4. Buffers / Tables
  *
  * Attempts to reserve objects of different types out of order will fail.
  */
@@ -150,6 +163,7 @@
     void* tableEnd;
     void* tableValidEnd;
     void* allocStart;
+    void* initOnceStart;
 
     BYTE allocFailed;
     int workspaceOversizedDuration;
@@ -162,6 +176,7 @@
 ***************************************/
 
 MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws);
+MEM_STATIC void*  ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws);
 
 MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) {
     (void)ws;
@@ -171,6 +186,20 @@
     assert(ws->tableEnd <= ws->allocStart);
     assert(ws->tableValidEnd <= ws->allocStart);
     assert(ws->allocStart <= ws->workspaceEnd);
+    assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws));
+    assert(ws->workspace <= ws->initOnceStart);
+#if ZSTD_MEMORY_SANITIZER
+    {
+        intptr_t const offset = __msan_test_shadow(ws->initOnceStart,
+            (U8*)ZSTD_cwksp_initialAllocStart(ws) - (U8*)ws->initOnceStart);
+#if defined(ZSTD_MSAN_PRINT)
+        if(offset!=-1) {
+            __msan_print_shadow((U8*)ws->initOnceStart + offset - 8, 32);
+        }
+#endif
+        assert(offset==-1);
+    };
+#endif
 }
 
 /**
@@ -217,14 +246,10 @@
  * for internal purposes (currently only alignment).
  */
 MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) {
-    /* For alignment, the wksp will always allocate an additional n_1=[1, 64] bytes
-     * to align the beginning of tables section, as well as another n_2=[0, 63] bytes
-     * to align the beginning of the aligned secion.
-     *
-     * n_1 + n_2 == 64 bytes if the cwksp is freshly allocated, due to tables and
-     * aligneds being sized in multiples of 64 bytes.
+    /* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES
+     * bytes to align the beginning of tables section and end of buffers;
      */
-    size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES;
+    size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES * 2;
     return slackSpace;
 }
 
@@ -237,18 +262,28 @@
     size_t const alignBytesMask = alignBytes - 1;
     size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask;
     assert((alignBytes & alignBytesMask) == 0);
-    assert(bytes != ZSTD_CWKSP_ALIGNMENT_BYTES);
+    assert(bytes < alignBytes);
     return bytes;
 }
 
 /**
+ * Returns the initial value for allocStart which is used to determine the position from
+ * which we can allocate from the end of the workspace.
+ */
+MEM_STATIC void*  ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws) {
+    return (void*)((size_t)ws->workspaceEnd & ~(ZSTD_CWKSP_ALIGNMENT_BYTES-1));
+}
+
+/**
  * Internal function. Do not use directly.
- * Reserves the given number of bytes within the aligned/buffer segment of the wksp, which
- * counts from the end of the wksp. (as opposed to the object/table segment)
+ * Reserves the given number of bytes within the aligned/buffer segment of the wksp,
+ * which counts from the end of the wksp (as opposed to the object/table segment).
  *
  * Returns a pointer to the beginning of that space.
  */
-MEM_STATIC void* ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes) {
+MEM_STATIC void*
+ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes)
+{
     void* const alloc = (BYTE*)ws->allocStart - bytes;
     void* const bottom = ws->tableEnd;
     DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining",
@@ -260,6 +295,8 @@
         ws->allocFailed = 1;
         return NULL;
     }
+    /* the area is reserved from the end of wksp.
+     * If it overlaps with tableValidEnd, it voids guarantees on values' range */
     if (alloc < ws->tableValidEnd) {
         ws->tableValidEnd = alloc;
     }
@@ -269,39 +306,32 @@
 
 /**
  * Moves the cwksp to the next phase, and does any necessary allocations.
+ * cwksp initialization must necessarily go through each phase in order.
  * Returns a 0 on success, or zstd error
  */
-MEM_STATIC size_t ZSTD_cwksp_internal_advance_phase(
-        ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) {
+MEM_STATIC size_t
+ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase)
+{
     assert(phase >= ws->phase);
     if (phase > ws->phase) {
-        /* Going from allocating objects to allocating buffers */
-        if (ws->phase < ZSTD_cwksp_alloc_buffers &&
-                phase >= ZSTD_cwksp_alloc_buffers) {
+        /* Going from allocating objects to allocating initOnce / tables */
+        if (ws->phase < ZSTD_cwksp_alloc_aligned_init_once &&
+            phase >= ZSTD_cwksp_alloc_aligned_init_once) {
             ws->tableValidEnd = ws->objectEnd;
-        }
+            ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws);
 
-        /* Going from allocating buffers to allocating aligneds/tables */
-        if (ws->phase < ZSTD_cwksp_alloc_aligned &&
-                phase >= ZSTD_cwksp_alloc_aligned) {
-            {   /* Align the start of the "aligned" to 64 bytes. Use [1, 64] bytes. */
-                size_t const bytesToAlign =
-                    ZSTD_CWKSP_ALIGNMENT_BYTES - ZSTD_cwksp_bytes_to_align_ptr(ws->allocStart, ZSTD_CWKSP_ALIGNMENT_BYTES);
-                DEBUGLOG(5, "reserving aligned alignment addtl space: %zu", bytesToAlign);
-                ZSTD_STATIC_ASSERT((ZSTD_CWKSP_ALIGNMENT_BYTES & (ZSTD_CWKSP_ALIGNMENT_BYTES - 1)) == 0); /* power of 2 */
-                RETURN_ERROR_IF(!ZSTD_cwksp_reserve_internal_buffer_space(ws, bytesToAlign),
-                                memory_allocation, "aligned phase - alignment initial allocation failed!");
-            }
             {   /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */
-                void* const alloc = ws->objectEnd;
+                void *const alloc = ws->objectEnd;
                 size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES);
-                void* const end = (BYTE*)alloc + bytesToAlign;
+                void *const objectEnd = (BYTE *) alloc + bytesToAlign;
                 DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign);
-                RETURN_ERROR_IF(end > ws->workspaceEnd, memory_allocation,
+                RETURN_ERROR_IF(objectEnd > ws->workspaceEnd, memory_allocation,
                                 "table phase - alignment initial allocation failed!");
-                ws->objectEnd = end;
-                ws->tableEnd = end;
-                ws->tableValidEnd = end;
+                ws->objectEnd = objectEnd;
+                ws->tableEnd = objectEnd;  /* table area starts being empty */
+                if (ws->tableValidEnd < ws->tableEnd) {
+                    ws->tableValidEnd = ws->tableEnd;
+                }
             }
         }
         ws->phase = phase;
@@ -313,15 +343,17 @@
 /**
  * Returns whether this object/buffer/etc was allocated in this workspace.
  */
-MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr) {
-    return (ptr != NULL) && (ws->workspace <= ptr) && (ptr <= ws->workspaceEnd);
+MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr)
+{
+    return (ptr != NULL) && (ws->workspace <= ptr) && (ptr < ws->workspaceEnd);
 }
 
 /**
  * Internal function. Do not use directly.
  */
-MEM_STATIC void* ZSTD_cwksp_reserve_internal(
-        ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) {
+MEM_STATIC void*
+ZSTD_cwksp_reserve_internal(ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase)
+{
     void* alloc;
     if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase)) || bytes == 0) {
         return NULL;
@@ -340,7 +372,9 @@
     if (alloc) {
         alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
         if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
-            __asan_unpoison_memory_region(alloc, bytes);
+            /* We need to keep the redzone poisoned while unpoisoning the bytes that
+             * are actually allocated. */
+            __asan_unpoison_memory_region(alloc, bytes - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE);
         }
     }
 #endif
@@ -351,14 +385,46 @@
 /**
  * Reserves and returns unaligned memory.
  */
-MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes) {
+MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes)
+{
     return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers);
 }
 
 /**
  * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes).
+ * This memory has been initialized at least once in the past.
+ * This doesn't mean it has been initialized this time, and it might contain data from previous
+ * operations.
+ * The main usage is for algorithms that might need read access into uninitialized memory.
+ * The algorithm must maintain safety under these conditions and must make sure it doesn't
+ * leak any of the past data (directly or in side channels).
  */
-MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) {
+MEM_STATIC void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, size_t bytes)
+{
+    size_t const alignedBytes = ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES);
+    void* ptr = ZSTD_cwksp_reserve_internal(ws, alignedBytes, ZSTD_cwksp_alloc_aligned_init_once);
+    assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0);
+    if(ptr && ptr < ws->initOnceStart) {
+        /* We assume the memory following the current allocation is either:
+         * 1. Not usable as initOnce memory (end of workspace)
+         * 2. Another initOnce buffer that has been allocated before (and so was previously memset)
+         * 3. An ASAN redzone, in which case we don't want to write on it
+         * For these reasons it should be fine to not explicitly zero every byte up to ws->initOnceStart.
+         * Note that we assume here that MSAN and ASAN cannot run in the same time. */
+        ZSTD_memset(ptr, 0, MIN((size_t)((U8*)ws->initOnceStart - (U8*)ptr), alignedBytes));
+        ws->initOnceStart = ptr;
+    }
+#if ZSTD_MEMORY_SANITIZER
+    assert(__msan_test_shadow(ptr, bytes) == -1);
+#endif
+    return ptr;
+}
+
+/**
+ * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes).
+ */
+MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes)
+{
     void* ptr = ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES),
                                             ZSTD_cwksp_alloc_aligned);
     assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0);
@@ -370,14 +436,19 @@
  * their values remain constrained, allowing us to re-use them without
  * memset()-ing them.
  */
-MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) {
-    const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned;
+MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes)
+{
+    const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned_init_once;
     void* alloc;
     void* end;
     void* top;
 
-    if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) {
-        return NULL;
+    /* We can only start allocating tables after we are done reserving space for objects at the
+     * start of the workspace */
+    if(ws->phase < phase) {
+        if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) {
+            return NULL;
+        }
     }
     alloc = ws->tableEnd;
     end = (BYTE *)alloc + bytes;
@@ -408,9 +479,11 @@
 
 /**
  * Aligned on sizeof(void*).
+ * Note : should happen only once, at workspace first initialization
  */
-MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) {
-    size_t roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*));
+MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes)
+{
+    size_t const roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*));
     void* alloc = ws->objectEnd;
     void* end = (BYTE*)alloc + roundedBytes;
 
@@ -419,15 +492,15 @@
     end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;
 #endif
 
-    DEBUGLOG(5,
+    DEBUGLOG(4,
         "cwksp: reserving %p object %zd bytes (rounded to %zd), %zd bytes remaining",
         alloc, bytes, roundedBytes, ZSTD_cwksp_available_space(ws) - roundedBytes);
-    assert(((size_t)alloc & (sizeof(void*)-1)) == 0);
-    assert((bytes & (sizeof(void*)-1)) == 0);
+    assert((size_t)alloc % ZSTD_ALIGNOF(void*) == 0);
+    assert(bytes % ZSTD_ALIGNOF(void*) == 0);
     ZSTD_cwksp_assert_internal_consistency(ws);
     /* we must be in the first phase, no advance is possible */
     if (ws->phase != ZSTD_cwksp_alloc_objects || end > ws->workspaceEnd) {
-        DEBUGLOG(4, "cwksp: object alloc failed!");
+        DEBUGLOG(3, "cwksp: object alloc failed!");
         ws->allocFailed = 1;
         return NULL;
     }
@@ -438,7 +511,7 @@
 #if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
     /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on
      * either size. */
-    alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
+    alloc = (BYTE*)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
     if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
         __asan_unpoison_memory_region(alloc, bytes);
     }
@@ -447,17 +520,26 @@
     return alloc;
 }
 
-MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) {
+MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws)
+{
     DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty");
 
 #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
     /* To validate that the table re-use logic is sound, and that we don't
      * access table space that we haven't cleaned, we re-"poison" the table
-     * space every time we mark it dirty. */
+     * space every time we mark it dirty.
+     * Since tableValidEnd space and initOnce space may overlap we don't poison
+     * the initOnce portion as it break its promise. This means that this poisoning
+     * check isn't always applied fully. */
     {
         size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;
         assert(__msan_test_shadow(ws->objectEnd, size) == -1);
-        __msan_poison(ws->objectEnd, size);
+        if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) {
+            __msan_poison(ws->objectEnd, size);
+        } else {
+            assert(ws->initOnceStart >= ws->objectEnd);
+            __msan_poison(ws->objectEnd, (BYTE*)ws->initOnceStart - (BYTE*)ws->objectEnd);
+        }
     }
 #endif
 
@@ -485,7 +567,7 @@
     assert(ws->tableValidEnd >= ws->objectEnd);
     assert(ws->tableValidEnd <= ws->allocStart);
     if (ws->tableValidEnd < ws->tableEnd) {
-        ZSTD_memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd);
+        ZSTD_memset(ws->tableValidEnd, 0, (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd));
     }
     ZSTD_cwksp_mark_tables_clean(ws);
 }
@@ -522,11 +604,14 @@
 #if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
     /* To validate that the context re-use logic is sound, and that we don't
      * access stuff that this compression hasn't initialized, we re-"poison"
-     * the workspace (or at least the non-static, non-table parts of it)
-     * every time we start a new compression. */
+     * the workspace except for the areas in which we expect memory re-use
+     * without initialization (objects, valid tables area and init once
+     * memory). */
     {
-        size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->tableValidEnd;
-        __msan_poison(ws->tableValidEnd, size);
+        if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) {
+            size_t size = (BYTE*)ws->initOnceStart - (BYTE*)ws->tableValidEnd;
+            __msan_poison(ws->tableValidEnd, size);
+        }
     }
 #endif
 
@@ -542,10 +627,10 @@
 #endif
 
     ws->tableEnd = ws->objectEnd;
-    ws->allocStart = ws->workspaceEnd;
+    ws->allocStart = ZSTD_cwksp_initialAllocStart(ws);
     ws->allocFailed = 0;
-    if (ws->phase > ZSTD_cwksp_alloc_buffers) {
-        ws->phase = ZSTD_cwksp_alloc_buffers;
+    if (ws->phase > ZSTD_cwksp_alloc_aligned_init_once) {
+        ws->phase = ZSTD_cwksp_alloc_aligned_init_once;
     }
     ZSTD_cwksp_assert_internal_consistency(ws);
 }
@@ -562,6 +647,7 @@
     ws->workspaceEnd = (BYTE*)start + size;
     ws->objectEnd = ws->workspace;
     ws->tableValidEnd = ws->objectEnd;
+    ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws);
     ws->phase = ZSTD_cwksp_alloc_objects;
     ws->isStatic = isStatic;
     ZSTD_cwksp_clear(ws);
@@ -614,17 +700,11 @@
  * Returns if the estimated space needed for a wksp is within an acceptable limit of the
  * actual amount of space used.
  */
-MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp* const ws,
-                                                        size_t const estimatedSpace, int resizedWorkspace) {
-    if (resizedWorkspace) {
-        /* Resized/newly allocated wksp should have exact bounds */
-        return ZSTD_cwksp_used(ws) == estimatedSpace;
-    } else {
-        /* Due to alignment, when reusing a workspace, we can actually consume 63 fewer or more bytes
-         * than estimatedSpace. See the comments in zstd_cwksp.h for details.
-         */
-        return (ZSTD_cwksp_used(ws) >= estimatedSpace - 63) && (ZSTD_cwksp_used(ws) <= estimatedSpace + 63);
-    }
+MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp *const ws, size_t const estimatedSpace) {
+    /* We have an alignment space between objects and tables between tables and buffers, so we can have up to twice
+     * the alignment bytes difference between estimation and actual usage */
+    return (estimatedSpace - ZSTD_cwksp_slack_space_required()) <= ZSTD_cwksp_used(ws) &&
+           ZSTD_cwksp_used(ws) <= estimatedSpace;
 }
 
 
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.c b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
index d0d3a78..0ad88ff 100644
--- a/Utilities/cmzstd/lib/compress/zstd_double_fast.c
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,8 +11,43 @@
 #include "zstd_compress_internal.h"
 #include "zstd_double_fast.h"
 
+static void ZSTD_fillDoubleHashTableForCDict(ZSTD_matchState_t* ms,
+                              void const* end, ZSTD_dictTableLoadMethod_e dtlm)
+{
+    const ZSTD_compressionParameters* const cParams = &ms->cParams;
+    U32* const hashLarge = ms->hashTable;
+    U32  const hBitsL = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+    U32  const mls = cParams->minMatch;
+    U32* const hashSmall = ms->chainTable;
+    U32  const hBitsS = cParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS;
+    const BYTE* const base = ms->window.base;
+    const BYTE* ip = base + ms->nextToUpdate;
+    const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+    const U32 fastHashFillStep = 3;
 
-void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+    /* Always insert every fastHashFillStep position into the hash tables.
+     * Insert the other positions into the large hash table if their entry
+     * is empty.
+     */
+    for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
+        U32 const curr = (U32)(ip - base);
+        U32 i;
+        for (i = 0; i < fastHashFillStep; ++i) {
+            size_t const smHashAndTag = ZSTD_hashPtr(ip + i, hBitsS, mls);
+            size_t const lgHashAndTag = ZSTD_hashPtr(ip + i, hBitsL, 8);
+            if (i == 0) {
+                ZSTD_writeTaggedIndex(hashSmall, smHashAndTag, curr + i);
+            }
+            if (i == 0 || hashLarge[lgHashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) {
+                ZSTD_writeTaggedIndex(hashLarge, lgHashAndTag, curr + i);
+            }
+            /* Only load extra positions for ZSTD_dtlm_full */
+            if (dtlm == ZSTD_dtlm_fast)
+                break;
+    }   }
+}
+
+static void ZSTD_fillDoubleHashTableForCCtx(ZSTD_matchState_t* ms,
                               void const* end, ZSTD_dictTableLoadMethod_e dtlm)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
@@ -43,15 +78,26 @@
             /* Only load extra positions for ZSTD_dtlm_full */
             if (dtlm == ZSTD_dtlm_fast)
                 break;
-    }   }
+        }   }
+}
+
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+                        const void* const end,
+                        ZSTD_dictTableLoadMethod_e dtlm,
+                        ZSTD_tableFillPurpose_e tfp)
+{
+    if (tfp == ZSTD_tfp_forCDict) {
+        ZSTD_fillDoubleHashTableForCDict(ms, end, dtlm);
+    } else {
+        ZSTD_fillDoubleHashTableForCCtx(ms, end, dtlm);
+    }
 }
 
 
 FORCE_INLINE_TEMPLATE
-size_t ZSTD_compressBlock_doubleFast_generic(
+size_t ZSTD_compressBlock_doubleFast_noDict_generic(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
-        void const* src, size_t srcSize,
-        U32 const mls /* template */, ZSTD_dictMode_e const dictMode)
+        void const* src, size_t srcSize, U32 const mls /* template */)
 {
     ZSTD_compressionParameters const* cParams = &ms->cParams;
     U32* const hashLong = ms->hashTable;
@@ -60,7 +106,6 @@
     const U32 hBitsS = cParams->chainLog;
     const BYTE* const base = ms->window.base;
     const BYTE* const istart = (const BYTE*)src;
-    const BYTE* ip = istart;
     const BYTE* anchor = istart;
     const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
     /* presumes that, if there is a dictionary, it must be using Attach mode */
@@ -69,188 +114,161 @@
     const BYTE* const iend = istart + srcSize;
     const BYTE* const ilimit = iend - HASH_READ_SIZE;
     U32 offset_1=rep[0], offset_2=rep[1];
-    U32 offsetSaved = 0;
+    U32 offsetSaved1 = 0, offsetSaved2 = 0;
 
-    const ZSTD_matchState_t* const dms = ms->dictMatchState;
-    const ZSTD_compressionParameters* const dictCParams =
-                                     dictMode == ZSTD_dictMatchState ?
-                                     &dms->cParams : NULL;
-    const U32* const dictHashLong  = dictMode == ZSTD_dictMatchState ?
-                                     dms->hashTable : NULL;
-    const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ?
-                                     dms->chainTable : NULL;
-    const U32 dictStartIndex       = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.dictLimit : 0;
-    const BYTE* const dictBase     = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.base : NULL;
-    const BYTE* const dictStart    = dictMode == ZSTD_dictMatchState ?
-                                     dictBase + dictStartIndex : NULL;
-    const BYTE* const dictEnd      = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.nextSrc : NULL;
-    const U32 dictIndexDelta       = dictMode == ZSTD_dictMatchState ?
-                                     prefixLowestIndex - (U32)(dictEnd - dictBase) :
-                                     0;
-    const U32 dictHBitsL           = dictMode == ZSTD_dictMatchState ?
-                                     dictCParams->hashLog : hBitsL;
-    const U32 dictHBitsS           = dictMode == ZSTD_dictMatchState ?
-                                     dictCParams->chainLog : hBitsS;
-    const U32 dictAndPrefixLength  = (U32)((ip - prefixLowest) + (dictEnd - dictStart));
+    size_t mLength;
+    U32 offset;
+    U32 curr;
 
-    DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_generic");
+    /* how many positions to search before increasing step size */
+    const size_t kStepIncr = 1 << kSearchStrength;
+    /* the position at which to increment the step size if no match is found */
+    const BYTE* nextStep;
+    size_t step; /* the current step size */
 
-    assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState);
+    size_t hl0; /* the long hash at ip */
+    size_t hl1; /* the long hash at ip1 */
 
-    /* if a dictionary is attached, it must be within window range */
-    if (dictMode == ZSTD_dictMatchState) {
-        assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex);
-    }
+    U32 idxl0; /* the long match index for ip */
+    U32 idxl1; /* the long match index for ip1 */
+
+    const BYTE* matchl0; /* the long match for ip */
+    const BYTE* matchs0; /* the short match for ip */
+    const BYTE* matchl1; /* the long match for ip1 */
+
+    const BYTE* ip = istart; /* the current position */
+    const BYTE* ip1; /* the next position */
+
+    DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_noDict_generic");
 
     /* init */
-    ip += (dictAndPrefixLength == 0);
-    if (dictMode == ZSTD_noDict) {
-        U32 const curr = (U32)(ip - base);
-        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog);
-        U32 const maxRep = curr - windowLow;
-        if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
-        if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
-    }
-    if (dictMode == ZSTD_dictMatchState) {
-        /* dictMatchState repCode checks don't currently handle repCode == 0
-         * disabling. */
-        assert(offset_1 <= dictAndPrefixLength);
-        assert(offset_2 <= dictAndPrefixLength);
+    ip += ((ip - prefixLowest) == 0);
+    {
+        U32 const current = (U32)(ip - base);
+        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog);
+        U32 const maxRep = current - windowLow;
+        if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+        if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0;
     }
 
-    /* Main Search Loop */
-    while (ip < ilimit) {   /* < instead of <=, because repcode check at (ip+1) */
-        size_t mLength;
-        U32 offset;
-        size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8);
-        size_t const h = ZSTD_hashPtr(ip, hBitsS, mls);
-        size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8);
-        size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls);
-        U32 const curr = (U32)(ip-base);
-        U32 const matchIndexL = hashLong[h2];
-        U32 matchIndexS = hashSmall[h];
-        const BYTE* matchLong = base + matchIndexL;
-        const BYTE* match = base + matchIndexS;
-        const U32 repIndex = curr + 1 - offset_1;
-        const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
-                            && repIndex < prefixLowestIndex) ?
-                               dictBase + (repIndex - dictIndexDelta) :
-                               base + repIndex;
-        hashLong[h2] = hashSmall[h] = curr;   /* update hash tables */
+    /* Outer Loop: one iteration per match found and stored */
+    while (1) {
+        step = 1;
+        nextStep = ip + kStepIncr;
+        ip1 = ip + step;
 
-        /* check dictMatchState repcode */
-        if (dictMode == ZSTD_dictMatchState
-            && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
-            && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
-            const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
-            mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
-            ip++;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH);
-            goto _match_stored;
+        if (ip1 > ilimit) {
+            goto _cleanup;
         }
 
-        /* check noDict repcode */
-        if ( dictMode == ZSTD_noDict
-          && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) {
-            mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
-            ip++;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH);
-            goto _match_stored;
-        }
+        hl0 = ZSTD_hashPtr(ip, hBitsL, 8);
+        idxl0 = hashLong[hl0];
+        matchl0 = base + idxl0;
 
-        if (matchIndexL > prefixLowestIndex) {
-            /* check prefix long match */
-            if (MEM_read64(matchLong) == MEM_read64(ip)) {
-                mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8;
-                offset = (U32)(ip-matchLong);
-                while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
-                goto _match_found;
+        /* Inner Loop: one iteration per search / position */
+        do {
+            const size_t hs0 = ZSTD_hashPtr(ip, hBitsS, mls);
+            const U32 idxs0 = hashSmall[hs0];
+            curr = (U32)(ip-base);
+            matchs0 = base + idxs0;
+
+            hashLong[hl0] = hashSmall[hs0] = curr;   /* update hash tables */
+
+            /* check noDict repcode */
+            if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) {
+                mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
+                ip++;
+                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
+                goto _match_stored;
             }
-        } else if (dictMode == ZSTD_dictMatchState) {
-            /* check dictMatchState long match */
-            U32 const dictMatchIndexL = dictHashLong[dictHL];
-            const BYTE* dictMatchL = dictBase + dictMatchIndexL;
-            assert(dictMatchL < dictEnd);
 
-            if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) {
-                mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8;
-                offset = (U32)(curr - dictMatchIndexL - dictIndexDelta);
-                while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */
-                goto _match_found;
-        }   }
+            hl1 = ZSTD_hashPtr(ip1, hBitsL, 8);
 
-        if (matchIndexS > prefixLowestIndex) {
-            /* check prefix short match */
-            if (MEM_read32(match) == MEM_read32(ip)) {
-                goto _search_next_long;
+            if (idxl0 > prefixLowestIndex) {
+                /* check prefix long match */
+                if (MEM_read64(matchl0) == MEM_read64(ip)) {
+                    mLength = ZSTD_count(ip+8, matchl0+8, iend) + 8;
+                    offset = (U32)(ip-matchl0);
+                    while (((ip>anchor) & (matchl0>prefixLowest)) && (ip[-1] == matchl0[-1])) { ip--; matchl0--; mLength++; } /* catch up */
+                    goto _match_found;
+                }
             }
-        } else if (dictMode == ZSTD_dictMatchState) {
-            /* check dictMatchState short match */
-            U32 const dictMatchIndexS = dictHashSmall[dictHS];
-            match = dictBase + dictMatchIndexS;
-            matchIndexS = dictMatchIndexS + dictIndexDelta;
 
-            if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) {
-                goto _search_next_long;
-        }   }
+            idxl1 = hashLong[hl1];
+            matchl1 = base + idxl1;
 
-        ip += ((ip-anchor) >> kSearchStrength) + 1;
-#if defined(__aarch64__)
-        PREFETCH_L1(ip+256);
-#endif
-        continue;
+            if (idxs0 > prefixLowestIndex) {
+                /* check prefix short match */
+                if (MEM_read32(matchs0) == MEM_read32(ip)) {
+                    goto _search_next_long;
+                }
+            }
+
+            if (ip1 >= nextStep) {
+                PREFETCH_L1(ip1 + 64);
+                PREFETCH_L1(ip1 + 128);
+                step++;
+                nextStep += kStepIncr;
+            }
+            ip = ip1;
+            ip1 += step;
+
+            hl0 = hl1;
+            idxl0 = idxl1;
+            matchl0 = matchl1;
+    #if defined(__aarch64__)
+            PREFETCH_L1(ip+256);
+    #endif
+        } while (ip1 <= ilimit);
+
+_cleanup:
+        /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+         * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+        offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
+
+        /* save reps for next block */
+        rep[0] = offset_1 ? offset_1 : offsetSaved1;
+        rep[1] = offset_2 ? offset_2 : offsetSaved2;
+
+        /* Return the last literals size */
+        return (size_t)(iend - anchor);
 
 _search_next_long:
 
-        {   size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
-            size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
-            U32 const matchIndexL3 = hashLong[hl3];
-            const BYTE* matchL3 = base + matchIndexL3;
-            hashLong[hl3] = curr + 1;
-
-            /* check prefix long +1 match */
-            if (matchIndexL3 > prefixLowestIndex) {
-                if (MEM_read64(matchL3) == MEM_read64(ip+1)) {
-                    mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8;
-                    ip++;
-                    offset = (U32)(ip-matchL3);
-                    while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */
-                    goto _match_found;
-                }
-            } else if (dictMode == ZSTD_dictMatchState) {
-                /* check dict long +1 match */
-                U32 const dictMatchIndexL3 = dictHashLong[dictHLNext];
-                const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3;
-                assert(dictMatchL3 < dictEnd);
-                if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) {
-                    mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8;
-                    ip++;
-                    offset = (U32)(curr + 1 - dictMatchIndexL3 - dictIndexDelta);
-                    while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */
-                    goto _match_found;
-        }   }   }
+        /* check prefix long +1 match */
+        if (idxl1 > prefixLowestIndex) {
+            if (MEM_read64(matchl1) == MEM_read64(ip1)) {
+                ip = ip1;
+                mLength = ZSTD_count(ip+8, matchl1+8, iend) + 8;
+                offset = (U32)(ip-matchl1);
+                while (((ip>anchor) & (matchl1>prefixLowest)) && (ip[-1] == matchl1[-1])) { ip--; matchl1--; mLength++; } /* catch up */
+                goto _match_found;
+            }
+        }
 
         /* if no long +1 match, explore the short match we found */
-        if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) {
-            mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4;
-            offset = (U32)(curr - matchIndexS);
-            while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
-        } else {
-            mLength = ZSTD_count(ip+4, match+4, iend) + 4;
-            offset = (U32)(ip - match);
-            while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
-        }
+        mLength = ZSTD_count(ip+4, matchs0+4, iend) + 4;
+        offset = (U32)(ip - matchs0);
+        while (((ip>anchor) & (matchs0>prefixLowest)) && (ip[-1] == matchs0[-1])) { ip--; matchs0--; mLength++; } /* catch up */
 
         /* fall-through */
 
-_match_found:
+_match_found: /* requires ip, offset, mLength */
         offset_2 = offset_1;
         offset_1 = offset;
 
-        ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+        if (step < 4) {
+            /* It is unsafe to write this value back to the hashtable when ip1 is
+             * greater than or equal to the new ip we will have after we're done
+             * processing this match. Rather than perform that test directly
+             * (ip1 >= ip + mLength), which costs speed in practice, we do a simpler
+             * more predictable test. The minmatch even if we take a short match is
+             * 4 bytes, so as long as step, the distance between ip and ip1
+             * (initially) is less than 4, we know ip1 < new ip. */
+            hashLong[hl1] = (U32)(ip1 - base);
+        }
+
+        ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
 
 _match_stored:
         /* match found */
@@ -268,53 +286,268 @@
             }
 
             /* check immediate repcode */
-            if (dictMode == ZSTD_dictMatchState) {
-                while (ip <= ilimit) {
-                    U32 const current2 = (U32)(ip-base);
-                    U32 const repIndex2 = current2 - offset_2;
-                    const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState
-                        && repIndex2 < prefixLowestIndex ?
-                            dictBase + repIndex2 - dictIndexDelta :
-                            base + repIndex2;
-                    if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
-                       && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
-                        const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend;
-                        size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4;
-                        U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset;   /* swap offset_2 <=> offset_1 */
-                        ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH);
-                        hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
-                        hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
-                        ip += repLength2;
-                        anchor = ip;
-                        continue;
-                    }
-                    break;
-            }   }
+            while ( (ip <= ilimit)
+                 && ( (offset_2>0)
+                    & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) {
+                /* store sequence */
+                size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
+                U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff;  /* swap offset_2 <=> offset_1 */
+                hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base);
+                hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base);
+                ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, rLength);
+                ip += rLength;
+                anchor = ip;
+                continue;   /* faster when present ... (?) */
+            }
+        }
+    }
+}
 
-            if (dictMode == ZSTD_noDict) {
-                while ( (ip <= ilimit)
-                     && ( (offset_2>0)
-                        & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) {
-                    /* store sequence */
-                    size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
-                    U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff;  /* swap offset_2 <=> offset_1 */
-                    hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base);
-                    hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base);
-                    ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, rLength-MINMATCH);
-                    ip += rLength;
-                    anchor = ip;
-                    continue;   /* faster when present ... (?) */
+
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize,
+        U32 const mls /* template */)
+{
+    ZSTD_compressionParameters const* cParams = &ms->cParams;
+    U32* const hashLong = ms->hashTable;
+    const U32 hBitsL = cParams->hashLog;
+    U32* const hashSmall = ms->chainTable;
+    const U32 hBitsS = cParams->chainLog;
+    const BYTE* const base = ms->window.base;
+    const BYTE* const istart = (const BYTE*)src;
+    const BYTE* ip = istart;
+    const BYTE* anchor = istart;
+    const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+    /* presumes that, if there is a dictionary, it must be using Attach mode */
+    const U32 prefixLowestIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog);
+    const BYTE* const prefixLowest = base + prefixLowestIndex;
+    const BYTE* const iend = istart + srcSize;
+    const BYTE* const ilimit = iend - HASH_READ_SIZE;
+    U32 offset_1=rep[0], offset_2=rep[1];
+
+    const ZSTD_matchState_t* const dms = ms->dictMatchState;
+    const ZSTD_compressionParameters* const dictCParams = &dms->cParams;
+    const U32* const dictHashLong  = dms->hashTable;
+    const U32* const dictHashSmall = dms->chainTable;
+    const U32 dictStartIndex       = dms->window.dictLimit;
+    const BYTE* const dictBase     = dms->window.base;
+    const BYTE* const dictStart    = dictBase + dictStartIndex;
+    const BYTE* const dictEnd      = dms->window.nextSrc;
+    const U32 dictIndexDelta       = prefixLowestIndex - (U32)(dictEnd - dictBase);
+    const U32 dictHBitsL           = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+    const U32 dictHBitsS           = dictCParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS;
+    const U32 dictAndPrefixLength  = (U32)((ip - prefixLowest) + (dictEnd - dictStart));
+
+    DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_dictMatchState_generic");
+
+    /* if a dictionary is attached, it must be within window range */
+    assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex);
+
+    if (ms->prefetchCDictTables) {
+        size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32);
+        size_t const chainTableBytes = (((size_t)1) << dictCParams->chainLog) * sizeof(U32);
+        PREFETCH_AREA(dictHashLong, hashTableBytes)
+        PREFETCH_AREA(dictHashSmall, chainTableBytes)
+    }
+
+    /* init */
+    ip += (dictAndPrefixLength == 0);
+
+    /* dictMatchState repCode checks don't currently handle repCode == 0
+     * disabling. */
+    assert(offset_1 <= dictAndPrefixLength);
+    assert(offset_2 <= dictAndPrefixLength);
+
+    /* Main Search Loop */
+    while (ip < ilimit) {   /* < instead of <=, because repcode check at (ip+1) */
+        size_t mLength;
+        U32 offset;
+        size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8);
+        size_t const h = ZSTD_hashPtr(ip, hBitsS, mls);
+        size_t const dictHashAndTagL = ZSTD_hashPtr(ip, dictHBitsL, 8);
+        size_t const dictHashAndTagS = ZSTD_hashPtr(ip, dictHBitsS, mls);
+        U32 const dictMatchIndexAndTagL = dictHashLong[dictHashAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS];
+        U32 const dictMatchIndexAndTagS = dictHashSmall[dictHashAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS];
+        int const dictTagsMatchL = ZSTD_comparePackedTags(dictMatchIndexAndTagL, dictHashAndTagL);
+        int const dictTagsMatchS = ZSTD_comparePackedTags(dictMatchIndexAndTagS, dictHashAndTagS);
+        U32 const curr = (U32)(ip-base);
+        U32 const matchIndexL = hashLong[h2];
+        U32 matchIndexS = hashSmall[h];
+        const BYTE* matchLong = base + matchIndexL;
+        const BYTE* match = base + matchIndexS;
+        const U32 repIndex = curr + 1 - offset_1;
+        const BYTE* repMatch = (repIndex < prefixLowestIndex) ?
+                               dictBase + (repIndex - dictIndexDelta) :
+                               base + repIndex;
+        hashLong[h2] = hashSmall[h] = curr;   /* update hash tables */
+
+        /* check repcode */
+        if (((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */)
+            && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
+            const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
+            mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
+            ip++;
+            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
+            goto _match_stored;
+        }
+
+        if (matchIndexL > prefixLowestIndex) {
+            /* check prefix long match */
+            if (MEM_read64(matchLong) == MEM_read64(ip)) {
+                mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8;
+                offset = (U32)(ip-matchLong);
+                while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
+                goto _match_found;
+            }
+        } else if (dictTagsMatchL) {
+            /* check dictMatchState long match */
+            U32 const dictMatchIndexL = dictMatchIndexAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS;
+            const BYTE* dictMatchL = dictBase + dictMatchIndexL;
+            assert(dictMatchL < dictEnd);
+
+            if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) {
+                mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8;
+                offset = (U32)(curr - dictMatchIndexL - dictIndexDelta);
+                while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */
+                goto _match_found;
+        }   }
+
+        if (matchIndexS > prefixLowestIndex) {
+            /* check prefix short match */
+            if (MEM_read32(match) == MEM_read32(ip)) {
+                goto _search_next_long;
+            }
+        } else if (dictTagsMatchS) {
+            /* check dictMatchState short match */
+            U32 const dictMatchIndexS = dictMatchIndexAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS;
+            match = dictBase + dictMatchIndexS;
+            matchIndexS = dictMatchIndexS + dictIndexDelta;
+
+            if (match > dictStart && MEM_read32(match) == MEM_read32(ip)) {
+                goto _search_next_long;
+        }   }
+
+        ip += ((ip-anchor) >> kSearchStrength) + 1;
+#if defined(__aarch64__)
+        PREFETCH_L1(ip+256);
+#endif
+        continue;
+
+_search_next_long:
+        {   size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
+            size_t const dictHashAndTagL3 = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
+            U32 const matchIndexL3 = hashLong[hl3];
+            U32 const dictMatchIndexAndTagL3 = dictHashLong[dictHashAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS];
+            int const dictTagsMatchL3 = ZSTD_comparePackedTags(dictMatchIndexAndTagL3, dictHashAndTagL3);
+            const BYTE* matchL3 = base + matchIndexL3;
+            hashLong[hl3] = curr + 1;
+
+            /* check prefix long +1 match */
+            if (matchIndexL3 > prefixLowestIndex) {
+                if (MEM_read64(matchL3) == MEM_read64(ip+1)) {
+                    mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8;
+                    ip++;
+                    offset = (U32)(ip-matchL3);
+                    while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */
+                    goto _match_found;
+                }
+            } else if (dictTagsMatchL3) {
+                /* check dict long +1 match */
+                U32 const dictMatchIndexL3 = dictMatchIndexAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS;
+                const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3;
+                assert(dictMatchL3 < dictEnd);
+                if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) {
+                    mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8;
+                    ip++;
+                    offset = (U32)(curr + 1 - dictMatchIndexL3 - dictIndexDelta);
+                    while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */
+                    goto _match_found;
         }   }   }
+
+        /* if no long +1 match, explore the short match we found */
+        if (matchIndexS < prefixLowestIndex) {
+            mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4;
+            offset = (U32)(curr - matchIndexS);
+            while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+        } else {
+            mLength = ZSTD_count(ip+4, match+4, iend) + 4;
+            offset = (U32)(ip - match);
+            while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
+        }
+
+_match_found:
+        offset_2 = offset_1;
+        offset_1 = offset;
+
+        ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+
+_match_stored:
+        /* match found */
+        ip += mLength;
+        anchor = ip;
+
+        if (ip <= ilimit) {
+            /* Complementary insertion */
+            /* done after iLimit test, as candidates could be > iend-8 */
+            {   U32 const indexToInsert = curr+2;
+                hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;
+                hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
+                hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;
+                hashSmall[ZSTD_hashPtr(ip-1, hBitsS, mls)] = (U32)(ip-1-base);
+            }
+
+            /* check immediate repcode */
+            while (ip <= ilimit) {
+                U32 const current2 = (U32)(ip-base);
+                U32 const repIndex2 = current2 - offset_2;
+                const BYTE* repMatch2 = repIndex2 < prefixLowestIndex ?
+                        dictBase + repIndex2 - dictIndexDelta :
+                        base + repIndex2;
+                if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
+                   && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+                    const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend;
+                    size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4;
+                    U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset;   /* swap offset_2 <=> offset_1 */
+                    ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
+                    hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
+                    hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
+                    ip += repLength2;
+                    anchor = ip;
+                    continue;
+                }
+                break;
+            }
+        }
     }   /* while (ip < ilimit) */
 
     /* save reps for next block */
-    rep[0] = offset_1 ? offset_1 : offsetSaved;
-    rep[1] = offset_2 ? offset_2 : offsetSaved;
+    rep[0] = offset_1;
+    rep[1] = offset_2;
 
     /* Return the last literals size */
     return (size_t)(iend - anchor);
 }
 
+#define ZSTD_GEN_DFAST_FN(dictMode, mls)                                                                 \
+    static size_t ZSTD_compressBlock_doubleFast_##dictMode##_##mls(                                      \
+            ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],                          \
+            void const* src, size_t srcSize)                                                             \
+    {                                                                                                    \
+        return ZSTD_compressBlock_doubleFast_##dictMode##_generic(ms, seqStore, rep, src, srcSize, mls); \
+    }
+
+ZSTD_GEN_DFAST_FN(noDict, 4)
+ZSTD_GEN_DFAST_FN(noDict, 5)
+ZSTD_GEN_DFAST_FN(noDict, 6)
+ZSTD_GEN_DFAST_FN(noDict, 7)
+
+ZSTD_GEN_DFAST_FN(dictMatchState, 4)
+ZSTD_GEN_DFAST_FN(dictMatchState, 5)
+ZSTD_GEN_DFAST_FN(dictMatchState, 6)
+ZSTD_GEN_DFAST_FN(dictMatchState, 7)
+
 
 size_t ZSTD_compressBlock_doubleFast(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -325,13 +558,13 @@
     {
     default: /* includes case 3 */
     case 4 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_noDict);
+        return ZSTD_compressBlock_doubleFast_noDict_4(ms, seqStore, rep, src, srcSize);
     case 5 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_noDict);
+        return ZSTD_compressBlock_doubleFast_noDict_5(ms, seqStore, rep, src, srcSize);
     case 6 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_noDict);
+        return ZSTD_compressBlock_doubleFast_noDict_6(ms, seqStore, rep, src, srcSize);
     case 7 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_noDict);
+        return ZSTD_compressBlock_doubleFast_noDict_7(ms, seqStore, rep, src, srcSize);
     }
 }
 
@@ -345,13 +578,13 @@
     {
     default: /* includes case 3 */
     case 4 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 4, ZSTD_dictMatchState);
+        return ZSTD_compressBlock_doubleFast_dictMatchState_4(ms, seqStore, rep, src, srcSize);
     case 5 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 5, ZSTD_dictMatchState);
+        return ZSTD_compressBlock_doubleFast_dictMatchState_5(ms, seqStore, rep, src, srcSize);
     case 6 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 6, ZSTD_dictMatchState);
+        return ZSTD_compressBlock_doubleFast_dictMatchState_6(ms, seqStore, rep, src, srcSize);
     case 7 :
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, 7, ZSTD_dictMatchState);
+        return ZSTD_compressBlock_doubleFast_dictMatchState_7(ms, seqStore, rep, src, srcSize);
     }
 }
 
@@ -387,7 +620,7 @@
 
     /* if extDict is invalidated due to maxDistance, switch to "regular" variant */
     if (prefixStartIndex == dictStartIndex)
-        return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, src, srcSize, mls, ZSTD_noDict);
+        return ZSTD_compressBlock_doubleFast(ms, seqStore, rep, src, srcSize);
 
     /* Search Loop */
     while (ip < ilimit) {  /* < instead of <=, because (ip+1) */
@@ -409,12 +642,12 @@
         hashSmall[hSmall] = hashLong[hLong] = curr;   /* update hash table */
 
         if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */
-            & (offset_1 < curr+1 - dictStartIndex)) /* note: we are searching at curr+1 */
+            & (offset_1 <= curr+1 - dictStartIndex)) /* note: we are searching at curr+1 */
           && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
             const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
             mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
             ip++;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH);
+            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
         } else {
             if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) {
                 const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend;
@@ -425,7 +658,7 @@
                 while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; }   /* catch up */
                 offset_2 = offset_1;
                 offset_1 = offset;
-                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
 
             } else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) {
                 size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
@@ -450,7 +683,7 @@
                 }
                 offset_2 = offset_1;
                 offset_1 = offset;
-                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
 
             } else {
                 ip += ((ip-anchor) >> kSearchStrength) + 1;
@@ -477,12 +710,12 @@
                 U32 const repIndex2 = current2 - offset_2;
                 const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
                 if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3)   /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */
-                    & (offset_2 < current2 - dictStartIndex))
+                    & (offset_2 <= current2 - dictStartIndex))
                   && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
                     const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
                     size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
                     U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset;   /* swap offset_2 <=> offset_1 */
-                    ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH);
+                    ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
                     hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
                     hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
                     ip += repLength2;
@@ -500,6 +733,10 @@
     return (size_t)(iend - anchor);
 }
 
+ZSTD_GEN_DFAST_FN(extDict, 4)
+ZSTD_GEN_DFAST_FN(extDict, 5)
+ZSTD_GEN_DFAST_FN(extDict, 6)
+ZSTD_GEN_DFAST_FN(extDict, 7)
 
 size_t ZSTD_compressBlock_doubleFast_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -510,12 +747,12 @@
     {
     default: /* includes case 3 */
     case 4 :
-        return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 4);
+        return ZSTD_compressBlock_doubleFast_extDict_4(ms, seqStore, rep, src, srcSize);
     case 5 :
-        return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 5);
+        return ZSTD_compressBlock_doubleFast_extDict_5(ms, seqStore, rep, src, srcSize);
     case 6 :
-        return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 6);
+        return ZSTD_compressBlock_doubleFast_extDict_6(ms, seqStore, rep, src, srcSize);
     case 7 :
-        return ZSTD_compressBlock_doubleFast_extDict_generic(ms, seqStore, rep, src, srcSize, 7);
+        return ZSTD_compressBlock_doubleFast_extDict_7(ms, seqStore, rep, src, srcSize);
     }
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.h b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
index e16b7b0..6f0047c 100644
--- a/Utilities/cmzstd/lib/compress/zstd_double_fast.h
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -19,7 +19,8 @@
 #include "zstd_compress_internal.h"     /* ZSTD_CCtx, size_t */
 
 void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
-                              void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+                              void const* end, ZSTD_dictTableLoadMethod_e dtlm,
+                              ZSTD_tableFillPurpose_e tfp);
 size_t ZSTD_compressBlock_doubleFast(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.c b/Utilities/cmzstd/lib/compress/zstd_fast.c
index 4edc04d..5f2c6a2 100644
--- a/Utilities/cmzstd/lib/compress/zstd_fast.c
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,8 +11,42 @@
 #include "zstd_compress_internal.h"  /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */
 #include "zstd_fast.h"
 
+static void ZSTD_fillHashTableForCDict(ZSTD_matchState_t* ms,
+                        const void* const end,
+                        ZSTD_dictTableLoadMethod_e dtlm)
+{
+    const ZSTD_compressionParameters* const cParams = &ms->cParams;
+    U32* const hashTable = ms->hashTable;
+    U32  const hBits = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+    U32  const mls = cParams->minMatch;
+    const BYTE* const base = ms->window.base;
+    const BYTE* ip = base + ms->nextToUpdate;
+    const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+    const U32 fastHashFillStep = 3;
 
-void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+    /* Currently, we always use ZSTD_dtlm_full for filling CDict tables.
+     * Feel free to remove this assert if there's a good reason! */
+    assert(dtlm == ZSTD_dtlm_full);
+
+    /* Always insert every fastHashFillStep position into the hash table.
+     * Insert the other positions if their hash entry is empty.
+     */
+    for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) {
+        U32 const curr = (U32)(ip - base);
+        {   size_t const hashAndTag = ZSTD_hashPtr(ip, hBits, mls);
+            ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr);   }
+
+        if (dtlm == ZSTD_dtlm_fast) continue;
+        /* Only load extra positions for ZSTD_dtlm_full */
+        {   U32 p;
+            for (p = 1; p < fastHashFillStep; ++p) {
+                size_t const hashAndTag = ZSTD_hashPtr(ip + p, hBits, mls);
+                if (hashTable[hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) {  /* not yet filled */
+                    ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr + p);
+                }   }   }   }
+}
+
+static void ZSTD_fillHashTableForCCtx(ZSTD_matchState_t* ms,
                         const void* const end,
                         ZSTD_dictTableLoadMethod_e dtlm)
 {
@@ -25,6 +59,10 @@
     const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
     const U32 fastHashFillStep = 3;
 
+    /* Currently, we always use ZSTD_dtlm_fast for filling CCtx tables.
+     * Feel free to remove this assert if there's a good reason! */
+    assert(dtlm == ZSTD_dtlm_fast);
+
     /* Always insert every fastHashFillStep position into the hash table.
      * Insert the other positions if their hash entry is empty.
      */
@@ -42,146 +80,344 @@
     }   }   }   }
 }
 
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+                        const void* const end,
+                        ZSTD_dictTableLoadMethod_e dtlm,
+                        ZSTD_tableFillPurpose_e tfp)
+{
+    if (tfp == ZSTD_tfp_forCDict) {
+        ZSTD_fillHashTableForCDict(ms, end, dtlm);
+    } else {
+        ZSTD_fillHashTableForCCtx(ms, end, dtlm);
+    }
+}
 
+
+/**
+ * If you squint hard enough (and ignore repcodes), the search operation at any
+ * given position is broken into 4 stages:
+ *
+ * 1. Hash   (map position to hash value via input read)
+ * 2. Lookup (map hash val to index via hashtable read)
+ * 3. Load   (map index to value at that position via input read)
+ * 4. Compare
+ *
+ * Each of these steps involves a memory read at an address which is computed
+ * from the previous step. This means these steps must be sequenced and their
+ * latencies are cumulative.
+ *
+ * Rather than do 1->2->3->4 sequentially for a single position before moving
+ * onto the next, this implementation interleaves these operations across the
+ * next few positions:
+ *
+ * R = Repcode Read & Compare
+ * H = Hash
+ * T = Table Lookup
+ * M = Match Read & Compare
+ *
+ * Pos | Time -->
+ * ----+-------------------
+ * N   | ... M
+ * N+1 | ...   TM
+ * N+2 |    R H   T M
+ * N+3 |         H    TM
+ * N+4 |           R H   T M
+ * N+5 |                H   ...
+ * N+6 |                  R ...
+ *
+ * This is very much analogous to the pipelining of execution in a CPU. And just
+ * like a CPU, we have to dump the pipeline when we find a match (i.e., take a
+ * branch).
+ *
+ * When this happens, we throw away our current state, and do the following prep
+ * to re-enter the loop:
+ *
+ * Pos | Time -->
+ * ----+-------------------
+ * N   | H T
+ * N+1 |  H
+ *
+ * This is also the work we do at the beginning to enter the loop initially.
+ */
 FORCE_INLINE_TEMPLATE size_t
-ZSTD_compressBlock_fast_generic(
+ZSTD_compressBlock_fast_noDict_generic(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize,
-        U32 const mls)
+        U32 const mls, U32 const hasStep)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     U32* const hashTable = ms->hashTable;
     U32 const hlog = cParams->hashLog;
     /* support stepSize of 0 */
-    size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1;
+    size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2;
     const BYTE* const base = ms->window.base;
     const BYTE* const istart = (const BYTE*)src;
-    /* We check ip0 (ip + 0) and ip1 (ip + 1) each loop */
-    const BYTE* ip0 = istart;
-    const BYTE* ip1;
-    const BYTE* anchor = istart;
     const U32   endIndex = (U32)((size_t)(istart - base) + srcSize);
     const U32   prefixStartIndex = ZSTD_getLowestPrefixIndex(ms, endIndex, cParams->windowLog);
     const BYTE* const prefixStart = base + prefixStartIndex;
     const BYTE* const iend = istart + srcSize;
     const BYTE* const ilimit = iend - HASH_READ_SIZE;
-    U32 offset_1=rep[0], offset_2=rep[1];
-    U32 offsetSaved = 0;
 
-    /* init */
+    const BYTE* anchor = istart;
+    const BYTE* ip0 = istart;
+    const BYTE* ip1;
+    const BYTE* ip2;
+    const BYTE* ip3;
+    U32 current0;
+
+    U32 rep_offset1 = rep[0];
+    U32 rep_offset2 = rep[1];
+    U32 offsetSaved1 = 0, offsetSaved2 = 0;
+
+    size_t hash0; /* hash for ip0 */
+    size_t hash1; /* hash for ip1 */
+    U32 idx; /* match idx for ip0 */
+    U32 mval; /* src value at match idx */
+
+    U32 offcode;
+    const BYTE* match0;
+    size_t mLength;
+
+    /* ip0 and ip1 are always adjacent. The targetLength skipping and
+     * uncompressibility acceleration is applied to every other position,
+     * matching the behavior of #1562. step therefore represents the gap
+     * between pairs of positions, from ip0 to ip2 or ip1 to ip3. */
+    size_t step;
+    const BYTE* nextStep;
+    const size_t kStepIncr = (1 << (kSearchStrength - 1));
+
     DEBUGLOG(5, "ZSTD_compressBlock_fast_generic");
     ip0 += (ip0 == prefixStart);
-    ip1 = ip0 + 1;
     {   U32 const curr = (U32)(ip0 - base);
         U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog);
         U32 const maxRep = curr - windowLow;
-        if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
-        if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
+        if (rep_offset2 > maxRep) offsetSaved2 = rep_offset2, rep_offset2 = 0;
+        if (rep_offset1 > maxRep) offsetSaved1 = rep_offset1, rep_offset1 = 0;
     }
 
-    /* Main Search Loop */
-#ifdef __INTEL_COMPILER
-    /* From intel 'The vector pragma indicates that the loop should be
-     * vectorized if it is legal to do so'. Can be used together with
-     * #pragma ivdep (but have opted to exclude that because intel
-     * warns against using it).*/
-    #pragma vector always
-#endif
-    while (ip1 < ilimit) {   /* < instead of <=, because check at ip0+2 */
-        size_t mLength;
-        BYTE const* ip2 = ip0 + 2;
-        size_t const h0 = ZSTD_hashPtr(ip0, hlog, mls);
-        U32 const val0 = MEM_read32(ip0);
-        size_t const h1 = ZSTD_hashPtr(ip1, hlog, mls);
-        U32 const val1 = MEM_read32(ip1);
-        U32 const current0 = (U32)(ip0-base);
-        U32 const current1 = (U32)(ip1-base);
-        U32 const matchIndex0 = hashTable[h0];
-        U32 const matchIndex1 = hashTable[h1];
-        BYTE const* repMatch = ip2 - offset_1;
-        const BYTE* match0 = base + matchIndex0;
-        const BYTE* match1 = base + matchIndex1;
-        U32 offcode;
+    /* start each op */
+_start: /* Requires: ip0 */
 
-#if defined(__aarch64__)
-        PREFETCH_L1(ip0+256);
-#endif
+    step = stepSize;
+    nextStep = ip0 + kStepIncr;
 
-        hashTable[h0] = current0;   /* update hash table */
-        hashTable[h1] = current1;   /* update hash table */
+    /* calculate positions, ip0 - anchor == 0, so we skip step calc */
+    ip1 = ip0 + 1;
+    ip2 = ip0 + step;
+    ip3 = ip2 + 1;
 
-        assert(ip0 + 1 == ip1);
+    if (ip3 >= ilimit) {
+        goto _cleanup;
+    }
 
-        if ((offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip2))) {
-            mLength = (ip2[-1] == repMatch[-1]) ? 1 : 0;
-            ip0 = ip2 - mLength;
-            match0 = repMatch - mLength;
+    hash0 = ZSTD_hashPtr(ip0, hlog, mls);
+    hash1 = ZSTD_hashPtr(ip1, hlog, mls);
+
+    idx = hashTable[hash0];
+
+    do {
+        /* load repcode match for ip[2]*/
+        const U32 rval = MEM_read32(ip2 - rep_offset1);
+
+        /* write back hash table entry */
+        current0 = (U32)(ip0 - base);
+        hashTable[hash0] = current0;
+
+        /* check repcode at ip[2] */
+        if ((MEM_read32(ip2) == rval) & (rep_offset1 > 0)) {
+            ip0 = ip2;
+            match0 = ip0 - rep_offset1;
+            mLength = ip0[-1] == match0[-1];
+            ip0 -= mLength;
+            match0 -= mLength;
+            offcode = REPCODE1_TO_OFFBASE;
             mLength += 4;
-            offcode = 0;
+
+            /* First write next hash table entry; we've already calculated it.
+             * This write is known to be safe because the ip1 is before the
+             * repcode (ip2). */
+            hashTable[hash1] = (U32)(ip1 - base);
+
             goto _match;
         }
-        if ((matchIndex0 > prefixStartIndex) && MEM_read32(match0) == val0) {
-            /* found a regular match */
+
+        /* load match for ip[0] */
+        if (idx >= prefixStartIndex) {
+            mval = MEM_read32(base + idx);
+        } else {
+            mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
+        }
+
+        /* check match at ip[0] */
+        if (MEM_read32(ip0) == mval) {
+            /* found a match! */
+
+            /* First write next hash table entry; we've already calculated it.
+             * This write is known to be safe because the ip1 == ip0 + 1, so
+             * we know we will resume searching after ip1 */
+            hashTable[hash1] = (U32)(ip1 - base);
+
             goto _offset;
         }
-        if ((matchIndex1 > prefixStartIndex) && MEM_read32(match1) == val1) {
-            /* found a regular match after one literal */
-            ip0 = ip1;
-            match0 = match1;
+
+        /* lookup ip[1] */
+        idx = hashTable[hash1];
+
+        /* hash ip[2] */
+        hash0 = hash1;
+        hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+        /* advance to next positions */
+        ip0 = ip1;
+        ip1 = ip2;
+        ip2 = ip3;
+
+        /* write back hash table entry */
+        current0 = (U32)(ip0 - base);
+        hashTable[hash0] = current0;
+
+        /* load match for ip[0] */
+        if (idx >= prefixStartIndex) {
+            mval = MEM_read32(base + idx);
+        } else {
+            mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
+        }
+
+        /* check match at ip[0] */
+        if (MEM_read32(ip0) == mval) {
+            /* found a match! */
+
+            /* first write next hash table entry; we've already calculated it */
+            if (step <= 4) {
+                /* We need to avoid writing an index into the hash table >= the
+                 * position at which we will pick up our searching after we've
+                 * taken this match.
+                 *
+                 * The minimum possible match has length 4, so the earliest ip0
+                 * can be after we take this match will be the current ip0 + 4.
+                 * ip1 is ip0 + step - 1. If ip1 is >= ip0 + 4, we can't safely
+                 * write this position.
+                 */
+                hashTable[hash1] = (U32)(ip1 - base);
+            }
+
             goto _offset;
         }
-        {   size_t const step = ((size_t)(ip0-anchor) >> (kSearchStrength - 1)) + stepSize;
-            assert(step >= 2);
-            ip0 += step;
-            ip1 += step;
-            continue;
+
+        /* lookup ip[1] */
+        idx = hashTable[hash1];
+
+        /* hash ip[2] */
+        hash0 = hash1;
+        hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+        /* advance to next positions */
+        ip0 = ip1;
+        ip1 = ip2;
+        ip2 = ip0 + step;
+        ip3 = ip1 + step;
+
+        /* calculate step */
+        if (ip2 >= nextStep) {
+            step++;
+            PREFETCH_L1(ip1 + 64);
+            PREFETCH_L1(ip1 + 128);
+            nextStep += kStepIncr;
         }
-_offset: /* Requires: ip0, match0 */
-        /* Compute the offset code */
-        offset_2 = offset_1;
-        offset_1 = (U32)(ip0-match0);
-        offcode = offset_1 + ZSTD_REP_MOVE;
-        mLength = 4;
-        /* Count the backwards match length */
-        while (((ip0>anchor) & (match0>prefixStart))
-             && (ip0[-1] == match0[-1])) { ip0--; match0--; mLength++; } /* catch up */
+    } while (ip3 < ilimit);
 
-_match: /* Requires: ip0, match0, offcode */
-        /* Count the forward length */
-        mLength += ZSTD_count(ip0+mLength, match0+mLength, iend);
-        ZSTD_storeSeq(seqStore, (size_t)(ip0-anchor), anchor, iend, offcode, mLength-MINMATCH);
-        /* match found */
-        ip0 += mLength;
-        anchor = ip0;
+_cleanup:
+    /* Note that there are probably still a couple positions we could search.
+     * However, it seems to be a meaningful performance hit to try to search
+     * them. So let's not. */
 
-        if (ip0 <= ilimit) {
-            /* Fill Table */
-            assert(base+current0+2 > istart);  /* check base overflow */
-            hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2;  /* here because current+2 could be > iend-8 */
-            hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
-
-            if (offset_2 > 0) { /* offset_2==0 means offset_2 is invalidated */
-                while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - offset_2)) ) {
-                    /* store sequence */
-                    size_t const rLength = ZSTD_count(ip0+4, ip0+4-offset_2, iend) + 4;
-                    { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */
-                    hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);
-                    ip0 += rLength;
-                    ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, 0 /*offCode*/, rLength-MINMATCH);
-                    anchor = ip0;
-                    continue;   /* faster when present (confirmed on gcc-8) ... (?) */
-        }   }   }
-        ip1 = ip0 + 1;
-    }
+    /* When the repcodes are outside of the prefix, we set them to zero before the loop.
+     * When the offsets are still zero, we need to restore them after the block to have a correct
+     * repcode history. If only one offset was invalid, it is easy. The tricky case is when both
+     * offsets were invalid. We need to figure out which offset to refill with.
+     *     - If both offsets are zero they are in the same order.
+     *     - If both offsets are non-zero, we won't restore the offsets from `offsetSaved[12]`.
+     *     - If only one is zero, we need to decide which offset to restore.
+     *         - If rep_offset1 is non-zero, then rep_offset2 must be offsetSaved1.
+     *         - It is impossible for rep_offset2 to be non-zero.
+     *
+     * So if rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0), then
+     * set rep[0] = rep_offset1 and rep[1] = offsetSaved1.
+     */
+    offsetSaved2 = ((offsetSaved1 != 0) && (rep_offset1 != 0)) ? offsetSaved1 : offsetSaved2;
 
     /* save reps for next block */
-    rep[0] = offset_1 ? offset_1 : offsetSaved;
-    rep[1] = offset_2 ? offset_2 : offsetSaved;
+    rep[0] = rep_offset1 ? rep_offset1 : offsetSaved1;
+    rep[1] = rep_offset2 ? rep_offset2 : offsetSaved2;
 
     /* Return the last literals size */
     return (size_t)(iend - anchor);
+
+_offset: /* Requires: ip0, idx */
+
+    /* Compute the offset code. */
+    match0 = base + idx;
+    rep_offset2 = rep_offset1;
+    rep_offset1 = (U32)(ip0-match0);
+    offcode = OFFSET_TO_OFFBASE(rep_offset1);
+    mLength = 4;
+
+    /* Count the backwards match length. */
+    while (((ip0>anchor) & (match0>prefixStart)) && (ip0[-1] == match0[-1])) {
+        ip0--;
+        match0--;
+        mLength++;
+    }
+
+_match: /* Requires: ip0, match0, offcode */
+
+    /* Count the forward length. */
+    mLength += ZSTD_count(ip0 + mLength, match0 + mLength, iend);
+
+    ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength);
+
+    ip0 += mLength;
+    anchor = ip0;
+
+    /* Fill table and check for immediate repcode. */
+    if (ip0 <= ilimit) {
+        /* Fill Table */
+        assert(base+current0+2 > istart);  /* check base overflow */
+        hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2;  /* here because current+2 could be > iend-8 */
+        hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
+
+        if (rep_offset2 > 0) { /* rep_offset2==0 means rep_offset2 is invalidated */
+            while ( (ip0 <= ilimit) && (MEM_read32(ip0) == MEM_read32(ip0 - rep_offset2)) ) {
+                /* store sequence */
+                size_t const rLength = ZSTD_count(ip0+4, ip0+4-rep_offset2, iend) + 4;
+                { U32 const tmpOff = rep_offset2; rep_offset2 = rep_offset1; rep_offset1 = tmpOff; } /* swap rep_offset2 <=> rep_offset1 */
+                hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);
+                ip0 += rLength;
+                ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, REPCODE1_TO_OFFBASE, rLength);
+                anchor = ip0;
+                continue;   /* faster when present (confirmed on gcc-8) ... (?) */
+    }   }   }
+
+    goto _start;
 }
 
+#define ZSTD_GEN_FAST_FN(dictMode, mls, step)                                                            \
+    static size_t ZSTD_compressBlock_fast_##dictMode##_##mls##_##step(                                      \
+            ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],                    \
+            void const* src, size_t srcSize)                                                       \
+    {                                                                                              \
+        return ZSTD_compressBlock_fast_##dictMode##_generic(ms, seqStore, rep, src, srcSize, mls, step); \
+    }
+
+ZSTD_GEN_FAST_FN(noDict, 4, 1)
+ZSTD_GEN_FAST_FN(noDict, 5, 1)
+ZSTD_GEN_FAST_FN(noDict, 6, 1)
+ZSTD_GEN_FAST_FN(noDict, 7, 1)
+
+ZSTD_GEN_FAST_FN(noDict, 4, 0)
+ZSTD_GEN_FAST_FN(noDict, 5, 0)
+ZSTD_GEN_FAST_FN(noDict, 6, 0)
+ZSTD_GEN_FAST_FN(noDict, 7, 0)
 
 size_t ZSTD_compressBlock_fast(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -189,24 +425,40 @@
 {
     U32 const mls = ms->cParams.minMatch;
     assert(ms->dictMatchState == NULL);
-    switch(mls)
-    {
-    default: /* includes case 3 */
-    case 4 :
-        return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 4);
-    case 5 :
-        return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 5);
-    case 6 :
-        return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 6);
-    case 7 :
-        return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, 7);
+    if (ms->cParams.targetLength > 1) {
+        switch(mls)
+        {
+        default: /* includes case 3 */
+        case 4 :
+            return ZSTD_compressBlock_fast_noDict_4_1(ms, seqStore, rep, src, srcSize);
+        case 5 :
+            return ZSTD_compressBlock_fast_noDict_5_1(ms, seqStore, rep, src, srcSize);
+        case 6 :
+            return ZSTD_compressBlock_fast_noDict_6_1(ms, seqStore, rep, src, srcSize);
+        case 7 :
+            return ZSTD_compressBlock_fast_noDict_7_1(ms, seqStore, rep, src, srcSize);
+        }
+    } else {
+        switch(mls)
+        {
+        default: /* includes case 3 */
+        case 4 :
+            return ZSTD_compressBlock_fast_noDict_4_0(ms, seqStore, rep, src, srcSize);
+        case 5 :
+            return ZSTD_compressBlock_fast_noDict_5_0(ms, seqStore, rep, src, srcSize);
+        case 6 :
+            return ZSTD_compressBlock_fast_noDict_6_0(ms, seqStore, rep, src, srcSize);
+        case 7 :
+            return ZSTD_compressBlock_fast_noDict_7_0(ms, seqStore, rep, src, srcSize);
+        }
+
     }
 }
 
 FORCE_INLINE_TEMPLATE
 size_t ZSTD_compressBlock_fast_dictMatchState_generic(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
-        void const* src, size_t srcSize, U32 const mls)
+        void const* src, size_t srcSize, U32 const mls, U32 const hasStep)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     U32* const hashTable = ms->hashTable;
@@ -215,14 +467,14 @@
     U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
     const BYTE* const base = ms->window.base;
     const BYTE* const istart = (const BYTE*)src;
-    const BYTE* ip = istart;
+    const BYTE* ip0 = istart;
+    const BYTE* ip1 = ip0 + stepSize; /* we assert below that stepSize >= 1 */
     const BYTE* anchor = istart;
     const U32   prefixStartIndex = ms->window.dictLimit;
     const BYTE* const prefixStart = base + prefixStartIndex;
     const BYTE* const iend = istart + srcSize;
     const BYTE* const ilimit = iend - HASH_READ_SIZE;
     U32 offset_1=rep[0], offset_2=rep[1];
-    U32 offsetSaved = 0;
 
     const ZSTD_matchState_t* const dms = ms->dictMatchState;
     const ZSTD_compressionParameters* const dictCParams = &dms->cParams ;
@@ -232,125 +484,182 @@
     const BYTE* const dictStart    = dictBase + dictStartIndex;
     const BYTE* const dictEnd      = dms->window.nextSrc;
     const U32 dictIndexDelta       = prefixStartIndex - (U32)(dictEnd - dictBase);
-    const U32 dictAndPrefixLength  = (U32)(ip - prefixStart + dictEnd - dictStart);
-    const U32 dictHLog             = dictCParams->hashLog;
+    const U32 dictAndPrefixLength  = (U32)(istart - prefixStart + dictEnd - dictStart);
+    const U32 dictHBits            = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
 
     /* if a dictionary is still attached, it necessarily means that
      * it is within window size. So we just check it. */
     const U32 maxDistance = 1U << cParams->windowLog;
-    const U32 endIndex = (U32)((size_t)(ip - base) + srcSize);
+    const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
     assert(endIndex - prefixStartIndex <= maxDistance);
     (void)maxDistance; (void)endIndex;   /* these variables are not used when assert() is disabled */
 
+    (void)hasStep; /* not currently specialized on whether it's accelerated */
+
     /* ensure there will be no underflow
      * when translating a dict index into a local index */
     assert(prefixStartIndex >= (U32)(dictEnd - dictBase));
 
+    if (ms->prefetchCDictTables) {
+        size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32);
+        PREFETCH_AREA(dictHashTable, hashTableBytes)
+    }
+
     /* init */
     DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic");
-    ip += (dictAndPrefixLength == 0);
+    ip0 += (dictAndPrefixLength == 0);
     /* dictMatchState repCode checks don't currently handle repCode == 0
      * disabling. */
     assert(offset_1 <= dictAndPrefixLength);
     assert(offset_2 <= dictAndPrefixLength);
 
-    /* Main Search Loop */
-    while (ip < ilimit) {   /* < instead of <=, because repcode check at (ip+1) */
+    /* Outer search loop */
+    assert(stepSize >= 1);
+    while (ip1 <= ilimit) {   /* repcode check at (ip0 + 1) is safe because ip0 < ip1 */
         size_t mLength;
-        size_t const h = ZSTD_hashPtr(ip, hlog, mls);
-        U32 const curr = (U32)(ip-base);
-        U32 const matchIndex = hashTable[h];
-        const BYTE* match = base + matchIndex;
-        const U32 repIndex = curr + 1 - offset_1;
-        const BYTE* repMatch = (repIndex < prefixStartIndex) ?
-                               dictBase + (repIndex - dictIndexDelta) :
-                               base + repIndex;
-        hashTable[h] = curr;   /* update hash table */
+        size_t hash0 = ZSTD_hashPtr(ip0, hlog, mls);
 
-        if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
-          && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
-            const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
-            mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
-            ip++;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, mLength-MINMATCH);
-        } else if ( (matchIndex <= prefixStartIndex) ) {
-            size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls);
-            U32 const dictMatchIndex = dictHashTable[dictHash];
-            const BYTE* dictMatch = dictBase + dictMatchIndex;
-            if (dictMatchIndex <= dictStartIndex ||
-                MEM_read32(dictMatch) != MEM_read32(ip)) {
-                assert(stepSize >= 1);
-                ip += ((ip-anchor) >> kSearchStrength) + stepSize;
-                continue;
-            } else {
-                /* found a dict match */
-                U32 const offset = (U32)(curr-dictMatchIndex-dictIndexDelta);
-                mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4;
-                while (((ip>anchor) & (dictMatch>dictStart))
-                     && (ip[-1] == dictMatch[-1])) {
-                    ip--; dictMatch--; mLength++;
+        size_t const dictHashAndTag0 = ZSTD_hashPtr(ip0, dictHBits, mls);
+        U32 dictMatchIndexAndTag = dictHashTable[dictHashAndTag0 >> ZSTD_SHORT_CACHE_TAG_BITS];
+        int dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag0);
+
+        U32 matchIndex = hashTable[hash0];
+        U32 curr = (U32)(ip0 - base);
+        size_t step = stepSize;
+        const size_t kStepIncr = 1 << kSearchStrength;
+        const BYTE* nextStep = ip0 + kStepIncr;
+
+        /* Inner search loop */
+        while (1) {
+            const BYTE* match = base + matchIndex;
+            const U32 repIndex = curr + 1 - offset_1;
+            const BYTE* repMatch = (repIndex < prefixStartIndex) ?
+                                   dictBase + (repIndex - dictIndexDelta) :
+                                   base + repIndex;
+            const size_t hash1 = ZSTD_hashPtr(ip1, hlog, mls);
+            size_t const dictHashAndTag1 = ZSTD_hashPtr(ip1, dictHBits, mls);
+            hashTable[hash0] = curr;   /* update hash table */
+
+            if (((U32) ((prefixStartIndex - 1) - repIndex) >=
+                 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
+                && (MEM_read32(repMatch) == MEM_read32(ip0 + 1))) {
+                const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+                mLength = ZSTD_count_2segments(ip0 + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixStart) + 4;
+                ip0++;
+                ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
+                break;
+            }
+
+            if (dictTagsMatch) {
+                /* Found a possible dict match */
+                const U32 dictMatchIndex = dictMatchIndexAndTag >> ZSTD_SHORT_CACHE_TAG_BITS;
+                const BYTE* dictMatch = dictBase + dictMatchIndex;
+                if (dictMatchIndex > dictStartIndex &&
+                    MEM_read32(dictMatch) == MEM_read32(ip0)) {
+                    /* To replicate extDict parse behavior, we only use dict matches when the normal matchIndex is invalid */
+                    if (matchIndex <= prefixStartIndex) {
+                        U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta);
+                        mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4;
+                        while (((ip0 > anchor) & (dictMatch > dictStart))
+                            && (ip0[-1] == dictMatch[-1])) {
+                            ip0--;
+                            dictMatch--;
+                            mLength++;
+                        } /* catch up */
+                        offset_2 = offset_1;
+                        offset_1 = offset;
+                        ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+                        break;
+                    }
+                }
+            }
+
+            if (matchIndex > prefixStartIndex && MEM_read32(match) == MEM_read32(ip0)) {
+                /* found a regular match */
+                U32 const offset = (U32) (ip0 - match);
+                mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4;
+                while (((ip0 > anchor) & (match > prefixStart))
+                       && (ip0[-1] == match[-1])) {
+                    ip0--;
+                    match--;
+                    mLength++;
                 } /* catch up */
                 offset_2 = offset_1;
                 offset_1 = offset;
-                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
+                ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+                break;
             }
-        } else if (MEM_read32(match) != MEM_read32(ip)) {
-            /* it's not a match, and we're not going to check the dictionary */
-            assert(stepSize >= 1);
-            ip += ((ip-anchor) >> kSearchStrength) + stepSize;
-            continue;
-        } else {
-            /* found a regular match */
-            U32 const offset = (U32)(ip-match);
-            mLength = ZSTD_count(ip+4, match+4, iend) + 4;
-            while (((ip>anchor) & (match>prefixStart))
-                 && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
-            offset_2 = offset_1;
-            offset_1 = offset;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
-        }
+
+            /* Prepare for next iteration */
+            dictMatchIndexAndTag = dictHashTable[dictHashAndTag1 >> ZSTD_SHORT_CACHE_TAG_BITS];
+            dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag1);
+            matchIndex = hashTable[hash1];
+
+            if (ip1 >= nextStep) {
+                step++;
+                nextStep += kStepIncr;
+            }
+            ip0 = ip1;
+            ip1 = ip1 + step;
+            if (ip1 > ilimit) goto _cleanup;
+
+            curr = (U32)(ip0 - base);
+            hash0 = hash1;
+        }   /* end inner search loop */
 
         /* match found */
-        ip += mLength;
-        anchor = ip;
+        assert(mLength);
+        ip0 += mLength;
+        anchor = ip0;
 
-        if (ip <= ilimit) {
+        if (ip0 <= ilimit) {
             /* Fill Table */
             assert(base+curr+2 > istart);  /* check base overflow */
             hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2;  /* here because curr+2 could be > iend-8 */
-            hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
+            hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
 
             /* check immediate repcode */
-            while (ip <= ilimit) {
-                U32 const current2 = (U32)(ip-base);
+            while (ip0 <= ilimit) {
+                U32 const current2 = (U32)(ip0-base);
                 U32 const repIndex2 = current2 - offset_2;
                 const BYTE* repMatch2 = repIndex2 < prefixStartIndex ?
                         dictBase - dictIndexDelta + repIndex2 :
                         base + repIndex2;
                 if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
-                   && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+                   && (MEM_read32(repMatch2) == MEM_read32(ip0))) {
                     const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
-                    size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+                    size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
                     U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset;   /* swap offset_2 <=> offset_1 */
-                    ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, repLength2-MINMATCH);
-                    hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
-                    ip += repLength2;
-                    anchor = ip;
+                    ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
+                    hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = current2;
+                    ip0 += repLength2;
+                    anchor = ip0;
                     continue;
                 }
                 break;
             }
         }
+
+        /* Prepare for next iteration */
+        assert(ip0 == anchor);
+        ip1 = ip0 + stepSize;
     }
 
+_cleanup:
     /* save reps for next block */
-    rep[0] = offset_1 ? offset_1 : offsetSaved;
-    rep[1] = offset_2 ? offset_2 : offsetSaved;
+    rep[0] = offset_1;
+    rep[1] = offset_2;
 
     /* Return the last literals size */
     return (size_t)(iend - anchor);
 }
 
+
+ZSTD_GEN_FAST_FN(dictMatchState, 4, 0)
+ZSTD_GEN_FAST_FN(dictMatchState, 5, 0)
+ZSTD_GEN_FAST_FN(dictMatchState, 6, 0)
+ZSTD_GEN_FAST_FN(dictMatchState, 7, 0)
+
 size_t ZSTD_compressBlock_fast_dictMatchState(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize)
@@ -361,30 +670,29 @@
     {
     default: /* includes case 3 */
     case 4 :
-        return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 4);
+        return ZSTD_compressBlock_fast_dictMatchState_4_0(ms, seqStore, rep, src, srcSize);
     case 5 :
-        return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 5);
+        return ZSTD_compressBlock_fast_dictMatchState_5_0(ms, seqStore, rep, src, srcSize);
     case 6 :
-        return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 6);
+        return ZSTD_compressBlock_fast_dictMatchState_6_0(ms, seqStore, rep, src, srcSize);
     case 7 :
-        return ZSTD_compressBlock_fast_dictMatchState_generic(ms, seqStore, rep, src, srcSize, 7);
+        return ZSTD_compressBlock_fast_dictMatchState_7_0(ms, seqStore, rep, src, srcSize);
     }
 }
 
 
 static size_t ZSTD_compressBlock_fast_extDict_generic(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
-        void const* src, size_t srcSize, U32 const mls)
+        void const* src, size_t srcSize, U32 const mls, U32 const hasStep)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     U32* const hashTable = ms->hashTable;
     U32 const hlog = cParams->hashLog;
     /* support stepSize of 0 */
-    U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
+    size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1;
     const BYTE* const base = ms->window.base;
     const BYTE* const dictBase = ms->window.dictBase;
     const BYTE* const istart = (const BYTE*)src;
-    const BYTE* ip = istart;
     const BYTE* anchor = istart;
     const U32   endIndex = (U32)((size_t)(istart - base) + srcSize);
     const U32   lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog);
@@ -397,100 +705,256 @@
     const BYTE* const iend = istart + srcSize;
     const BYTE* const ilimit = iend - 8;
     U32 offset_1=rep[0], offset_2=rep[1];
+    U32 offsetSaved1 = 0, offsetSaved2 = 0;
+
+    const BYTE* ip0 = istart;
+    const BYTE* ip1;
+    const BYTE* ip2;
+    const BYTE* ip3;
+    U32 current0;
+
+
+    size_t hash0; /* hash for ip0 */
+    size_t hash1; /* hash for ip1 */
+    U32 idx; /* match idx for ip0 */
+    const BYTE* idxBase; /* base pointer for idx */
+
+    U32 offcode;
+    const BYTE* match0;
+    size_t mLength;
+    const BYTE* matchEnd = 0; /* initialize to avoid warning, assert != 0 later */
+
+    size_t step;
+    const BYTE* nextStep;
+    const size_t kStepIncr = (1 << (kSearchStrength - 1));
+
+    (void)hasStep; /* not currently specialized on whether it's accelerated */
 
     DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1);
 
     /* switch to "regular" variant if extDict is invalidated due to maxDistance */
     if (prefixStartIndex == dictStartIndex)
-        return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, mls);
+        return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize);
 
-    /* Search Loop */
-    while (ip < ilimit) {  /* < instead of <=, because (ip+1) */
-        const size_t h = ZSTD_hashPtr(ip, hlog, mls);
-        const U32    matchIndex = hashTable[h];
-        const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base;
-        const BYTE*  match = matchBase + matchIndex;
-        const U32    curr = (U32)(ip-base);
-        const U32    repIndex = curr + 1 - offset_1;
-        const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
-        const BYTE* const repMatch = repBase + repIndex;
-        hashTable[h] = curr;   /* update hash table */
-        DEBUGLOG(7, "offset_1 = %u , curr = %u", offset_1, curr);
+    {   U32 const curr = (U32)(ip0 - base);
+        U32 const maxRep = curr - dictStartIndex;
+        if (offset_2 >= maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+        if (offset_1 >= maxRep) offsetSaved1 = offset_1, offset_1 = 0;
+    }
 
-        if ( ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */
-             & (offset_1 < curr+1 - dictStartIndex) ) /* note: we are searching at curr+1 */
-           && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
-            const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
-            size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4;
-            ip++;
-            ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, 0, rLength-MINMATCH);
-            ip += rLength;
-            anchor = ip;
-        } else {
-            if ( (matchIndex < dictStartIndex) ||
-                 (MEM_read32(match) != MEM_read32(ip)) ) {
-                assert(stepSize >= 1);
-                ip += ((ip-anchor) >> kSearchStrength) + stepSize;
-                continue;
+    /* start each op */
+_start: /* Requires: ip0 */
+
+    step = stepSize;
+    nextStep = ip0 + kStepIncr;
+
+    /* calculate positions, ip0 - anchor == 0, so we skip step calc */
+    ip1 = ip0 + 1;
+    ip2 = ip0 + step;
+    ip3 = ip2 + 1;
+
+    if (ip3 >= ilimit) {
+        goto _cleanup;
+    }
+
+    hash0 = ZSTD_hashPtr(ip0, hlog, mls);
+    hash1 = ZSTD_hashPtr(ip1, hlog, mls);
+
+    idx = hashTable[hash0];
+    idxBase = idx < prefixStartIndex ? dictBase : base;
+
+    do {
+        {   /* load repcode match for ip[2] */
+            U32 const current2 = (U32)(ip2 - base);
+            U32 const repIndex = current2 - offset_1;
+            const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
+            U32 rval;
+            if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
+                 & (offset_1 > 0) ) {
+                rval = MEM_read32(repBase + repIndex);
+            } else {
+                rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
             }
-            {   const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
-                const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
-                U32 const offset = curr - matchIndex;
-                size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
-                while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; }   /* catch up */
-                offset_2 = offset_1; offset_1 = offset;  /* update offset history */
-                ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, offset + ZSTD_REP_MOVE, mLength-MINMATCH);
-                ip += mLength;
-                anchor = ip;
+
+            /* write back hash table entry */
+            current0 = (U32)(ip0 - base);
+            hashTable[hash0] = current0;
+
+            /* check repcode at ip[2] */
+            if (MEM_read32(ip2) == rval) {
+                ip0 = ip2;
+                match0 = repBase + repIndex;
+                matchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+                assert((match0 != prefixStart) & (match0 != dictStart));
+                mLength = ip0[-1] == match0[-1];
+                ip0 -= mLength;
+                match0 -= mLength;
+                offcode = REPCODE1_TO_OFFBASE;
+                mLength += 4;
+                goto _match;
         }   }
 
-        if (ip <= ilimit) {
-            /* Fill Table */
-            hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2;
-            hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
-            /* check immediate repcode */
-            while (ip <= ilimit) {
-                U32 const current2 = (U32)(ip-base);
-                U32 const repIndex2 = current2 - offset_2;
-                const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
-                if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 < curr - dictStartIndex))  /* intentional overflow */
-                   && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
-                    const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
-                    size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
-                    { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; }  /* swap offset_2 <=> offset_1 */
-                    ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, 0 /*offcode*/, repLength2-MINMATCH);
-                    hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
-                    ip += repLength2;
-                    anchor = ip;
-                    continue;
-                }
-                break;
-    }   }   }
+        {   /* load match for ip[0] */
+            U32 const mval = idx >= dictStartIndex ?
+                    MEM_read32(idxBase + idx) :
+                    MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+
+            /* check match at ip[0] */
+            if (MEM_read32(ip0) == mval) {
+                /* found a match! */
+                goto _offset;
+        }   }
+
+        /* lookup ip[1] */
+        idx = hashTable[hash1];
+        idxBase = idx < prefixStartIndex ? dictBase : base;
+
+        /* hash ip[2] */
+        hash0 = hash1;
+        hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+        /* advance to next positions */
+        ip0 = ip1;
+        ip1 = ip2;
+        ip2 = ip3;
+
+        /* write back hash table entry */
+        current0 = (U32)(ip0 - base);
+        hashTable[hash0] = current0;
+
+        {   /* load match for ip[0] */
+            U32 const mval = idx >= dictStartIndex ?
+                    MEM_read32(idxBase + idx) :
+                    MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+
+            /* check match at ip[0] */
+            if (MEM_read32(ip0) == mval) {
+                /* found a match! */
+                goto _offset;
+        }   }
+
+        /* lookup ip[1] */
+        idx = hashTable[hash1];
+        idxBase = idx < prefixStartIndex ? dictBase : base;
+
+        /* hash ip[2] */
+        hash0 = hash1;
+        hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+        /* advance to next positions */
+        ip0 = ip1;
+        ip1 = ip2;
+        ip2 = ip0 + step;
+        ip3 = ip1 + step;
+
+        /* calculate step */
+        if (ip2 >= nextStep) {
+            step++;
+            PREFETCH_L1(ip1 + 64);
+            PREFETCH_L1(ip1 + 128);
+            nextStep += kStepIncr;
+        }
+    } while (ip3 < ilimit);
+
+_cleanup:
+    /* Note that there are probably still a couple positions we could search.
+     * However, it seems to be a meaningful performance hit to try to search
+     * them. So let's not. */
+
+    /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+     * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+    offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
 
     /* save reps for next block */
-    rep[0] = offset_1;
-    rep[1] = offset_2;
+    rep[0] = offset_1 ? offset_1 : offsetSaved1;
+    rep[1] = offset_2 ? offset_2 : offsetSaved2;
 
     /* Return the last literals size */
     return (size_t)(iend - anchor);
+
+_offset: /* Requires: ip0, idx, idxBase */
+
+    /* Compute the offset code. */
+    {   U32 const offset = current0 - idx;
+        const BYTE* const lowMatchPtr = idx < prefixStartIndex ? dictStart : prefixStart;
+        matchEnd = idx < prefixStartIndex ? dictEnd : iend;
+        match0 = idxBase + idx;
+        offset_2 = offset_1;
+        offset_1 = offset;
+        offcode = OFFSET_TO_OFFBASE(offset);
+        mLength = 4;
+
+        /* Count the backwards match length. */
+        while (((ip0>anchor) & (match0>lowMatchPtr)) && (ip0[-1] == match0[-1])) {
+            ip0--;
+            match0--;
+            mLength++;
+    }   }
+
+_match: /* Requires: ip0, match0, offcode, matchEnd */
+
+    /* Count the forward length. */
+    assert(matchEnd != 0);
+    mLength += ZSTD_count_2segments(ip0 + mLength, match0 + mLength, iend, matchEnd, prefixStart);
+
+    ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength);
+
+    ip0 += mLength;
+    anchor = ip0;
+
+    /* write next hash table entry */
+    if (ip1 < ip0) {
+        hashTable[hash1] = (U32)(ip1 - base);
+    }
+
+    /* Fill table and check for immediate repcode. */
+    if (ip0 <= ilimit) {
+        /* Fill Table */
+        assert(base+current0+2 > istart);  /* check base overflow */
+        hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2;  /* here because current+2 could be > iend-8 */
+        hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
+
+        while (ip0 <= ilimit) {
+            U32 const repIndex2 = (U32)(ip0-base) - offset_2;
+            const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
+            if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0))  /* intentional underflow */
+                 && (MEM_read32(repMatch2) == MEM_read32(ip0)) ) {
+                const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
+                size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+                { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; }  /* swap offset_2 <=> offset_1 */
+                ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
+                hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);
+                ip0 += repLength2;
+                anchor = ip0;
+                continue;
+            }
+            break;
+    }   }
+
+    goto _start;
 }
 
+ZSTD_GEN_FAST_FN(extDict, 4, 0)
+ZSTD_GEN_FAST_FN(extDict, 5, 0)
+ZSTD_GEN_FAST_FN(extDict, 6, 0)
+ZSTD_GEN_FAST_FN(extDict, 7, 0)
 
 size_t ZSTD_compressBlock_fast_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize)
 {
     U32 const mls = ms->cParams.minMatch;
+    assert(ms->dictMatchState == NULL);
     switch(mls)
     {
     default: /* includes case 3 */
     case 4 :
-        return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 4);
+        return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize);
     case 5 :
-        return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 5);
+        return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize);
     case 6 :
-        return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 6);
+        return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize);
     case 7 :
-        return ZSTD_compressBlock_fast_extDict_generic(ms, seqStore, rep, src, srcSize, 7);
+        return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize);
     }
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.h b/Utilities/cmzstd/lib/compress/zstd_fast.h
index 0d4a0c1..9e4236b 100644
--- a/Utilities/cmzstd/lib/compress/zstd_fast.h
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -19,7 +19,8 @@
 #include "zstd_compress_internal.h"
 
 void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
-                        void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+                        void const* end, ZSTD_dictTableLoadMethod_e dtlm,
+                        ZSTD_tableFillPurpose_e tfp);
 size_t ZSTD_compressBlock_fast(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.c b/Utilities/cmzstd/lib/compress/zstd_lazy.c
index 6ce897d..5ba88e8 100644
--- a/Utilities/cmzstd/lib/compress/zstd_lazy.c
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -10,6 +10,9 @@
 
 #include "zstd_compress_internal.h"
 #include "zstd_lazy.h"
+#include "../common/bits.h" /* ZSTD_countTrailingZeros64 */
+
+#define kLazySkippingStep 8
 
 
 /*-*************************************
@@ -61,7 +64,7 @@
  *  assumption : curr >= btlow == (curr - btmask)
  *  doesn't fail */
 static void
-ZSTD_insertDUBT1(ZSTD_matchState_t* ms,
+ZSTD_insertDUBT1(const ZSTD_matchState_t* ms,
                  U32 curr, const BYTE* inputEnd,
                  U32 nbCompares, U32 btLow,
                  const ZSTD_dictMode_e dictMode)
@@ -93,7 +96,7 @@
     assert(curr >= btLow);
     assert(ip < iend);   /* condition for ZSTD_count */
 
-    while (nbCompares-- && (matchIndex > windowLow)) {
+    for (; nbCompares && (matchIndex > windowLow); --nbCompares) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
         assert(matchIndex < curr);
@@ -151,7 +154,7 @@
 
 static size_t
 ZSTD_DUBT_findBetterDictMatch (
-        ZSTD_matchState_t* ms,
+        const ZSTD_matchState_t* ms,
         const BYTE* const ip, const BYTE* const iend,
         size_t* offsetPtr,
         size_t bestLength,
@@ -185,7 +188,7 @@
     (void)dictMode;
     assert(dictMode == ZSTD_dictMatchState);
 
-    while (nbCompares-- && (dictMatchIndex > dictLowLimit)) {
+    for (; nbCompares && (dictMatchIndex > dictLowLimit); --nbCompares) {
         U32* const nextPtr = dictBt + 2*(dictMatchIndex & btMask);
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
         const BYTE* match = dictBase + dictMatchIndex;
@@ -197,8 +200,8 @@
             U32 matchIndex = dictMatchIndex + dictIndexDelta;
             if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) {
                 DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)",
-                    curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + curr - matchIndex, dictMatchIndex, matchIndex);
-                bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex;
+                    curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, OFFSET_TO_OFFBASE(curr - matchIndex), dictMatchIndex, matchIndex);
+                bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
             }
             if (ip+matchLength == iend) {   /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */
                 break;   /* drop, to guarantee consistency (miss a little bit of compression) */
@@ -218,7 +221,7 @@
     }
 
     if (bestLength >= MINMATCH) {
-        U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+        U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex;
         DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
                     curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
     }
@@ -230,7 +233,7 @@
 static size_t
 ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
                         const BYTE* const ip, const BYTE* const iend,
-                        size_t* offsetPtr,
+                        size_t* offBasePtr,
                         U32 const mls,
                         const ZSTD_dictMode_e dictMode)
 {
@@ -309,7 +312,7 @@
         matchIndex  = hashTable[h];
         hashTable[h] = curr;   /* Update Hash Table */
 
-        while (nbCompares-- && (matchIndex > windowLow)) {
+        for (; nbCompares && (matchIndex > windowLow); --nbCompares) {
             U32* const nextPtr = bt + 2*(matchIndex & btMask);
             size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
             const BYTE* match;
@@ -327,8 +330,8 @@
             if (matchLength > bestLength) {
                 if (matchLength > matchEndIdx - matchIndex)
                     matchEndIdx = matchIndex + (U32)matchLength;
-                if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
-                    bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex;
+                if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr - matchIndex + 1) - ZSTD_highbit32((U32)*offBasePtr)) )
+                    bestLength = matchLength, *offBasePtr = OFFSET_TO_OFFBASE(curr - matchIndex);
                 if (ip+matchLength == iend) {   /* equal : no way to know if inf or sup */
                     if (dictMode == ZSTD_dictMatchState) {
                         nbCompares = 0; /* in addition to avoiding checking any
@@ -357,19 +360,20 @@
 
         *smallerPtr = *largerPtr = 0;
 
+        assert(nbCompares <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */
         if (dictMode == ZSTD_dictMatchState && nbCompares) {
             bestLength = ZSTD_DUBT_findBetterDictMatch(
                     ms, ip, iend,
-                    offsetPtr, bestLength, nbCompares,
+                    offBasePtr, bestLength, nbCompares,
                     mls, dictMode);
         }
 
         assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */
         ms->nextToUpdate = matchEndIdx - 8;   /* skip repetitive patterns */
         if (bestLength >= MINMATCH) {
-            U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+            U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offBasePtr); (void)mIndex;
             DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
-                        curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
+                        curr, (U32)bestLength, (U32)*offBasePtr, mIndex);
         }
         return bestLength;
     }
@@ -380,62 +384,14 @@
 FORCE_INLINE_TEMPLATE size_t
 ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms,
                 const BYTE* const ip, const BYTE* const iLimit,
-                      size_t* offsetPtr,
+                      size_t* offBasePtr,
                 const U32 mls /* template */,
                 const ZSTD_dictMode_e dictMode)
 {
     DEBUGLOG(7, "ZSTD_BtFindBestMatch");
     if (ip < ms->window.base + ms->nextToUpdate) return 0;   /* skipped area */
     ZSTD_updateDUBT(ms, ip, iLimit, mls);
-    return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode);
-}
-
-
-static size_t
-ZSTD_BtFindBestMatch_selectMLS (  ZSTD_matchState_t* ms,
-                            const BYTE* ip, const BYTE* const iLimit,
-                                  size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict);
-    case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict);
-    case 7 :
-    case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict);
-    }
-}
-
-
-static size_t ZSTD_BtFindBestMatch_dictMatchState_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState);
-    case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState);
-    case 7 :
-    case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState);
-    }
-}
-
-
-static size_t ZSTD_BtFindBestMatch_extDict_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict);
-    case 5 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict);
-    case 7 :
-    case 6 : return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict);
-    }
+    return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offBasePtr, mls, dictMode);
 }
 
 /***********************************
@@ -450,7 +406,7 @@
     U32* const chainTable = ms->chainTable;
     U32 const chainSize = 1 << ms->cParams.chainLog;
     U32 idx = ms->nextToUpdate;
-    U32 const minChain = chainSize < target ? target - chainSize : idx;
+    U32 const minChain = chainSize < target - idx ? target - chainSize : idx;
     U32 const bucketSize = 1 << ZSTD_LAZY_DDSS_BUCKET_LOG;
     U32 const cacheSize = bucketSize - 1;
     U32 const chainAttempts = (1 << ms->cParams.searchLog) - cacheSize;
@@ -464,7 +420,7 @@
     U32 const hashLog = ms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG;
     U32* const tmpHashTable = hashTable;
     U32* const tmpChainTable = hashTable + ((size_t)1 << hashLog);
-    U32 const tmpChainSize = ((1 << ZSTD_LAZY_DDSS_BUCKET_LOG) - 1) << hashLog;
+    U32 const tmpChainSize = (U32)((1 << ZSTD_LAZY_DDSS_BUCKET_LOG) - 1) << hashLog;
     U32 const tmpMinChain = tmpChainSize < target ? target - tmpChainSize : idx;
     U32 hashIdx;
 
@@ -608,7 +564,7 @@
         /* save best solution */
         if (currentMl > ml) {
             ml = currentMl;
-            *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE;
+            *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta));
             if (ip+currentMl == iLimit) {
                 /* best possible, avoids read overflow on next attempt */
                 return ml;
@@ -645,7 +601,7 @@
             /* save best solution */
             if (currentMl > ml) {
                 ml = currentMl;
-                *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE;
+                *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta));
                 if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
             }
         }
@@ -664,7 +620,7 @@
 FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal(
                         ZSTD_matchState_t* ms,
                         const ZSTD_compressionParameters* const cParams,
-                        const BYTE* ip, U32 const mls)
+                        const BYTE* ip, U32 const mls, U32 const lazySkipping)
 {
     U32* const hashTable  = ms->hashTable;
     const U32 hashLog = cParams->hashLog;
@@ -679,6 +635,9 @@
         NEXT_IN_CHAIN(idx, chainMask) = hashTable[h];
         hashTable[h] = idx;
         idx++;
+        /* Stop inserting every position when in the lazy skipping mode. */
+        if (lazySkipping)
+            break;
     }
 
     ms->nextToUpdate = target;
@@ -687,12 +646,12 @@
 
 U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip) {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
-    return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch);
+    return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch, /* lazySkipping*/ 0);
 }
 
 /* inlining is important to hardwire a hot branch (template emulation) */
 FORCE_INLINE_TEMPLATE
-size_t ZSTD_HcFindBestMatch_generic (
+size_t ZSTD_HcFindBestMatch(
                         ZSTD_matchState_t* ms,
                         const BYTE* const ip, const BYTE* const iLimit,
                         size_t* offsetPtr,
@@ -731,14 +690,15 @@
     }
 
     /* HC4 match finder */
-    matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls);
+    matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls, ms->lazySkipping);
 
     for ( ; (matchIndex>=lowLimit) & (nbAttempts>0) ; nbAttempts--) {
         size_t currentMl=0;
         if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
             const BYTE* const match = base + matchIndex;
             assert(matchIndex >= dictLimit);   /* ensures this is true if dictMode != ZSTD_extDict */
-            if (match[ml] == ip[ml])   /* potentially better */
+            /* read 4B starting from (match + ml + 1 - sizeof(U32)) */
+            if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3))   /* potentially better */
                 currentMl = ZSTD_count(ip, match, iLimit);
         } else {
             const BYTE* const match = dictBase + matchIndex;
@@ -750,7 +710,7 @@
         /* save best solution */
         if (currentMl > ml) {
             ml = currentMl;
-            *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE;
+            *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
             if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
         }
 
@@ -758,6 +718,7 @@
         matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask);
     }
 
+    assert(nbAttempts <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */
     if (dictMode == ZSTD_dedicatedDictSearch) {
         ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts, dms,
                                                   ip, iLimit, prefixStart, curr, dictLimit, ddsIdx);
@@ -784,7 +745,8 @@
             /* save best solution */
             if (currentMl > ml) {
                 ml = currentMl;
-                *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
+                assert(curr > matchIndex + dmsIndexDelta);
+                *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta));
                 if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
             }
 
@@ -797,321 +759,34 @@
     return ml;
 }
 
-
-FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_noDict);
-    case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_noDict);
-    case 7 :
-    case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_noDict);
-    }
-}
-
-
-static size_t ZSTD_HcFindBestMatch_dictMatchState_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dictMatchState);
-    case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dictMatchState);
-    case 7 :
-    case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dictMatchState);
-    }
-}
-
-
-static size_t ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dedicatedDictSearch);
-    case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dedicatedDictSearch);
-    case 7 :
-    case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dedicatedDictSearch);
-    }
-}
-
-
-FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_extDict);
-    case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_extDict);
-    case 7 :
-    case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_extDict);
-    }
-}
-
 /* *********************************
 * (SIMD) Row-based matchfinder
 ***********************************/
 /* Constants for row-based hash */
-#define ZSTD_ROW_HASH_TAG_OFFSET 1                               /* byte offset of hashes in the match state's tagTable from the beginning of a row */
-#define ZSTD_ROW_HASH_TAG_BITS 8                                 /* nb bits to use for the tag */
 #define ZSTD_ROW_HASH_TAG_MASK ((1u << ZSTD_ROW_HASH_TAG_BITS) - 1)
+#define ZSTD_ROW_HASH_MAX_ENTRIES 64    /* absolute maximum number of entries per row, for all configurations */
 
 #define ZSTD_ROW_HASH_CACHE_MASK (ZSTD_ROW_HASH_CACHE_SIZE - 1)
 
-typedef U32 ZSTD_VecMask;   /* Clarifies when we are interacting with a U32 representing a mask of matches */
-
-#if !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) /* SIMD SSE version */
-
-#include <emmintrin.h>
-typedef __m128i ZSTD_Vec128;
-
-/* Returns a 128-bit container with 128-bits from src */
-static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
-  return _mm_loadu_si128((ZSTD_Vec128 const*)src);
-}
-
-/* Returns a ZSTD_Vec128 with the byte "val" packed 16 times */
-static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
-  return _mm_set1_epi8((char)val);
-}
-
-/* Do byte-by-byte comparison result of x and y. Then collapse 128-bit resultant mask
- * into a 32-bit mask that is the MSB of each byte.
- * */
-static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
-  return (ZSTD_VecMask)_mm_movemask_epi8(_mm_cmpeq_epi8(x, y));
-}
-
-typedef struct {
-  __m128i fst;
-  __m128i snd;
-} ZSTD_Vec256;
-
-static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) {
-  ZSTD_Vec256 v;
-  v.fst = ZSTD_Vec128_read(ptr);
-  v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1);
-  return v;
-}
-
-static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
-  ZSTD_Vec256 v;
-  v.fst = ZSTD_Vec128_set8(val);
-  v.snd = ZSTD_Vec128_set8(val);
-  return v;
-}
-
-static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
-  ZSTD_VecMask fstMask;
-  ZSTD_VecMask sndMask;
-  fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst);
-  sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd);
-  return fstMask | (sndMask << 16);
-}
-
-#elif !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) /* SIMD ARM NEON Version */
-
-#include <arm_neon.h>
-typedef uint8x16_t ZSTD_Vec128;
-
-static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
-  return vld1q_u8((const BYTE* const)src);
-}
-
-static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
-  return vdupq_n_u8(val);
-}
-
-/* Mimics '_mm_movemask_epi8()' from SSE */
-static U32 ZSTD_vmovmaskq_u8(ZSTD_Vec128 val) {
-    /* Shift out everything but the MSB bits in each byte */
-    uint16x8_t highBits = vreinterpretq_u16_u8(vshrq_n_u8(val, 7));
-    /* Merge the even lanes together with vsra (right shift and add) */
-    uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(highBits, highBits, 7));
-    uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14));
-    uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28));
-    /* Extract the low 8 bits from each lane, merge */
-    return vgetq_lane_u8(paired64, 0) | ((U32)vgetq_lane_u8(paired64, 8) << 8);
-}
-
-static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
-  return (ZSTD_VecMask)ZSTD_vmovmaskq_u8(vceqq_u8(x, y));
-}
-
-typedef struct {
-    uint8x16_t fst;
-    uint8x16_t snd;
-} ZSTD_Vec256;
-
-static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) {
-  ZSTD_Vec256 v;
-  v.fst = ZSTD_Vec128_read(ptr);
-  v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1);
-  return v;
-}
-
-static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
-  ZSTD_Vec256 v;
-  v.fst = ZSTD_Vec128_set8(val);
-  v.snd = ZSTD_Vec128_set8(val);
-  return v;
-}
-
-static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
-  ZSTD_VecMask fstMask;
-  ZSTD_VecMask sndMask;
-  fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst);
-  sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd);
-  return fstMask | (sndMask << 16);
-}
-
-#else /* Scalar fallback version */
-
-#define VEC128_NB_SIZE_T (16 / sizeof(size_t))
-typedef struct {
-    size_t vec[VEC128_NB_SIZE_T];
-} ZSTD_Vec128;
-
-static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
-    ZSTD_Vec128 ret;
-    ZSTD_memcpy(ret.vec, src, VEC128_NB_SIZE_T*sizeof(size_t));
-    return ret;
-}
-
-static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
-    ZSTD_Vec128 ret = { {0} };
-    int startBit = sizeof(size_t) * 8 - 8;
-    for (;startBit >= 0; startBit -= 8) {
-        unsigned j = 0;
-        for (;j < VEC128_NB_SIZE_T; ++j) {
-            ret.vec[j] |= ((size_t)val << startBit);
-        }
-    }
-    return ret;
-}
-
-/* Compare x to y, byte by byte, generating a "matches" bitfield */
-static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
-    ZSTD_VecMask res = 0;
-    unsigned i = 0;
-    unsigned l = 0;
-    for (; i < VEC128_NB_SIZE_T; ++i) {
-        const size_t cmp1 = x.vec[i];
-        const size_t cmp2 = y.vec[i];
-        unsigned j = 0;
-        for (; j < sizeof(size_t); ++j, ++l) {
-            if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) {
-                res |= ((U32)1 << (j+i*sizeof(size_t)));
-            }
-        }
-    }
-    return res;
-}
-
-#define VEC256_NB_SIZE_T 2*VEC128_NB_SIZE_T
-typedef struct {
-    size_t vec[VEC256_NB_SIZE_T];
-} ZSTD_Vec256;
-
-static ZSTD_Vec256 ZSTD_Vec256_read(const void* const src) {
-    ZSTD_Vec256 ret;
-    ZSTD_memcpy(ret.vec, src, VEC256_NB_SIZE_T*sizeof(size_t));
-    return ret;
-}
-
-static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
-    ZSTD_Vec256 ret = { {0} };
-    int startBit = sizeof(size_t) * 8 - 8;
-    for (;startBit >= 0; startBit -= 8) {
-        unsigned j = 0;
-        for (;j < VEC256_NB_SIZE_T; ++j) {
-            ret.vec[j] |= ((size_t)val << startBit);
-        }
-    }
-    return ret;
-}
-
-/* Compare x to y, byte by byte, generating a "matches" bitfield */
-static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
-    ZSTD_VecMask res = 0;
-    unsigned i = 0;
-    unsigned l = 0;
-    for (; i < VEC256_NB_SIZE_T; ++i) {
-        const size_t cmp1 = x.vec[i];
-        const size_t cmp2 = y.vec[i];
-        unsigned j = 0;
-        for (; j < sizeof(size_t); ++j, ++l) {
-            if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) {
-                res |= ((U32)1 << (j+i*sizeof(size_t)));
-            }
-        }
-    }
-    return res;
-}
-
-#endif /* !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) */
+typedef U64 ZSTD_VecMask;   /* Clarifies when we are interacting with a U64 representing a mask of matches */
 
 /* ZSTD_VecMask_next():
  * Starting from the LSB, returns the idx of the next non-zero bit.
  * Basically counting the nb of trailing zeroes.
  */
-static U32 ZSTD_VecMask_next(ZSTD_VecMask val) {
-#   if defined(_MSC_VER)   /* Visual */
-    unsigned long r=0;
-    return _BitScanForward(&r, val) ? (U32)r : 0;
-#   elif defined(__GNUC__) && (__GNUC__ >= 3)
-    return (U32)__builtin_ctz(val);
-#   else
-    /* Software ctz version: http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup */
-    static const U32 multiplyDeBruijnBitPosition[32] =
-    {
-        0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
-		31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
-    };
-	return multiplyDeBruijnBitPosition[((U32)((val & -(int)val) * 0x077CB531U)) >> 27];
-
-#   endif
-}
-
-/* ZSTD_VecMask_rotateRight():
- * Rotates a bitfield to the right by "rotation" bits.
- * If the rotation is greater than totalBits, the returned mask is 0.
- */
-FORCE_INLINE_TEMPLATE ZSTD_VecMask
-ZSTD_VecMask_rotateRight(ZSTD_VecMask mask, U32 const rotation, U32 const totalBits) {
-  if (rotation == 0)
-    return mask;
-  switch (totalBits) {
-    default:
-      assert(0);
-    case 16:
-      return (mask >> rotation) | (U16)(mask << (16 - rotation));
-    case 32:
-      return (mask >> rotation) | (U32)(mask << (32 - rotation));
-  }
+MEM_STATIC U32 ZSTD_VecMask_next(ZSTD_VecMask val) {
+    return ZSTD_countTrailingZeros64(val);
 }
 
 /* ZSTD_row_nextIndex():
  * Returns the next index to insert at within a tagTable row, and updates the "head"
- * value to reflect the update. Essentially cycles backwards from [0, {entries per row})
+ * value to reflect the update. Essentially cycles backwards from [1, {entries per row})
  */
 FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextIndex(BYTE* const tagRow, U32 const rowMask) {
-  U32 const next = (*tagRow - 1) & rowMask;
-  *tagRow = (BYTE)next;
-  return next;
+    U32 next = (*tagRow-1) & rowMask;
+    next += (next == 0) ? rowMask : 0; /* skip first position */
+    *tagRow = (BYTE)next;
+    return next;
 }
 
 /* ZSTD_isAligned():
@@ -1125,33 +800,37 @@
 /* ZSTD_row_prefetch():
  * Performs prefetching for the hashTable and tagTable at a given row.
  */
-FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, U16 const* tagTable, U32 const relRow, U32 const rowLog) {
+FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, BYTE const* tagTable, U32 const relRow, U32 const rowLog) {
     PREFETCH_L1(hashTable + relRow);
-    if (rowLog == 5) {
+    if (rowLog >= 5) {
         PREFETCH_L1(hashTable + relRow + 16);
+        /* Note: prefetching more of the hash table does not appear to be beneficial for 128-entry rows */
     }
     PREFETCH_L1(tagTable + relRow);
-    assert(rowLog == 4 || rowLog == 5);
+    if (rowLog == 6) {
+        PREFETCH_L1(tagTable + relRow + 32);
+    }
+    assert(rowLog == 4 || rowLog == 5 || rowLog == 6);
     assert(ZSTD_isAligned(hashTable + relRow, 64));                 /* prefetched hash row always 64-byte aligned */
-    assert(ZSTD_isAligned(tagTable + relRow, (size_t)1 << rowLog)); /* prefetched tagRow sits on a multiple of 32 or 64 bytes */
+    assert(ZSTD_isAligned(tagTable + relRow, (size_t)1 << rowLog)); /* prefetched tagRow sits on correct multiple of bytes (32,64,128) */
 }
 
 /* ZSTD_row_fillHashCache():
  * Fill up the hash cache starting at idx, prefetching up to ZSTD_ROW_HASH_CACHE_SIZE entries,
  * but not beyond iLimit.
  */
-static void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const BYTE* base,
+FORCE_INLINE_TEMPLATE void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const BYTE* base,
                                    U32 const rowLog, U32 const mls,
                                    U32 idx, const BYTE* const iLimit)
 {
     U32 const* const hashTable = ms->hashTable;
-    U16 const* const tagTable = ms->tagTable;
+    BYTE const* const tagTable = ms->tagTable;
     U32 const hashLog = ms->rowHashLog;
     U32 const maxElemsToPrefetch = (base + idx) > iLimit ? 0 : (U32)(iLimit - (base + idx) + 1);
     U32 const lim = idx + MIN(ZSTD_ROW_HASH_CACHE_SIZE, maxElemsToPrefetch);
 
     for (; idx < lim; ++idx) {
-        U32 const hash = (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+        U32 const hash = (U32)ZSTD_hashPtrSalted(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt);
         U32 const row = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
         ZSTD_row_prefetch(hashTable, tagTable, row, rowLog);
         ms->hashCache[idx & ZSTD_ROW_HASH_CACHE_MASK] = hash;
@@ -1167,11 +846,12 @@
  * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable.
  */
 FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextCachedHash(U32* cache, U32 const* hashTable,
-                                                  U16 const* tagTable, BYTE const* base,
+                                                  BYTE const* tagTable, BYTE const* base,
                                                   U32 idx, U32 const hashLog,
-                                                  U32 const rowLog, U32 const mls)
+                                                  U32 const rowLog, U32 const mls,
+                                                  U64 const hashSalt)
 {
-    U32 const newHash = (U32)ZSTD_hashPtr(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+    U32 const newHash = (U32)ZSTD_hashPtrSalted(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt);
     U32 const row = (newHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
     ZSTD_row_prefetch(hashTable, tagTable, row, rowLog);
     {   U32 const hash = cache[idx & ZSTD_ROW_HASH_CACHE_MASK];
@@ -1180,35 +860,64 @@
     }
 }
 
+/* ZSTD_row_update_internalImpl():
+ * Updates the hash table with positions starting from updateStartIdx until updateEndIdx.
+ */
+FORCE_INLINE_TEMPLATE void ZSTD_row_update_internalImpl(ZSTD_matchState_t* ms,
+                                                        U32 updateStartIdx, U32 const updateEndIdx,
+                                                        U32 const mls, U32 const rowLog,
+                                                        U32 const rowMask, U32 const useCache)
+{
+    U32* const hashTable = ms->hashTable;
+    BYTE* const tagTable = ms->tagTable;
+    U32 const hashLog = ms->rowHashLog;
+    const BYTE* const base = ms->window.base;
+
+    DEBUGLOG(6, "ZSTD_row_update_internalImpl(): updateStartIdx=%u, updateEndIdx=%u", updateStartIdx, updateEndIdx);
+    for (; updateStartIdx < updateEndIdx; ++updateStartIdx) {
+        U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, updateStartIdx, hashLog, rowLog, mls, ms->hashSalt)
+                                  : (U32)ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt);
+        U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+        U32* const row = hashTable + relRow;
+        BYTE* tagRow = tagTable + relRow;
+        U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask);
+
+        assert(hash == ZSTD_hashPtrSalted(base + updateStartIdx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, ms->hashSalt));
+        tagRow[pos] = hash & ZSTD_ROW_HASH_TAG_MASK;
+        row[pos] = updateStartIdx;
+    }
+}
+
 /* ZSTD_row_update_internal():
- * Inserts the byte at ip into the appropriate position in the hash table.
- * Determines the relative row, and the position within the {16, 32} entry row to insert at.
+ * Inserts the byte at ip into the appropriate position in the hash table, and updates ms->nextToUpdate.
+ * Skips sections of long matches as is necessary.
  */
 FORCE_INLINE_TEMPLATE void ZSTD_row_update_internal(ZSTD_matchState_t* ms, const BYTE* ip,
                                                     U32 const mls, U32 const rowLog,
                                                     U32 const rowMask, U32 const useCache)
 {
-    U32* const hashTable = ms->hashTable;
-    U16* const tagTable = ms->tagTable;
-    U32 const hashLog = ms->rowHashLog;
+    U32 idx = ms->nextToUpdate;
     const BYTE* const base = ms->window.base;
     const U32 target = (U32)(ip - base);
-    U32 idx = ms->nextToUpdate;
+    const U32 kSkipThreshold = 384;
+    const U32 kMaxMatchStartPositionsToUpdate = 96;
+    const U32 kMaxMatchEndPositionsToUpdate = 32;
 
-    DEBUGLOG(6, "ZSTD_row_update_internal(): nextToUpdate=%u, current=%u", idx, target);
-    for (; idx < target; ++idx) {
-        U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, idx, hashLog, rowLog, mls)
-                                  : (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
-        U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
-        U32* const row = hashTable + relRow;
-        BYTE* tagRow = (BYTE*)(tagTable + relRow);  /* Though tagTable is laid out as a table of U16, each tag is only 1 byte.
-                                                       Explicit cast allows us to get exact desired position within each row */
-        U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask);
-
-        assert(hash == ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls));
-        ((BYTE*)tagRow)[pos + ZSTD_ROW_HASH_TAG_OFFSET] = hash & ZSTD_ROW_HASH_TAG_MASK;
-        row[pos] = idx;
+    if (useCache) {
+        /* Only skip positions when using hash cache, i.e.
+         * if we are loading a dict, don't skip anything.
+         * If we decide to skip, then we only update a set number
+         * of positions at the beginning and end of the match.
+         */
+        if (UNLIKELY(target - idx > kSkipThreshold)) {
+            U32 const bound = idx + kMaxMatchStartPositionsToUpdate;
+            ZSTD_row_update_internalImpl(ms, idx, bound, mls, rowLog, rowMask, useCache);
+            idx = target - kMaxMatchEndPositionsToUpdate;
+            ZSTD_row_fillHashCache(ms, base, rowLog, mls, idx, ip+1);
+        }
     }
+    assert(target >= idx);
+    ZSTD_row_update_internalImpl(ms, idx, target, mls, rowLog, rowMask, useCache);
     ms->nextToUpdate = target;
 }
 
@@ -1217,34 +926,178 @@
  * processing.
  */
 void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) {
-    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
+    const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6);
     const U32 rowMask = (1u << rowLog) - 1;
     const U32 mls = MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */);
 
     DEBUGLOG(5, "ZSTD_row_update(), rowLog=%u", rowLog);
-    ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* dont use cache */);
+    ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* don't use cache */);
 }
 
-/* Returns a ZSTD_VecMask (U32) that has the nth bit set to 1 if the newly-computed "tag" matches
- * the hash at the nth position in a row of the tagTable.
+/* Returns the mask width of bits group of which will be set to 1. Given not all
+ * architectures have easy movemask instruction, this helps to iterate over
+ * groups of bits easier and faster.
  */
-FORCE_INLINE_TEMPLATE
-ZSTD_VecMask ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, const U32 rowEntries) {
-    ZSTD_VecMask matches = 0;
-    if (rowEntries == 16) {
-        ZSTD_Vec128 hashes        = ZSTD_Vec128_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET);
-        ZSTD_Vec128 expandedTags  = ZSTD_Vec128_set8(tag);
-        matches                   = ZSTD_Vec128_cmpMask8(hashes, expandedTags);
-    } else if (rowEntries == 32) {
-        ZSTD_Vec256 hashes        = ZSTD_Vec256_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET);
-        ZSTD_Vec256 expandedTags  = ZSTD_Vec256_set8(tag);
-        matches                   = ZSTD_Vec256_cmpMask8(hashes, expandedTags);
-    } else {
-        assert(0);
+FORCE_INLINE_TEMPLATE U32
+ZSTD_row_matchMaskGroupWidth(const U32 rowEntries)
+{
+    assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+    assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+    (void)rowEntries;
+#if defined(ZSTD_ARCH_ARM_NEON)
+    /* NEON path only works for little endian */
+    if (!MEM_isLittleEndian()) {
+        return 1;
     }
-    /* Each row is a circular buffer beginning at the value of "head". So we must rotate the "matches" bitfield
-        to match up with the actual layout of the entries within the hashTable */
-    return ZSTD_VecMask_rotateRight(matches, head, rowEntries);
+    if (rowEntries == 16) {
+        return 4;
+    }
+    if (rowEntries == 32) {
+        return 2;
+    }
+    if (rowEntries == 64) {
+        return 1;
+    }
+#endif
+    return 1;
+}
+
+#if defined(ZSTD_ARCH_X86_SSE2)
+FORCE_INLINE_TEMPLATE ZSTD_VecMask
+ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U32 head)
+{
+    const __m128i comparisonMask = _mm_set1_epi8((char)tag);
+    int matches[4] = {0};
+    int i;
+    assert(nbChunks == 1 || nbChunks == 2 || nbChunks == 4);
+    for (i=0; i<nbChunks; i++) {
+        const __m128i chunk = _mm_loadu_si128((const __m128i*)(const void*)(src + 16*i));
+        const __m128i equalMask = _mm_cmpeq_epi8(chunk, comparisonMask);
+        matches[i] = _mm_movemask_epi8(equalMask);
+    }
+    if (nbChunks == 1) return ZSTD_rotateRight_U16((U16)matches[0], head);
+    if (nbChunks == 2) return ZSTD_rotateRight_U32((U32)matches[1] << 16 | (U32)matches[0], head);
+    assert(nbChunks == 4);
+    return ZSTD_rotateRight_U64((U64)matches[3] << 48 | (U64)matches[2] << 32 | (U64)matches[1] << 16 | (U64)matches[0], head);
+}
+#endif
+
+#if defined(ZSTD_ARCH_ARM_NEON)
+FORCE_INLINE_TEMPLATE ZSTD_VecMask
+ZSTD_row_getNEONMask(const U32 rowEntries, const BYTE* const src, const BYTE tag, const U32 headGrouped)
+{
+    assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+    if (rowEntries == 16) {
+        /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits.
+         * After that groups of 4 bits represent the equalMask. We lower
+         * all bits except the highest in these groups by doing AND with
+         * 0x88 = 0b10001000.
+         */
+        const uint8x16_t chunk = vld1q_u8(src);
+        const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag)));
+        const uint8x8_t res = vshrn_n_u16(equalMask, 4);
+        const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0);
+        return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull;
+    } else if (rowEntries == 32) {
+        /* Same idea as with rowEntries == 16 but doing AND with
+         * 0x55 = 0b01010101.
+         */
+        const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src);
+        const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]);
+        const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]);
+        const uint8x16_t dup = vdupq_n_u8(tag);
+        const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6);
+        const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6);
+        const uint8x8_t res = vsli_n_u8(t0, t1, 4);
+        const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ;
+        return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull;
+    } else { /* rowEntries == 64 */
+        const uint8x16x4_t chunk = vld4q_u8(src);
+        const uint8x16_t dup = vdupq_n_u8(tag);
+        const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup);
+        const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup);
+        const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup);
+        const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup);
+
+        const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1);
+        const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1);
+        const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2);
+        const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4);
+        const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4);
+        const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0);
+        return ZSTD_rotateRight_U64(matches, headGrouped);
+    }
+}
+#endif
+
+/* Returns a ZSTD_VecMask (U64) that has the nth group (determined by
+ * ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag"
+ * matches the hash at the nth position in a row of the tagTable.
+ * Each row is a circular buffer beginning at the value of "headGrouped". So we
+ * must rotate the "matches" bitfield to match up with the actual layout of the
+ * entries within the hashTable */
+FORCE_INLINE_TEMPLATE ZSTD_VecMask
+ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGrouped, const U32 rowEntries)
+{
+    const BYTE* const src = tagRow;
+    assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+    assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+    assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ZSTD_VecMask) * 8);
+
+#if defined(ZSTD_ARCH_X86_SSE2)
+
+    return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, headGrouped);
+
+#else /* SW or NEON-LE */
+
+# if defined(ZSTD_ARCH_ARM_NEON)
+  /* This NEON path only works for little endian - otherwise use SWAR below */
+    if (MEM_isLittleEndian()) {
+        return ZSTD_row_getNEONMask(rowEntries, src, tag, headGrouped);
+    }
+# endif /* ZSTD_ARCH_ARM_NEON */
+    /* SWAR */
+    {   const int chunkSize = sizeof(size_t);
+        const size_t shiftAmount = ((chunkSize * 8) - chunkSize);
+        const size_t xFF = ~((size_t)0);
+        const size_t x01 = xFF / 0xFF;
+        const size_t x80 = x01 << 7;
+        const size_t splatChar = tag * x01;
+        ZSTD_VecMask matches = 0;
+        int i = rowEntries - chunkSize;
+        assert((sizeof(size_t) == 4) || (sizeof(size_t) == 8));
+        if (MEM_isLittleEndian()) { /* runtime check so have two loops */
+            const size_t extractMagic = (xFF / 0x7F) >> chunkSize;
+            do {
+                size_t chunk = MEM_readST(&src[i]);
+                chunk ^= splatChar;
+                chunk = (((chunk | x80) - x01) | chunk) & x80;
+                matches <<= chunkSize;
+                matches |= (chunk * extractMagic) >> shiftAmount;
+                i -= chunkSize;
+            } while (i >= 0);
+        } else { /* big endian: reverse bits during extraction */
+            const size_t msb = xFF ^ (xFF >> 1);
+            const size_t extractMagic = (msb / 0x1FF) | msb;
+            do {
+                size_t chunk = MEM_readST(&src[i]);
+                chunk ^= splatChar;
+                chunk = (((chunk | x80) - x01) | chunk) & x80;
+                matches <<= chunkSize;
+                matches |= ((chunk >> 7) * extractMagic) >> shiftAmount;
+                i -= chunkSize;
+            } while (i >= 0);
+        }
+        matches = ~matches;
+        if (rowEntries == 16) {
+            return ZSTD_rotateRight_U16((U16)matches, headGrouped);
+        } else if (rowEntries == 32) {
+            return ZSTD_rotateRight_U32((U32)matches, headGrouped);
+        } else {
+            return ZSTD_rotateRight_U64((U64)matches, headGrouped);
+        }
+    }
+#endif
 }
 
 /* The high-level approach of the SIMD row based match finder is as follows:
@@ -1263,7 +1116,7 @@
  * - Pick the longest match.
  */
 FORCE_INLINE_TEMPLATE
-size_t ZSTD_RowFindBestMatch_generic (
+size_t ZSTD_RowFindBestMatch(
                         ZSTD_matchState_t* ms,
                         const BYTE* const ip, const BYTE* const iLimit,
                         size_t* offsetPtr,
@@ -1271,7 +1124,7 @@
                         const U32 rowLog)
 {
     U32* const hashTable = ms->hashTable;
-    U16* const tagTable = ms->tagTable;
+    BYTE* const tagTable = ms->tagTable;
     U32* const hashCache = ms->hashCache;
     const U32 hashLog = ms->rowHashLog;
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
@@ -1289,16 +1142,21 @@
     const U32 rowEntries = (1U << rowLog);
     const U32 rowMask = rowEntries - 1;
     const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */
+    const U32 groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries);
+    const U64 hashSalt = ms->hashSalt;
     U32 nbAttempts = 1U << cappedSearchLog;
     size_t ml=4-1;
+    U32 hash;
 
     /* DMS/DDS variables that may be referenced laster */
     const ZSTD_matchState_t* const dms = ms->dictMatchState;
-    size_t ddsIdx;
-    U32 ddsExtraAttempts; /* cctx hash tables are limited in searches, but allow extra searches into DDS */
-    U32 dmsTag;
-    U32* dmsRow;
-    BYTE* dmsTagRow;
+
+    /* Initialize the following variables to satisfy static analyzer */
+    size_t ddsIdx = 0;
+    U32 ddsExtraAttempts = 0; /* cctx hash tables are limited in searches, but allow extra searches into DDS */
+    U32 dmsTag = 0;
+    U32* dmsRow = NULL;
+    BYTE* dmsTagRow = NULL;
 
     if (dictMode == ZSTD_dedicatedDictSearch) {
         const U32 ddsHashLog = dms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG;
@@ -1312,7 +1170,7 @@
     if (dictMode == ZSTD_dictMatchState) {
         /* Prefetch DMS rows */
         U32* const dmsHashTable = dms->hashTable;
-        U16* const dmsTagTable = dms->tagTable;
+        BYTE* const dmsTagTable = dms->tagTable;
         U32 const dmsHash = (U32)ZSTD_hashPtr(ip, dms->rowHashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
         U32 const dmsRelRow = (dmsHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
         dmsTag = dmsHash & ZSTD_ROW_HASH_TAG_MASK;
@@ -1322,23 +1180,34 @@
     }
 
     /* Update the hashTable and tagTable up to (but not including) ip */
-    ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */);
+    if (!ms->lazySkipping) {
+        ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */);
+        hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls, hashSalt);
+    } else {
+        /* Stop inserting every position when in the lazy skipping mode.
+         * The hash cache is also not kept up to date in this mode.
+         */
+        hash = (U32)ZSTD_hashPtrSalted(ip, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls, hashSalt);
+        ms->nextToUpdate = curr;
+    }
+    ms->hashSaltEntropy += hash; /* collect salt entropy */
+
     {   /* Get the hash for ip, compute the appropriate row */
-        U32 const hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls);
         U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
         U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK;
         U32* const row = hashTable + relRow;
         BYTE* tagRow = (BYTE*)(tagTable + relRow);
-        U32 const head = *tagRow & rowMask;
-        U32 matchBuffer[32 /* maximum nb entries per row */];
+        U32 const headGrouped = (*tagRow & rowMask) * groupWidth;
+        U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES];
         size_t numMatches = 0;
         size_t currMatch = 0;
-        ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, head, rowEntries);
+        ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, headGrouped, rowEntries);
 
         /* Cycle through the matches and prefetch */
-        for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
-            U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+        for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) {
+            U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask;
             U32 const matchIndex = row[matchPos];
+            if(matchPos == 0) continue;
             assert(numMatches < rowEntries);
             if (matchIndex < lowLimit)
                 break;
@@ -1348,13 +1217,14 @@
                 PREFETCH_L1(dictBase + matchIndex);
             }
             matchBuffer[numMatches++] = matchIndex;
+            --nbAttempts;
         }
 
         /* Speed opt: insert current byte into hashtable too. This allows us to avoid one iteration of the loop
            in ZSTD_row_update_internal() at the next search. */
         {
             U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask);
-            tagRow[pos + ZSTD_ROW_HASH_TAG_OFFSET] = (BYTE)tag;
+            tagRow[pos] = (BYTE)tag;
             row[pos] = ms->nextToUpdate++;
         }
 
@@ -1368,7 +1238,8 @@
             if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
                 const BYTE* const match = base + matchIndex;
                 assert(matchIndex >= dictLimit);   /* ensures this is true if dictMode != ZSTD_extDict */
-                if (match[ml] == ip[ml])   /* potentially better */
+                /* read 4B starting from (match + ml + 1 - sizeof(U32)) */
+                if (MEM_read32(match + ml - 3) == MEM_read32(ip + ml - 3))   /* potentially better */
                     currentMl = ZSTD_count(ip, match, iLimit);
             } else {
                 const BYTE* const match = dictBase + matchIndex;
@@ -1380,12 +1251,13 @@
             /* Save best solution */
             if (currentMl > ml) {
                 ml = currentMl;
-                *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE;
+                *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
                 if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
             }
         }
     }
 
+    assert(nbAttempts <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */
     if (dictMode == ZSTD_dedicatedDictSearch) {
         ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts + ddsExtraAttempts, dms,
                                                   ip, iLimit, prefixStart, curr, dictLimit, ddsIdx);
@@ -1397,19 +1269,21 @@
         const U32 dmsSize              = (U32)(dmsEnd - dmsBase);
         const U32 dmsIndexDelta        = dictLimit - dmsSize;
 
-        {   U32 const head = *dmsTagRow & rowMask;
-            U32 matchBuffer[32 /* maximum nb row entries */];
+        {   U32 const headGrouped = (*dmsTagRow & rowMask) * groupWidth;
+            U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES];
             size_t numMatches = 0;
             size_t currMatch = 0;
-            ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries);
+            ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, headGrouped, rowEntries);
 
-            for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
-                U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+            for (; (matches > 0) && (nbAttempts > 0); matches &= (matches - 1)) {
+                U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask;
                 U32 const matchIndex = dmsRow[matchPos];
+                if(matchPos == 0) continue;
                 if (matchIndex < dmsLowestIndex)
                     break;
                 PREFETCH_L1(dmsBase + matchIndex);
                 matchBuffer[numMatches++] = matchIndex;
+                --nbAttempts;
             }
 
             /* Return the longest match */
@@ -1427,7 +1301,8 @@
 
                 if (currentMl > ml) {
                     ml = currentMl;
-                    *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
+                    assert(curr > matchIndex + dmsIndexDelta);
+                    *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta));
                     if (ip+currentMl == iLimit) break;
                 }
             }
@@ -1436,83 +1311,183 @@
     return ml;
 }
 
-/* Inlining is important to hardwire a hot branch (template emulation) */
-FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectMLS (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        const ZSTD_dictMode_e dictMode, size_t* offsetPtr, const U32 rowLog)
-{
-    switch(ms->cParams.minMatch)
-    {
-    default : /* includes case 3 */
-    case 4 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, dictMode, rowLog);
-    case 5 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, dictMode, rowLog);
-    case 7 :
-    case 6 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, dictMode, rowLog);
-    }
-}
 
-FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectRowLog (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
-    switch(cappedSearchLog)
-    {
-    default :
-    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 4);
-    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 5);
-    }
-}
+/**
+ * Generate search functions templated on (dictMode, mls, rowLog).
+ * These functions are outlined for code size & compilation time.
+ * ZSTD_searchMax() dispatches to the correct implementation function.
+ *
+ * TODO: The start of the search function involves loading and calculating a
+ * bunch of constants from the ZSTD_matchState_t. These computations could be
+ * done in an initialization function, and saved somewhere in the match state.
+ * Then we could pass a pointer to the saved state instead of the match state,
+ * and avoid duplicate computations.
+ *
+ * TODO: Move the match re-winding into searchMax. This improves compression
+ * ratio, and unlocks further simplifications with the next TODO.
+ *
+ * TODO: Try moving the repcode search into searchMax. After the re-winding
+ * and repcode search are in searchMax, there is no more logic in the match
+ * finder loop that requires knowledge about the dictMode. So we should be
+ * able to avoid force inlining it, and we can join the extDict loop with
+ * the single segment loop. It should go in searchMax instead of its own
+ * function to avoid having multiple virtual function calls per search.
+ */
 
-FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dictMatchState_selectRowLog(
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
-    switch(cappedSearchLog)
-    {
-    default :
-    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 4);
-    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 5);
-    }
-}
+#define ZSTD_BT_SEARCH_FN(dictMode, mls) ZSTD_BtFindBestMatch_##dictMode##_##mls
+#define ZSTD_HC_SEARCH_FN(dictMode, mls) ZSTD_HcFindBestMatch_##dictMode##_##mls
+#define ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog) ZSTD_RowFindBestMatch_##dictMode##_##mls##_##rowLog
 
-FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog(
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
-    switch(cappedSearchLog)
-    {
-    default :
-    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 4);
-    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 5);
-    }
-}
+#define ZSTD_SEARCH_FN_ATTRS FORCE_NOINLINE
 
-FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_extDict_selectRowLog (
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* const iLimit,
-                        size_t* offsetPtr)
-{
-    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
-    switch(cappedSearchLog)
-    {
-    default :
-    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 4);
-    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 5);
-    }
-}
+#define GEN_ZSTD_BT_SEARCH_FN(dictMode, mls)                                           \
+    ZSTD_SEARCH_FN_ATTRS size_t ZSTD_BT_SEARCH_FN(dictMode, mls)(                      \
+            ZSTD_matchState_t* ms,                                                     \
+            const BYTE* ip, const BYTE* const iLimit,                                  \
+            size_t* offBasePtr)                                                        \
+    {                                                                                  \
+        assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls);                           \
+        return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, mls, ZSTD_##dictMode); \
+    }                                                                                  \
 
+#define GEN_ZSTD_HC_SEARCH_FN(dictMode, mls)                                          \
+    ZSTD_SEARCH_FN_ATTRS size_t ZSTD_HC_SEARCH_FN(dictMode, mls)(                     \
+            ZSTD_matchState_t* ms,                                                    \
+            const BYTE* ip, const BYTE* const iLimit,                                 \
+            size_t* offsetPtr)                                                        \
+    {                                                                                 \
+        assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls);                          \
+        return ZSTD_HcFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode); \
+    }                                                                                 \
+
+#define GEN_ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)                                          \
+    ZSTD_SEARCH_FN_ATTRS size_t ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)(                     \
+            ZSTD_matchState_t* ms,                                                             \
+            const BYTE* ip, const BYTE* const iLimit,                                          \
+            size_t* offsetPtr)                                                                 \
+    {                                                                                          \
+        assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls);                                   \
+        assert(MAX(4, MIN(6, ms->cParams.searchLog)) == rowLog);                               \
+        return ZSTD_RowFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode, rowLog); \
+    }                                                                                          \
+
+#define ZSTD_FOR_EACH_ROWLOG(X, dictMode, mls) \
+    X(dictMode, mls, 4)                        \
+    X(dictMode, mls, 5)                        \
+    X(dictMode, mls, 6)
+
+#define ZSTD_FOR_EACH_MLS_ROWLOG(X, dictMode) \
+    ZSTD_FOR_EACH_ROWLOG(X, dictMode, 4)      \
+    ZSTD_FOR_EACH_ROWLOG(X, dictMode, 5)      \
+    ZSTD_FOR_EACH_ROWLOG(X, dictMode, 6)
+
+#define ZSTD_FOR_EACH_MLS(X, dictMode) \
+    X(dictMode, 4)                     \
+    X(dictMode, 5)                     \
+    X(dictMode, 6)
+
+#define ZSTD_FOR_EACH_DICT_MODE(X, ...) \
+    X(__VA_ARGS__, noDict)              \
+    X(__VA_ARGS__, extDict)             \
+    X(__VA_ARGS__, dictMatchState)      \
+    X(__VA_ARGS__, dedicatedDictSearch)
+
+/* Generate row search fns for each combination of (dictMode, mls, rowLog) */
+ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS_ROWLOG, GEN_ZSTD_ROW_SEARCH_FN)
+/* Generate binary Tree search fns for each combination of (dictMode, mls) */
+ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_BT_SEARCH_FN)
+/* Generate hash chain search fns for each combination of (dictMode, mls) */
+ZSTD_FOR_EACH_DICT_MODE(ZSTD_FOR_EACH_MLS, GEN_ZSTD_HC_SEARCH_FN)
+
+typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e;
+
+#define GEN_ZSTD_CALL_BT_SEARCH_FN(dictMode, mls)                         \
+    case mls:                                                             \
+        return ZSTD_BT_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr);
+#define GEN_ZSTD_CALL_HC_SEARCH_FN(dictMode, mls)                         \
+    case mls:                                                             \
+        return ZSTD_HC_SEARCH_FN(dictMode, mls)(ms, ip, iend, offsetPtr);
+#define GEN_ZSTD_CALL_ROW_SEARCH_FN(dictMode, mls, rowLog)                         \
+    case rowLog:                                                                   \
+        return ZSTD_ROW_SEARCH_FN(dictMode, mls, rowLog)(ms, ip, iend, offsetPtr);
+
+#define ZSTD_SWITCH_MLS(X, dictMode)   \
+    switch (mls) {                     \
+        ZSTD_FOR_EACH_MLS(X, dictMode) \
+    }
+
+#define ZSTD_SWITCH_ROWLOG(dictMode, mls)                                    \
+    case mls:                                                                \
+        switch (rowLog) {                                                    \
+            ZSTD_FOR_EACH_ROWLOG(GEN_ZSTD_CALL_ROW_SEARCH_FN, dictMode, mls) \
+        }                                                                    \
+        ZSTD_UNREACHABLE;                                                    \
+        break;
+
+#define ZSTD_SWITCH_SEARCH_METHOD(dictMode)                       \
+    switch (searchMethod) {                                       \
+        case search_hashChain:                                    \
+            ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_HC_SEARCH_FN, dictMode) \
+            break;                                                \
+        case search_binaryTree:                                   \
+            ZSTD_SWITCH_MLS(GEN_ZSTD_CALL_BT_SEARCH_FN, dictMode) \
+            break;                                                \
+        case search_rowHash:                                      \
+            ZSTD_SWITCH_MLS(ZSTD_SWITCH_ROWLOG, dictMode)         \
+            break;                                                \
+    }                                                             \
+    ZSTD_UNREACHABLE;
+
+/**
+ * Searches for the longest match at @p ip.
+ * Dispatches to the correct implementation function based on the
+ * (searchMethod, dictMode, mls, rowLog). We use switch statements
+ * here instead of using an indirect function call through a function
+ * pointer because after Spectre and Meltdown mitigations, indirect
+ * function calls can be very costly, especially in the kernel.
+ *
+ * NOTE: dictMode and searchMethod should be templated, so those switch
+ * statements should be optimized out. Only the mls & rowLog switches
+ * should be left.
+ *
+ * @param ms The match state.
+ * @param ip The position to search at.
+ * @param iend The end of the input data.
+ * @param[out] offsetPtr Stores the match offset into this pointer.
+ * @param mls The minimum search length, in the range [4, 6].
+ * @param rowLog The row log (if applicable), in the range [4, 6].
+ * @param searchMethod The search method to use (templated).
+ * @param dictMode The dictMode (templated).
+ *
+ * @returns The length of the longest match found, or < mls if no match is found.
+ * If a match is found its offset is stored in @p offsetPtr.
+ */
+FORCE_INLINE_TEMPLATE size_t ZSTD_searchMax(
+    ZSTD_matchState_t* ms,
+    const BYTE* ip,
+    const BYTE* iend,
+    size_t* offsetPtr,
+    U32 const mls,
+    U32 const rowLog,
+    searchMethod_e const searchMethod,
+    ZSTD_dictMode_e const dictMode)
+{
+    if (dictMode == ZSTD_noDict) {
+        ZSTD_SWITCH_SEARCH_METHOD(noDict)
+    } else if (dictMode == ZSTD_extDict) {
+        ZSTD_SWITCH_SEARCH_METHOD(extDict)
+    } else if (dictMode == ZSTD_dictMatchState) {
+        ZSTD_SWITCH_SEARCH_METHOD(dictMatchState)
+    } else if (dictMode == ZSTD_dedicatedDictSearch) {
+        ZSTD_SWITCH_SEARCH_METHOD(dedicatedDictSearch)
+    }
+    ZSTD_UNREACHABLE;
+    return 0;
+}
 
 /* *******************************
 *  Common parser - lazy strategy
 *********************************/
-typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e;
 
 FORCE_INLINE_TEMPLATE size_t
 ZSTD_compressBlock_lazy_generic(
@@ -1526,47 +1501,15 @@
     const BYTE* ip = istart;
     const BYTE* anchor = istart;
     const BYTE* const iend = istart + srcSize;
-    const BYTE* const ilimit = searchMethod == search_rowHash ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8;
+    const BYTE* const ilimit = (searchMethod == search_rowHash) ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8;
     const BYTE* const base = ms->window.base;
     const U32 prefixLowestIndex = ms->window.dictLimit;
     const BYTE* const prefixLowest = base + prefixLowestIndex;
-    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
+    const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6);
+    const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6);
 
-    typedef size_t (*searchMax_f)(
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
-
-    /**
-     * This table is indexed first by the four ZSTD_dictMode_e values, and then
-     * by the two searchMethod_e values. NULLs are placed for configurations
-     * that should never occur (extDict modes go to the other implementation
-     * below and there is no DDSS for binary tree search yet).
-     */
-    const searchMax_f searchFuncs[4][3] = {
-        {
-            ZSTD_HcFindBestMatch_selectMLS,
-            ZSTD_BtFindBestMatch_selectMLS,
-            ZSTD_RowFindBestMatch_selectRowLog
-        },
-        {
-            NULL,
-            NULL,
-            NULL
-        },
-        {
-            ZSTD_HcFindBestMatch_dictMatchState_selectMLS,
-            ZSTD_BtFindBestMatch_dictMatchState_selectMLS,
-            ZSTD_RowFindBestMatch_dictMatchState_selectRowLog
-        },
-        {
-            ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS,
-            NULL,
-            ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog
-        }
-    };
-
-    searchMax_f const searchMax = searchFuncs[dictMode][(int)searchMethod];
-    U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0;
+    U32 offset_1 = rep[0], offset_2 = rep[1];
+    U32 offsetSaved1 = 0, offsetSaved2 = 0;
 
     const int isDMS = dictMode == ZSTD_dictMatchState;
     const int isDDS = dictMode == ZSTD_dedicatedDictSearch;
@@ -1581,16 +1524,14 @@
                                      0;
     const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest));
 
-    assert(searchMax != NULL);
-
     DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u) (searchFunc=%u)", (U32)dictMode, (U32)searchMethod);
     ip += (dictAndPrefixLength == 0);
     if (dictMode == ZSTD_noDict) {
         U32 const curr = (U32)(ip - base);
         U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog);
         U32 const maxRep = curr - windowLow;
-        if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
-        if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0;
+        if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+        if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0;
     }
     if (isDxS) {
         /* dictMatchState repCode checks don't currently handle repCode == 0
@@ -1599,10 +1540,11 @@
         assert(offset_2 <= dictAndPrefixLength);
     }
 
+    /* Reset the lazy skipping state */
+    ms->lazySkipping = 0;
+
     if (searchMethod == search_rowHash) {
-        ZSTD_row_fillHashCache(ms, base, rowLog,
-                            MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */),
-                            ms->nextToUpdate, ilimit);
+        ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit);
     }
 
     /* Match Loop */
@@ -1614,8 +1556,9 @@
 #endif
     while (ip < ilimit) {
         size_t matchLength=0;
-        size_t offset=0;
+        size_t offBase = REPCODE1_TO_OFFBASE;
         const BYTE* start=ip+1;
+        DEBUGLOG(7, "search baseline (depth 0)");
 
         /* check repCode */
         if (isDxS) {
@@ -1638,28 +1581,38 @@
         }
 
         /* first search (depth 0) */
-        {   size_t offsetFound = 999999999;
-            size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+        {   size_t offbaseFound = 999999999;
+            size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &offbaseFound, mls, rowLog, searchMethod, dictMode);
             if (ml2 > matchLength)
-                matchLength = ml2, start = ip, offset=offsetFound;
+                matchLength = ml2, start = ip, offBase = offbaseFound;
         }
 
         if (matchLength < 4) {
-            ip += ((ip-anchor) >> kSearchStrength) + 1;   /* jump faster over incompressible sections */
+            size_t const step = ((size_t)(ip-anchor) >> kSearchStrength) + 1;   /* jump faster over incompressible sections */;
+            ip += step;
+            /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time.
+             * In this mode we stop inserting every position into our tables, and only insert
+             * positions that we search, which is one in step positions.
+             * The exact cutoff is flexible, I've just chosen a number that is reasonably high,
+             * so we minimize the compression ratio loss in "normal" scenarios. This mode gets
+             * triggered once we've gone 2KB without finding any matches.
+             */
+            ms->lazySkipping = step > kLazySkippingStep;
             continue;
         }
 
         /* let's try to find a better solution */
         if (depth>=1)
         while (ip<ilimit) {
+            DEBUGLOG(7, "search depth 1");
             ip ++;
             if ( (dictMode == ZSTD_noDict)
-              && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+              && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
                 size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
                 int const gain2 = (int)(mlRep * 3);
-                int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+                int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
                 if ((mlRep >= 4) && (gain2 > gain1))
-                    matchLength = mlRep, offset = 0, start = ip;
+                    matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
             }
             if (isDxS) {
                 const U32 repIndex = (U32)(ip - base) - offset_1;
@@ -1671,30 +1624,31 @@
                     const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
                     size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
                     int const gain2 = (int)(mlRep * 3);
-                    int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+                    int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
                     if ((mlRep >= 4) && (gain2 > gain1))
-                        matchLength = mlRep, offset = 0, start = ip;
+                        matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
                 }
             }
-            {   size_t offset2=999999999;
-                size_t const ml2 = searchMax(ms, ip, iend, &offset2);
-                int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1));   /* raw approx */
-                int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
+            {   size_t ofbCandidate=999999999;
+                size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode);
+                int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate));   /* raw approx */
+                int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4);
                 if ((ml2 >= 4) && (gain2 > gain1)) {
-                    matchLength = ml2, offset = offset2, start = ip;
+                    matchLength = ml2, offBase = ofbCandidate, start = ip;
                     continue;   /* search a better one */
             }   }
 
             /* let's find an even better one */
             if ((depth==2) && (ip<ilimit)) {
+                DEBUGLOG(7, "search depth 2");
                 ip ++;
                 if ( (dictMode == ZSTD_noDict)
-                  && (offset) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+                  && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
                     size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
                     int const gain2 = (int)(mlRep * 4);
-                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
                     if ((mlRep >= 4) && (gain2 > gain1))
-                        matchLength = mlRep, offset = 0, start = ip;
+                        matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
                 }
                 if (isDxS) {
                     const U32 repIndex = (U32)(ip - base) - offset_1;
@@ -1706,48 +1660,54 @@
                         const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
                         size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
                         int const gain2 = (int)(mlRep * 4);
-                        int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+                        int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
                         if ((mlRep >= 4) && (gain2 > gain1))
-                            matchLength = mlRep, offset = 0, start = ip;
+                            matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
                     }
                 }
-                {   size_t offset2=999999999;
-                    size_t const ml2 = searchMax(ms, ip, iend, &offset2);
-                    int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1));   /* raw approx */
-                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
+                {   size_t ofbCandidate=999999999;
+                    size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, dictMode);
+                    int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate));   /* raw approx */
+                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7);
                     if ((ml2 >= 4) && (gain2 > gain1)) {
-                        matchLength = ml2, offset = offset2, start = ip;
+                        matchLength = ml2, offBase = ofbCandidate, start = ip;
                         continue;
             }   }   }
             break;  /* nothing found : store previous solution */
         }
 
         /* NOTE:
-         * start[-offset+ZSTD_REP_MOVE-1] is undefined behavior.
-         * (-offset+ZSTD_REP_MOVE-1) is unsigned, and is added to start, which
-         * overflows the pointer, which is undefined behavior.
+         * Pay attention that `start[-value]` can lead to strange undefined behavior
+         * notably if `value` is unsigned, resulting in a large positive `-value`.
          */
         /* catch up */
-        if (offset) {
+        if (OFFBASE_IS_OFFSET(offBase)) {
             if (dictMode == ZSTD_noDict) {
-                while ( ((start > anchor) & (start - (offset-ZSTD_REP_MOVE) > prefixLowest))
-                     && (start[-1] == (start-(offset-ZSTD_REP_MOVE))[-1]) )  /* only search for offset within prefix */
+                while ( ((start > anchor) & (start - OFFBASE_TO_OFFSET(offBase) > prefixLowest))
+                     && (start[-1] == (start-OFFBASE_TO_OFFSET(offBase))[-1]) )  /* only search for offset within prefix */
                     { start--; matchLength++; }
             }
             if (isDxS) {
-                U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE));
+                U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase));
                 const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex;
                 const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest;
                 while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; }  /* catch up */
             }
-            offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE);
+            offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase);
         }
         /* store sequence */
 _storeSequence:
-        {   size_t const litLength = start - anchor;
-            ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH);
+        {   size_t const litLength = (size_t)(start - anchor);
+            ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength);
             anchor = ip = start + matchLength;
         }
+        if (ms->lazySkipping) {
+            /* We've found a match, disable lazy skipping mode, and refill the hash cache. */
+            if (searchMethod == search_rowHash) {
+                ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit);
+            }
+            ms->lazySkipping = 0;
+        }
 
         /* check immediate repcode */
         if (isDxS) {
@@ -1761,8 +1721,8 @@
                    && (MEM_read32(repMatch) == MEM_read32(ip)) ) {
                     const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend;
                     matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4;
-                    offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset;   /* swap offset_2 <=> offset_1 */
-                    ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH);
+                    offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase;   /* swap offset_2 <=> offset_1 */
+                    ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
                     ip += matchLength;
                     anchor = ip;
                     continue;
@@ -1776,16 +1736,20 @@
                  && (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) {
                 /* store sequence */
                 matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
-                offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset; /* swap repcodes */
-                ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH);
+                offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap repcodes */
+                ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
                 ip += matchLength;
                 anchor = ip;
                 continue;   /* faster when present ... (?) */
     }   }   }
 
-    /* Save reps for next block */
-    rep[0] = offset_1 ? offset_1 : savedOffset;
-    rep[1] = offset_2 ? offset_2 : savedOffset;
+    /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+     * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+    offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
+
+    /* save reps for next block */
+    rep[0] = offset_1 ? offset_1 : offsetSaved1;
+    rep[1] = offset_2 ? offset_2 : offsetSaved2;
 
     /* Return the last literals size */
     return (size_t)(iend - anchor);
@@ -1954,27 +1918,20 @@
     const BYTE* const dictEnd  = dictBase + dictLimit;
     const BYTE* const dictStart  = dictBase + ms->window.lowLimit;
     const U32 windowLog = ms->cParams.windowLog;
-    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
+    const U32 mls = BOUNDED(4, ms->cParams.minMatch, 6);
+    const U32 rowLog = BOUNDED(4, ms->cParams.searchLog, 6);
 
-    typedef size_t (*searchMax_f)(
-                        ZSTD_matchState_t* ms,
-                        const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
-    const searchMax_f searchFuncs[3] = {
-        ZSTD_HcFindBestMatch_extDict_selectMLS,
-        ZSTD_BtFindBestMatch_extDict_selectMLS,
-        ZSTD_RowFindBestMatch_extDict_selectRowLog
-    };
-    searchMax_f searchMax = searchFuncs[(int)searchMethod];
     U32 offset_1 = rep[0], offset_2 = rep[1];
 
     DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic (searchFunc=%u)", (U32)searchMethod);
 
+    /* Reset the lazy skipping state */
+    ms->lazySkipping = 0;
+
     /* init */
     ip += (ip == prefixStart);
     if (searchMethod == search_rowHash) {
-        ZSTD_row_fillHashCache(ms, base, rowLog,
-                               MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */),
-                               ms->nextToUpdate, ilimit);
+        ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit);
     }
 
     /* Match Loop */
@@ -1986,7 +1943,7 @@
 #endif
     while (ip < ilimit) {
         size_t matchLength=0;
-        size_t offset=0;
+        size_t offBase = REPCODE1_TO_OFFBASE;
         const BYTE* start=ip+1;
         U32 curr = (U32)(ip-base);
 
@@ -1996,7 +1953,7 @@
             const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
             const BYTE* const repMatch = repBase + repIndex;
             if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow */
-               & (offset_1 < curr+1 - windowLow) ) /* note: we are searching at curr+1 */
+               & (offset_1 <= curr+1 - windowLow) ) /* note: we are searching at curr+1 */
             if (MEM_read32(ip+1) == MEM_read32(repMatch)) {
                 /* repcode detected we should take it */
                 const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
@@ -2005,14 +1962,23 @@
         }   }
 
         /* first search (depth 0) */
-        {   size_t offsetFound = 999999999;
-            size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+        {   size_t ofbCandidate = 999999999;
+            size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict);
             if (ml2 > matchLength)
-                matchLength = ml2, start = ip, offset=offsetFound;
+                matchLength = ml2, start = ip, offBase = ofbCandidate;
         }
 
-         if (matchLength < 4) {
-            ip += ((ip-anchor) >> kSearchStrength) + 1;   /* jump faster over incompressible sections */
+        if (matchLength < 4) {
+            size_t const step = ((size_t)(ip-anchor) >> kSearchStrength);
+            ip += step + 1;   /* jump faster over incompressible sections */
+            /* Enter the lazy skipping mode once we are skipping more than 8 bytes at a time.
+             * In this mode we stop inserting every position into our tables, and only insert
+             * positions that we search, which is one in step positions.
+             * The exact cutoff is flexible, I've just chosen a number that is reasonably high,
+             * so we minimize the compression ratio loss in "normal" scenarios. This mode gets
+             * triggered once we've gone 2KB without finding any matches.
+             */
+            ms->lazySkipping = step > kLazySkippingStep;
             continue;
         }
 
@@ -2022,30 +1988,30 @@
             ip ++;
             curr++;
             /* check repCode */
-            if (offset) {
+            if (offBase) {
                 const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
                 const U32 repIndex = (U32)(curr - offset_1);
                 const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
                 const BYTE* const repMatch = repBase + repIndex;
                 if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
-                   & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
+                   & (offset_1 <= curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
                 if (MEM_read32(ip) == MEM_read32(repMatch)) {
                     /* repcode detected */
                     const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
                     size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
                     int const gain2 = (int)(repLength * 3);
-                    int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offset+1) + 1);
+                    int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
                     if ((repLength >= 4) && (gain2 > gain1))
-                        matchLength = repLength, offset = 0, start = ip;
+                        matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip;
             }   }
 
             /* search match, depth 1 */
-            {   size_t offset2=999999999;
-                size_t const ml2 = searchMax(ms, ip, iend, &offset2);
-                int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1));   /* raw approx */
-                int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 4);
+            {   size_t ofbCandidate = 999999999;
+                size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict);
+                int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate));   /* raw approx */
+                int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4);
                 if ((ml2 >= 4) && (gain2 > gain1)) {
-                    matchLength = ml2, offset = offset2, start = ip;
+                    matchLength = ml2, offBase = ofbCandidate, start = ip;
                     continue;   /* search a better one */
             }   }
 
@@ -2054,50 +2020,57 @@
                 ip ++;
                 curr++;
                 /* check repCode */
-                if (offset) {
+                if (offBase) {
                     const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
                     const U32 repIndex = (U32)(curr - offset_1);
                     const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
                     const BYTE* const repMatch = repBase + repIndex;
                     if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
-                       & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
+                       & (offset_1 <= curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
                     if (MEM_read32(ip) == MEM_read32(repMatch)) {
                         /* repcode detected */
                         const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
                         size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
                         int const gain2 = (int)(repLength * 4);
-                        int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 1);
+                        int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
                         if ((repLength >= 4) && (gain2 > gain1))
-                            matchLength = repLength, offset = 0, start = ip;
+                            matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip;
                 }   }
 
                 /* search match, depth 2 */
-                {   size_t offset2=999999999;
-                    size_t const ml2 = searchMax(ms, ip, iend, &offset2);
-                    int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)offset2+1));   /* raw approx */
-                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offset+1) + 7);
+                {   size_t ofbCandidate = 999999999;
+                    size_t const ml2 = ZSTD_searchMax(ms, ip, iend, &ofbCandidate, mls, rowLog, searchMethod, ZSTD_extDict);
+                    int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate));   /* raw approx */
+                    int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7);
                     if ((ml2 >= 4) && (gain2 > gain1)) {
-                        matchLength = ml2, offset = offset2, start = ip;
+                        matchLength = ml2, offBase = ofbCandidate, start = ip;
                         continue;
             }   }   }
             break;  /* nothing found : store previous solution */
         }
 
         /* catch up */
-        if (offset) {
-            U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE));
+        if (OFFBASE_IS_OFFSET(offBase)) {
+            U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase));
             const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex;
             const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart;
             while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; }  /* catch up */
-            offset_2 = offset_1; offset_1 = (U32)(offset - ZSTD_REP_MOVE);
+            offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase);
         }
 
         /* store sequence */
 _storeSequence:
-        {   size_t const litLength = start - anchor;
-            ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offset, matchLength-MINMATCH);
+        {   size_t const litLength = (size_t)(start - anchor);
+            ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength);
             anchor = ip = start + matchLength;
         }
+        if (ms->lazySkipping) {
+            /* We've found a match, disable lazy skipping mode, and refill the hash cache. */
+            if (searchMethod == search_rowHash) {
+                ZSTD_row_fillHashCache(ms, base, rowLog, mls, ms->nextToUpdate, ilimit);
+            }
+            ms->lazySkipping = 0;
+        }
 
         /* check immediate repcode */
         while (ip <= ilimit) {
@@ -2107,13 +2080,13 @@
             const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
             const BYTE* const repMatch = repBase + repIndex;
             if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
-               & (offset_2 < repCurrent - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
+               & (offset_2 <= repCurrent - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
             if (MEM_read32(ip) == MEM_read32(repMatch)) {
                 /* repcode detected we should take it */
                 const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
                 matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
-                offset = offset_2; offset_2 = offset_1; offset_1 = (U32)offset;   /* swap offset history */
-                ZSTD_storeSeq(seqStore, 0, anchor, iend, 0, matchLength-MINMATCH);
+                offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase;   /* swap offset history */
+                ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
                 ip += matchLength;
                 anchor = ip;
                 continue;   /* faster when present ... (?) */
@@ -2179,7 +2152,6 @@
 size_t ZSTD_compressBlock_lazy2_extDict_row(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize)
-
 {
     return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2);
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.h b/Utilities/cmzstd/lib/compress/zstd_lazy.h
index 150f7b3..3bde673 100644
--- a/Utilities/cmzstd/lib/compress/zstd_lazy.h
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -25,6 +25,8 @@
  */
 #define ZSTD_LAZY_DDSS_BUCKET_LOG 2
 
+#define ZSTD_ROW_HASH_TAG_BITS 8        /* nb bits to use for the tag */
+
 U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip);
 void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip);
 
@@ -116,7 +118,7 @@
 size_t ZSTD_compressBlock_btlazy2_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
-        
+
 
 #if defined (__cplusplus)
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.c b/Utilities/cmzstd/lib/compress/zstd_ldm.c
index fa4ebea..3d74ff1 100644
--- a/Utilities/cmzstd/lib/compress/zstd_ldm.c
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -159,12 +159,12 @@
     size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
     size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)
                            + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));
-    return params.enableLdm ? totalSize : 0;
+    return params.enableLdm == ZSTD_ps_enable ? totalSize : 0;
 }
 
 size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
 {
-    return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
+    return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0;
 }
 
 /** ZSTD_ldm_getBucket() :
@@ -242,11 +242,11 @@
     switch(ms->cParams.strategy)
     {
     case ZSTD_fast:
-        ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
+        ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
         break;
 
     case ZSTD_dfast:
-        ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
+        ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
         break;
 
     case ZSTD_greedy:
@@ -478,7 +478,7 @@
              */
             if (anchor > ip + hashed) {
                 ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);
-                /* Continue the outter loop at anchor (ip + hashed == anchor). */
+                /* Continue the outer loop at anchor (ip + hashed == anchor). */
                 ip = anchor - hashed;
                 break;
             }
@@ -549,7 +549,7 @@
          * the window through early invalidation.
          * TODO: * Test the chunk size.
          *       * Try invalidation after the sequence generation and test the
-         *         the offset against maxDist directly.
+         *         offset against maxDist directly.
          *
          * NOTE: Because of dictionaries + sequence splitting we MUST make sure
          * that any offset used is valid at the END of the sequence, since it may
@@ -579,7 +579,9 @@
     return 0;
 }
 
-void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) {
+void
+ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch)
+{
     while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
         rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
         if (srcSize <= seq->litLength) {
@@ -657,7 +659,7 @@
 
 size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
     ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
-    ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+    ZSTD_paramSwitch_e useRowMatchFinder,
     void const* src, size_t srcSize)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
@@ -709,8 +711,8 @@
             rep[0] = sequence.offset;
             /* Store the sequence */
             ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
-                          sequence.offset + ZSTD_REP_MOVE,
-                          sequence.matchLength - MINMATCH);
+                          OFFSET_TO_OFFBASE(sequence.offset),
+                          sequence.matchLength);
             ip += sequence.matchLength;
         }
     }
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.h b/Utilities/cmzstd/lib/compress/zstd_ldm.h
index 393466f..f147021 100644
--- a/Utilities/cmzstd/lib/compress/zstd_ldm.h
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -66,7 +66,7 @@
  */
 size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
             ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
-            ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+            ZSTD_paramSwitch_e useRowMatchFinder,
             void const* src, size_t srcSize);
 
 /**
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
index e5c24d8..ef34bc5 100644
--- a/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,7 +11,10 @@
 #ifndef ZSTD_LDM_GEARTAB_H
 #define ZSTD_LDM_GEARTAB_H
 
-static U64 ZSTD_ldm_gearTab[256] = {
+#include "../common/compiler.h" /* UNUSED_ATTR */
+#include "../common/mem.h"      /* U64 */
+
+static UNUSED_ATTR const U64 ZSTD_ldm_gearTab[256] = {
     0xf5b8f72c5f77775c, 0x84935f266b7ac412, 0xb647ada9ca730ccc,
     0xb065bb4b114fb1de, 0x34584e7e8c3a9fd0, 0x4e97e17c6ae26b05,
     0x3a03d743bc99a604, 0xcecd042422c4044f, 0x76de76c58524259e,
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.c b/Utilities/cmzstd/lib/compress/zstd_opt.c
index 402a7e5..f02a760 100644
--- a/Utilities/cmzstd/lib/compress/zstd_opt.c
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,40 +14,47 @@
 
 
 #define ZSTD_LITFREQ_ADD    2   /* scaling factor for litFreq, so that frequencies adapt faster to new stats */
-#define ZSTD_FREQ_DIV       4   /* log factor when using previous stats to init next stats */
 #define ZSTD_MAX_PRICE     (1<<30)
 
-#define ZSTD_PREDEF_THRESHOLD 1024   /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */
+#define ZSTD_PREDEF_THRESHOLD 8   /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */
 
 
 /*-*************************************
 *  Price functions for optimal parser
 ***************************************/
 
-#if 0    /* approximation at bit level */
+#if 0    /* approximation at bit level (for tests) */
 #  define BITCOST_ACCURACY 0
 #  define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
-#  define WEIGHT(stat)  ((void)opt, ZSTD_bitWeight(stat))
-#elif 0  /* fractional bit accuracy */
+#  define WEIGHT(stat, opt) ((void)(opt), ZSTD_bitWeight(stat))
+#elif 0  /* fractional bit accuracy (for tests) */
 #  define BITCOST_ACCURACY 8
 #  define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
-#  define WEIGHT(stat,opt) ((void)opt, ZSTD_fracWeight(stat))
+#  define WEIGHT(stat,opt) ((void)(opt), ZSTD_fracWeight(stat))
 #else    /* opt==approx, ultra==accurate */
 #  define BITCOST_ACCURACY 8
 #  define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)
-#  define WEIGHT(stat,opt) (opt ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat))
+#  define WEIGHT(stat,opt) ((opt) ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat))
 #endif
 
+/* ZSTD_bitWeight() :
+ * provide estimated "cost" of a stat in full bits only */
 MEM_STATIC U32 ZSTD_bitWeight(U32 stat)
 {
     return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER);
 }
 
+/* ZSTD_fracWeight() :
+ * provide fractional-bit "cost" of a stat,
+ * using linear interpolation approximation */
 MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat)
 {
     U32 const stat = rawStat + 1;
     U32 const hb = ZSTD_highbit32(stat);
     U32 const BWeight = hb * BITCOST_MULTIPLIER;
+    /* Fweight was meant for "Fractional weight"
+     * but it's effectively a value between 1 and 2
+     * using fixed point arithmetic */
     U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb;
     U32 const weight = BWeight + FWeight;
     assert(hb + BITCOST_ACCURACY < 31);
@@ -58,7 +65,7 @@
 /* debugging function,
  * @return price in bytes as fractional value
  * for debug messages only */
-MEM_STATIC double ZSTD_fCost(U32 price)
+MEM_STATIC double ZSTD_fCost(int price)
 {
     return (double)price / (BITCOST_MULTIPLIER*8);
 }
@@ -66,7 +73,7 @@
 
 static int ZSTD_compressedLiterals(optState_t const* const optPtr)
 {
-    return optPtr->literalCompressionMode != ZSTD_lcm_uncompressed;
+    return optPtr->literalCompressionMode != ZSTD_ps_disable;
 }
 
 static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel)
@@ -79,25 +86,52 @@
 }
 
 
-/* ZSTD_downscaleStat() :
- * reduce all elements in table by a factor 2^(ZSTD_FREQ_DIV+malus)
- * return the resulting sum of elements */
-static U32 ZSTD_downscaleStat(unsigned* table, U32 lastEltIndex, int malus)
+static U32 sum_u32(const unsigned table[], size_t nbElts)
+{
+    size_t n;
+    U32 total = 0;
+    for (n=0; n<nbElts; n++) {
+        total += table[n];
+    }
+    return total;
+}
+
+typedef enum { base_0possible=0, base_1guaranteed=1 } base_directive_e;
+
+static U32
+ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift, base_directive_e base1)
 {
     U32 s, sum=0;
-    DEBUGLOG(5, "ZSTD_downscaleStat (nbElts=%u)", (unsigned)lastEltIndex+1);
-    assert(ZSTD_FREQ_DIV+malus > 0 && ZSTD_FREQ_DIV+malus < 31);
+    DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)",
+            (unsigned)lastEltIndex+1, (unsigned)shift );
+    assert(shift < 30);
     for (s=0; s<lastEltIndex+1; s++) {
-        table[s] = 1 + (table[s] >> (ZSTD_FREQ_DIV+malus));
-        sum += table[s];
+        unsigned const base = base1 ? 1 : (table[s]>0);
+        unsigned const newStat = base + (table[s] >> shift);
+        sum += newStat;
+        table[s] = newStat;
     }
     return sum;
 }
 
+/* ZSTD_scaleStats() :
+ * reduce all elt frequencies in table if sum too large
+ * return the resulting sum of elements */
+static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget)
+{
+    U32 const prevsum = sum_u32(table, lastEltIndex+1);
+    U32 const factor = prevsum >> logTarget;
+    DEBUGLOG(5, "ZSTD_scaleStats (nbElts=%u, target=%u)", (unsigned)lastEltIndex+1, (unsigned)logTarget);
+    assert(logTarget < 30);
+    if (factor <= 1) return prevsum;
+    return ZSTD_downscaleStats(table, lastEltIndex, ZSTD_highbit32(factor), base_1guaranteed);
+}
+
 /* ZSTD_rescaleFreqs() :
  * if first block (detected by optPtr->litLengthSum == 0) : init statistics
  *    take hints from dictionary if there is one
- *    or init from zero, using src for literals stats, or flat 1 for match symbols
+ *    and init from zero if there is none,
+ *    using src for literals stats, and baseline stats for sequence symbols
  * otherwise downscale existing stats, to be used as seed for next block.
  */
 static void
@@ -109,24 +143,28 @@
     DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize);
     optPtr->priceType = zop_dynamic;
 
-    if (optPtr->litLengthSum == 0) {  /* first block : init */
-        if (srcSize <= ZSTD_PREDEF_THRESHOLD) {  /* heuristic */
-            DEBUGLOG(5, "(srcSize <= ZSTD_PREDEF_THRESHOLD) => zop_predef");
+    if (optPtr->litLengthSum == 0) {  /* no literals stats collected -> first block assumed -> init */
+
+        /* heuristic: use pre-defined stats for too small inputs */
+        if (srcSize <= ZSTD_PREDEF_THRESHOLD) {
+            DEBUGLOG(5, "srcSize <= %i : use predefined stats", ZSTD_PREDEF_THRESHOLD);
             optPtr->priceType = zop_predef;
         }
 
         assert(optPtr->symbolCosts != NULL);
         if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) {
-            /* huffman table presumed generated by dictionary */
+
+            /* huffman stats covering the full value set : table presumed generated by dictionary */
             optPtr->priceType = zop_dynamic;
 
             if (compressedLiterals) {
+                /* generate literals statistics from huffman table */
                 unsigned lit;
                 assert(optPtr->litFreq != NULL);
                 optPtr->litSum = 0;
                 for (lit=0; lit<=MaxLit; lit++) {
                     U32 const scaleLog = 11;   /* scale to 2K */
-                    U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit);
+                    U32 const bitCost = HUF_getNbBitsFromCTable(optPtr->symbolCosts->huf.CTable, lit);
                     assert(bitCost <= scaleLog);
                     optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;
                     optPtr->litSum += optPtr->litFreq[lit];
@@ -168,20 +206,26 @@
                     optPtr->offCodeSum += optPtr->offCodeFreq[of];
             }   }
 
-        } else {  /* not a dictionary */
+        } else {  /* first block, no dictionary */
 
             assert(optPtr->litFreq != NULL);
             if (compressedLiterals) {
+                /* base initial cost of literals on direct frequency within src */
                 unsigned lit = MaxLit;
                 HIST_count_simple(optPtr->litFreq, &lit, src, srcSize);   /* use raw first block to init statistics */
-                optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
+                optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8, base_0possible);
             }
 
-            {   unsigned ll;
-                for (ll=0; ll<=MaxLL; ll++)
-                    optPtr->litLengthFreq[ll] = 1;
+            {   unsigned const baseLLfreqs[MaxLL+1] = {
+                    4, 2, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1
+                };
+                ZSTD_memcpy(optPtr->litLengthFreq, baseLLfreqs, sizeof(baseLLfreqs));
+                optPtr->litLengthSum = sum_u32(baseLLfreqs, MaxLL+1);
             }
-            optPtr->litLengthSum = MaxLL+1;
 
             {   unsigned ml;
                 for (ml=0; ml<=MaxML; ml++)
@@ -189,21 +233,25 @@
             }
             optPtr->matchLengthSum = MaxML+1;
 
-            {   unsigned of;
-                for (of=0; of<=MaxOff; of++)
-                    optPtr->offCodeFreq[of] = 1;
+            {   unsigned const baseOFCfreqs[MaxOff+1] = {
+                    6, 2, 1, 1, 2, 3, 4, 4,
+                    4, 3, 2, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1
+                };
+                ZSTD_memcpy(optPtr->offCodeFreq, baseOFCfreqs, sizeof(baseOFCfreqs));
+                optPtr->offCodeSum = sum_u32(baseOFCfreqs, MaxOff+1);
             }
-            optPtr->offCodeSum = MaxOff+1;
 
         }
 
-    } else {   /* new block : re-use previous statistics, scaled down */
+    } else {   /* new block : scale down accumulated statistics */
 
         if (compressedLiterals)
-            optPtr->litSum = ZSTD_downscaleStat(optPtr->litFreq, MaxLit, 1);
-        optPtr->litLengthSum = ZSTD_downscaleStat(optPtr->litLengthFreq, MaxLL, 0);
-        optPtr->matchLengthSum = ZSTD_downscaleStat(optPtr->matchLengthFreq, MaxML, 0);
-        optPtr->offCodeSum = ZSTD_downscaleStat(optPtr->offCodeFreq, MaxOff, 0);
+            optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, MaxLit, 12);
+        optPtr->litLengthSum = ZSTD_scaleStats(optPtr->litLengthFreq, MaxLL, 11);
+        optPtr->matchLengthSum = ZSTD_scaleStats(optPtr->matchLengthFreq, MaxML, 11);
+        optPtr->offCodeSum = ZSTD_scaleStats(optPtr->offCodeFreq, MaxOff, 11);
     }
 
     ZSTD_setBasePrices(optPtr, optLevel);
@@ -225,11 +273,14 @@
         return (litLength*6) * BITCOST_MULTIPLIER;  /* 6 bit per literal - no statistic used */
 
     /* dynamic statistics */
-    {   U32 price = litLength * optPtr->litSumBasePrice;
+    {   U32 price = optPtr->litSumBasePrice * litLength;
+        U32 const litPriceMax = optPtr->litSumBasePrice - BITCOST_MULTIPLIER;
         U32 u;
+        assert(optPtr->litSumBasePrice >= BITCOST_MULTIPLIER);
         for (u=0; u < litLength; u++) {
-            assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice);   /* literal cost should never be negative */
-            price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel);
+            U32 litPrice = WEIGHT(optPtr->litFreq[literals[u]], optLevel);
+            if (UNLIKELY(litPrice > litPriceMax)) litPrice = litPriceMax;
+            price -= litPrice;
         }
         return price;
     }
@@ -239,7 +290,17 @@
  * cost of literalLength symbol */
 static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel)
 {
-    if (optPtr->priceType == zop_predef) return WEIGHT(litLength, optLevel);
+    assert(litLength <= ZSTD_BLOCKSIZE_MAX);
+    if (optPtr->priceType == zop_predef)
+        return WEIGHT(litLength, optLevel);
+
+    /* ZSTD_LLcode() can't compute litLength price for sizes >= ZSTD_BLOCKSIZE_MAX
+     * because it isn't representable in the zstd format.
+     * So instead just pretend it would cost 1 bit more than ZSTD_BLOCKSIZE_MAX - 1.
+     * In such a case, the block would be all literals.
+     */
+    if (litLength == ZSTD_BLOCKSIZE_MAX)
+        return BITCOST_MULTIPLIER + ZSTD_litLengthPrice(ZSTD_BLOCKSIZE_MAX - 1, optPtr, optLevel);
 
     /* dynamic statistics */
     {   U32 const llCode = ZSTD_LLcode(litLength);
@@ -250,22 +311,25 @@
 }
 
 /* ZSTD_getMatchPrice() :
- * Provides the cost of the match part (offset + matchLength) of a sequence
+ * Provides the cost of the match part (offset + matchLength) of a sequence.
  * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence.
- * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */
+ * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq()
+ * @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency)
+ */
 FORCE_INLINE_TEMPLATE U32
-ZSTD_getMatchPrice(U32 const offset,
+ZSTD_getMatchPrice(U32 const offBase,
                    U32 const matchLength,
              const optState_t* const optPtr,
                    int const optLevel)
 {
     U32 price;
-    U32 const offCode = ZSTD_highbit32(offset+1);
+    U32 const offCode = ZSTD_highbit32(offBase);
     U32 const mlBase = matchLength - MINMATCH;
     assert(matchLength >= MINMATCH);
 
-    if (optPtr->priceType == zop_predef)  /* fixed scheme, do not use statistics */
-        return WEIGHT(mlBase, optLevel) + ((16 + offCode) * BITCOST_MULTIPLIER);
+    if (optPtr->priceType == zop_predef)  /* fixed scheme, does not use statistics */
+        return WEIGHT(mlBase, optLevel)
+             + ((16 + offCode) * BITCOST_MULTIPLIER); /* emulated offset cost */
 
     /* dynamic statistics */
     price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel));
@@ -284,10 +348,10 @@
 }
 
 /* ZSTD_updateStats() :
- * assumption : literals + litLengtn <= iend */
+ * assumption : literals + litLength <= iend */
 static void ZSTD_updateStats(optState_t* const optPtr,
                              U32 litLength, const BYTE* literals,
-                             U32 offsetCode, U32 matchLength)
+                             U32 offBase, U32 matchLength)
 {
     /* literals */
     if (ZSTD_compressedLiterals(optPtr)) {
@@ -303,8 +367,8 @@
         optPtr->litLengthSum++;
     }
 
-    /* match offset code (0-2=>repCode; 3+=>offset+2) */
-    {   U32 const offCode = ZSTD_highbit32(offsetCode+1);
+    /* offset code : follows storeSeq() numeric representation */
+    {   U32 const offCode = ZSTD_highbit32(offBase);
         assert(offCode <= MaxOff);
         optPtr->offCodeFreq[offCode]++;
         optPtr->offCodeSum++;
@@ -338,7 +402,7 @@
 
 /* Update hashTable3 up to ip (excluded)
    Assumption : always within prefix (i.e. not within extDict) */
-static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_matchState_t* ms,
+static U32 ZSTD_insertAndFindFirstIndexHash3 (const ZSTD_matchState_t* ms,
                                               U32* nextToUpdate3,
                                               const BYTE* const ip)
 {
@@ -364,11 +428,13 @@
 *  Binary Tree search
 ***************************************/
 /** ZSTD_insertBt1() : add one or multiple positions to tree.
- *  ip : assumed <= iend-8 .
+ * @param ip assumed <= iend-8 .
+ * @param target The target of ZSTD_updateTree_internal() - we are filling to this position
  * @return : nb of positions added */
 static U32 ZSTD_insertBt1(
-                ZSTD_matchState_t* ms,
+                const ZSTD_matchState_t* ms,
                 const BYTE* const ip, const BYTE* const iend,
+                U32 const target,
                 U32 const mls, const int extDict)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
@@ -391,7 +457,10 @@
     U32* smallerPtr = bt + 2*(curr&btMask);
     U32* largerPtr  = smallerPtr + 1;
     U32 dummy32;   /* to be nullified at the end */
-    U32 const windowLow = ms->window.lowLimit;
+    /* windowLow is based on target because
+     * we only need positions that will be in the window at the end of the tree update.
+     */
+    U32 const windowLow = ZSTD_getLowestMatchIndex(ms, target, cParams->windowLog);
     U32 matchEndIdx = curr+8+1;
     size_t bestLength = 8;
     U32 nbCompares = 1U << cParams->searchLog;
@@ -404,11 +473,12 @@
 
     DEBUGLOG(8, "ZSTD_insertBt1 (%u)", curr);
 
+    assert(curr <= target);
     assert(ip <= iend-8);   /* required for h calculation */
     hashTable[h] = curr;   /* Update Hash Table */
 
     assert(windowLow > 0);
-    while (nbCompares-- && (matchIndex >= windowLow)) {
+    for (; nbCompares && (matchIndex >= windowLow); --nbCompares) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
         assert(matchIndex < curr);
@@ -492,7 +562,7 @@
                 idx, target, dictMode);
 
     while(idx < target) {
-        U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, mls, dictMode == ZSTD_extDict);
+        U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, target, mls, dictMode == ZSTD_extDict);
         assert(idx < (U32)(idx + forward));
         idx += forward;
     }
@@ -505,16 +575,17 @@
     ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict);
 }
 
-FORCE_INLINE_TEMPLATE
-U32 ZSTD_insertBtAndGetAllMatches (
-                    ZSTD_match_t* matches,   /* store result (found matches) in this table (presumed large enough) */
-                    ZSTD_matchState_t* ms,
-                    U32* nextToUpdate3,
-                    const BYTE* const ip, const BYTE* const iLimit, const ZSTD_dictMode_e dictMode,
-                    const U32 rep[ZSTD_REP_NUM],
-                    U32 const ll0,   /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
-                    const U32 lengthToBeat,
-                    U32 const mls /* template */)
+FORCE_INLINE_TEMPLATE U32
+ZSTD_insertBtAndGetAllMatches (
+                ZSTD_match_t* matches,  /* store result (found matches) in this table (presumed large enough) */
+                ZSTD_matchState_t* ms,
+                U32* nextToUpdate3,
+                const BYTE* const ip, const BYTE* const iLimit,
+                const ZSTD_dictMode_e dictMode,
+                const U32 rep[ZSTD_REP_NUM],
+                const U32 ll0,  /* tells if associated literal length is 0 or not. This value must be 0 or 1 */
+                const U32 lengthToBeat,
+                const U32 mls /* template */)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
@@ -597,7 +668,7 @@
                 DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u",
                             repCode, ll0, repOffset, repLen);
                 bestLength = repLen;
-                matches[mnum].off = repCode - ll0;
+                matches[mnum].off = REPCODE_TO_OFFBASE(repCode - ll0 + 1);  /* expect value between 1 and 3 */
                 matches[mnum].len = (U32)repLen;
                 mnum++;
                 if ( (repLen > sufficient_len)
@@ -626,7 +697,7 @@
                 bestLength = mlen;
                 assert(curr > matchIndex3);
                 assert(mnum==0);  /* no prior solution */
-                matches[0].off = (curr - matchIndex3) + ZSTD_REP_MOVE;
+                matches[0].off = OFFSET_TO_OFFBASE(curr - matchIndex3);
                 matches[0].len = (U32)mlen;
                 mnum = 1;
                 if ( (mlen > sufficient_len) |
@@ -635,11 +706,11 @@
                     return 1;
         }   }   }
         /* no dictMatchState lookup: dicts don't have a populated HC3 table */
-    }
+    }  /* if (mls == 3) */
 
     hashTable[h] = curr;   /* Update Hash Table */
 
-    while (nbCompares-- && (matchIndex >= matchLow)) {
+    for (; nbCompares && (matchIndex >= matchLow); --nbCompares) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         const BYTE* match;
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
@@ -659,21 +730,20 @@
         }
 
         if (matchLength > bestLength) {
-            DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)",
-                    (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
+            DEBUGLOG(8, "found match of length %u at distance %u (offBase=%u)",
+                    (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));
             assert(matchEndIdx > matchIndex);
             if (matchLength > matchEndIdx - matchIndex)
                 matchEndIdx = matchIndex + (U32)matchLength;
             bestLength = matchLength;
-            matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
+            matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);
             matches[mnum].len = (U32)matchLength;
             mnum++;
             if ( (matchLength > ZSTD_OPT_NUM)
                | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
                 if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */
                 break; /* drop, to preserve bt consistency (miss a little bit of compression) */
-            }
-        }
+        }   }
 
         if (match[matchLength] < ip[matchLength]) {
             /* match smaller than current */
@@ -692,12 +762,13 @@
 
     *smallerPtr = *largerPtr = 0;
 
+    assert(nbCompares <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */
     if (dictMode == ZSTD_dictMatchState && nbCompares) {
         size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls);
         U32 dictMatchIndex = dms->hashTable[dmsH];
         const U32* const dmsBt = dms->chainTable;
         commonLengthSmaller = commonLengthLarger = 0;
-        while (nbCompares-- && (dictMatchIndex > dmsLowLimit)) {
+        for (; nbCompares && (dictMatchIndex > dmsLowLimit); --nbCompares) {
             const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask);
             size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
             const BYTE* match = dmsBase + dictMatchIndex;
@@ -707,19 +778,18 @@
 
             if (matchLength > bestLength) {
                 matchIndex = dictMatchIndex + dmsIndexDelta;
-                DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)",
-                        (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
+                DEBUGLOG(8, "found dms match of length %u at distance %u (offBase=%u)",
+                        (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));
                 if (matchLength > matchEndIdx - matchIndex)
                     matchEndIdx = matchIndex + (U32)matchLength;
                 bestLength = matchLength;
-                matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
+                matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);
                 matches[mnum].len = (U32)matchLength;
                 mnum++;
                 if ( (matchLength > ZSTD_OPT_NUM)
                    | (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {
                     break;   /* drop, to guarantee consistency (miss a little bit of compression) */
-                }
-            }
+            }   }
 
             if (dictMatchIndex <= dmsBtLow) { break; }   /* beyond tree size, stop the search */
             if (match[matchLength] < ip[matchLength]) {
@@ -729,39 +799,91 @@
                 /* match is larger than current */
                 commonLengthLarger = matchLength;
                 dictMatchIndex = nextPtr[0];
-            }
-        }
-    }
+    }   }   }  /* if (dictMode == ZSTD_dictMatchState) */
 
     assert(matchEndIdx > curr+8);
     ms->nextToUpdate = matchEndIdx - 8;  /* skip repetitive patterns */
     return mnum;
 }
 
+typedef U32 (*ZSTD_getAllMatchesFn)(
+    ZSTD_match_t*,
+    ZSTD_matchState_t*,
+    U32*,
+    const BYTE*,
+    const BYTE*,
+    const U32 rep[ZSTD_REP_NUM],
+    U32 const ll0,
+    U32 const lengthToBeat);
 
-FORCE_INLINE_TEMPLATE U32 ZSTD_BtGetAllMatches (
-                        ZSTD_match_t* matches,   /* store result (match found, increasing size) in this table */
-                        ZSTD_matchState_t* ms,
-                        U32* nextToUpdate3,
-                        const BYTE* ip, const BYTE* const iHighLimit, const ZSTD_dictMode_e dictMode,
-                        const U32 rep[ZSTD_REP_NUM],
-                        U32 const ll0,
-                        U32 const lengthToBeat)
+FORCE_INLINE_TEMPLATE U32 ZSTD_btGetAllMatches_internal(
+        ZSTD_match_t* matches,
+        ZSTD_matchState_t* ms,
+        U32* nextToUpdate3,
+        const BYTE* ip,
+        const BYTE* const iHighLimit,
+        const U32 rep[ZSTD_REP_NUM],
+        U32 const ll0,
+        U32 const lengthToBeat,
+        const ZSTD_dictMode_e dictMode,
+        const U32 mls)
 {
-    const ZSTD_compressionParameters* const cParams = &ms->cParams;
-    U32 const matchLengthSearch = cParams->minMatch;
-    DEBUGLOG(8, "ZSTD_BtGetAllMatches");
-    if (ip < ms->window.base + ms->nextToUpdate) return 0;   /* skipped area */
-    ZSTD_updateTree_internal(ms, ip, iHighLimit, matchLengthSearch, dictMode);
-    switch(matchLengthSearch)
-    {
-    case 3 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 3);
-    default :
-    case 4 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 4);
-    case 5 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 5);
-    case 7 :
-    case 6 : return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, 6);
+    assert(BOUNDED(3, ms->cParams.minMatch, 6) == mls);
+    DEBUGLOG(8, "ZSTD_BtGetAllMatches(dictMode=%d, mls=%u)", (int)dictMode, mls);
+    if (ip < ms->window.base + ms->nextToUpdate)
+        return 0;   /* skipped area */
+    ZSTD_updateTree_internal(ms, ip, iHighLimit, mls, dictMode);
+    return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, mls);
+}
+
+#define ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls) ZSTD_btGetAllMatches_##dictMode##_##mls
+
+#define GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, mls)            \
+    static U32 ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls)(      \
+            ZSTD_match_t* matches,                             \
+            ZSTD_matchState_t* ms,                             \
+            U32* nextToUpdate3,                                \
+            const BYTE* ip,                                    \
+            const BYTE* const iHighLimit,                      \
+            const U32 rep[ZSTD_REP_NUM],                       \
+            U32 const ll0,                                     \
+            U32 const lengthToBeat)                            \
+    {                                                          \
+        return ZSTD_btGetAllMatches_internal(                  \
+                matches, ms, nextToUpdate3, ip, iHighLimit,    \
+                rep, ll0, lengthToBeat, ZSTD_##dictMode, mls); \
     }
+
+#define GEN_ZSTD_BT_GET_ALL_MATCHES(dictMode)  \
+    GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 3)  \
+    GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 4)  \
+    GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 5)  \
+    GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 6)
+
+GEN_ZSTD_BT_GET_ALL_MATCHES(noDict)
+GEN_ZSTD_BT_GET_ALL_MATCHES(extDict)
+GEN_ZSTD_BT_GET_ALL_MATCHES(dictMatchState)
+
+#define ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMode)  \
+    {                                            \
+        ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 3), \
+        ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 4), \
+        ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 5), \
+        ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 6)  \
+    }
+
+static ZSTD_getAllMatchesFn
+ZSTD_selectBtGetAllMatches(ZSTD_matchState_t const* ms, ZSTD_dictMode_e const dictMode)
+{
+    ZSTD_getAllMatchesFn const getAllMatchesFns[3][4] = {
+        ZSTD_BT_GET_ALL_MATCHES_ARRAY(noDict),
+        ZSTD_BT_GET_ALL_MATCHES_ARRAY(extDict),
+        ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMatchState)
+    };
+    U32 const mls = BOUNDED(3, ms->cParams.minMatch, 6);
+    assert((U32)dictMode < 3);
+    assert(mls - 3 < 4);
+    return getAllMatchesFns[(int)dictMode][mls - 3];
 }
 
 /*************************
@@ -770,16 +892,18 @@
 
 /* Struct containing info needed to make decision about ldm inclusion */
 typedef struct {
-    rawSeqStore_t seqStore;         /* External match candidates store for this block */
-    U32 startPosInBlock;            /* Start position of the current match candidate */
-    U32 endPosInBlock;              /* End position of the current match candidate */
-    U32 offset;                     /* Offset of the match candidate */
+    rawSeqStore_t seqStore;   /* External match candidates store for this block */
+    U32 startPosInBlock;      /* Start position of the current match candidate */
+    U32 endPosInBlock;        /* End position of the current match candidate */
+    U32 offset;               /* Offset of the match candidate */
 } ZSTD_optLdm_t;
 
 /* ZSTD_optLdm_skipRawSeqStoreBytes():
- * Moves forward in rawSeqStore by nbBytes, which will update the fields 'pos' and 'posInSequence'.
+ * Moves forward in @rawSeqStore by @nbBytes,
+ * which will update the fields 'pos' and 'posInSequence'.
  */
-static void ZSTD_optLdm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) {
+static void ZSTD_optLdm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes)
+{
     U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
     while (currPos && rawSeqStore->pos < rawSeqStore->size) {
         rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
@@ -800,8 +924,10 @@
  * Calculates the beginning and end of the next match in the current block.
  * Updates 'pos' and 'posInSequence' of the ldmSeqStore.
  */
-static void ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock,
-                                                   U32 blockBytesRemaining) {
+static void
+ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock,
+                                       U32 blockBytesRemaining)
+{
     rawSeq currSeq;
     U32 currBlockEndPos;
     U32 literalsBytesRemaining;
@@ -813,8 +939,8 @@
         optLdm->endPosInBlock = UINT_MAX;
         return;
     }
-    /* Calculate appropriate bytes left in matchLength and litLength after adjusting
-       based on ldmSeqStore->posInSequence */
+    /* Calculate appropriate bytes left in matchLength and litLength
+     * after adjusting based on ldmSeqStore->posInSequence */
     currSeq = optLdm->seqStore.seq[optLdm->seqStore.pos];
     assert(optLdm->seqStore.posInSequence <= currSeq.litLength + currSeq.matchLength);
     currBlockEndPos = currPosInBlock + blockBytesRemaining;
@@ -850,15 +976,16 @@
 }
 
 /* ZSTD_optLdm_maybeAddMatch():
- * Adds a match if it's long enough, based on it's 'matchStartPosInBlock'
- * and 'matchEndPosInBlock', into 'matches'. Maintains the correct ordering of 'matches'
+ * Adds a match if it's long enough,
+ * based on it's 'matchStartPosInBlock' and 'matchEndPosInBlock',
+ * into 'matches'. Maintains the correct ordering of 'matches'.
  */
 static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches,
-                                      ZSTD_optLdm_t* optLdm, U32 currPosInBlock) {
-    U32 posDiff = currPosInBlock - optLdm->startPosInBlock;
-    /* Note: ZSTD_match_t actually contains offCode and matchLength (before subtracting MINMATCH) */
-    U32 candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff;
-    U32 candidateOffCode = optLdm->offset + ZSTD_REP_MOVE;
+                                      const ZSTD_optLdm_t* optLdm, U32 currPosInBlock)
+{
+    U32 const posDiff = currPosInBlock - optLdm->startPosInBlock;
+    /* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */
+    U32 const candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff;
 
     /* Ensure that current block position is not outside of the match */
     if (currPosInBlock < optLdm->startPosInBlock
@@ -868,10 +995,11 @@
     }
 
     if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) {
-        DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offCode: %u matchLength %u) at block position=%u",
-                 candidateOffCode, candidateMatchLength, currPosInBlock);
+        U32 const candidateOffBase = OFFSET_TO_OFFBASE(optLdm->offset);
+        DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offBase: %u matchLength %u) at block position=%u",
+                 candidateOffBase, candidateMatchLength, currPosInBlock);
         matches[*nbMatches].len = candidateMatchLength;
-        matches[*nbMatches].off = candidateOffCode;
+        matches[*nbMatches].off = candidateOffBase;
         (*nbMatches)++;
     }
 }
@@ -879,8 +1007,11 @@
 /* ZSTD_optLdm_processMatchCandidate():
  * Wrapper function to update ldm seq store and call ldm functions as necessary.
  */
-static void ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm, ZSTD_match_t* matches, U32* nbMatches,
-                                              U32 currPosInBlock, U32 remainingBytes) {
+static void
+ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm,
+                                  ZSTD_match_t* matches, U32* nbMatches,
+                                  U32 currPosInBlock, U32 remainingBytes)
+{
     if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) {
         return;
     }
@@ -891,19 +1022,19 @@
              * at the end of a match from the ldm seq store, and will often be some bytes
              * over beyond matchEndPosInBlock. As such, we need to correct for these "overshoots"
              */
-            U32 posOvershoot = currPosInBlock - optLdm->endPosInBlock;
+            U32 const posOvershoot = currPosInBlock - optLdm->endPosInBlock;
             ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, posOvershoot);
-        } 
+        }
         ZSTD_opt_getNextMatchAndUpdateSeqStore(optLdm, currPosInBlock, remainingBytes);
     }
     ZSTD_optLdm_maybeAddMatch(matches, nbMatches, optLdm, currPosInBlock);
 }
 
+
 /*-*******************************
 *  Optimal parser
 *********************************/
 
-
 static U32 ZSTD_totalLen(ZSTD_optimal_t sol)
 {
     return sol.litlen + sol.mlen;
@@ -944,6 +1075,8 @@
     const BYTE* const prefixStart = base + ms->window.dictLimit;
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
 
+    ZSTD_getAllMatchesFn getAllMatches = ZSTD_selectBtGetAllMatches(ms, dictMode);
+
     U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
     U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;
     U32 nextToUpdate3 = ms->nextToUpdate;
@@ -953,6 +1086,8 @@
     ZSTD_optimal_t lastSequence;
     ZSTD_optLdm_t optLdm;
 
+    ZSTD_memset(&lastSequence, 0, sizeof(ZSTD_optimal_t));
+
     optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore;
     optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0;
     ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip));
@@ -971,7 +1106,7 @@
         /* find first match */
         {   U32 const litlen = (U32)(ip - anchor);
             U32 const ll0 = !litlen;
-            U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch);
+            U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, ip, iend, rep, ll0, minMatch);
             ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,
                                               (U32)(ip-istart), (U32)(iend - ip));
             if (!nbMatches) { ip++; continue; }
@@ -985,18 +1120,18 @@
              * in every price. We include the literal length to avoid negative
              * prices when we subtract the previous literal length.
              */
-            opt[0].price = ZSTD_litLengthPrice(litlen, optStatePtr, optLevel);
+            opt[0].price = (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel);
 
             /* large match -> immediate encoding */
             {   U32 const maxML = matches[nbMatches-1].len;
-                U32 const maxOffset = matches[nbMatches-1].off;
-                DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series",
-                            nbMatches, maxML, maxOffset, (U32)(ip-prefixStart));
+                U32 const maxOffBase = matches[nbMatches-1].off;
+                DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffBase=%u at cPos=%u => start new series",
+                            nbMatches, maxML, maxOffBase, (U32)(ip-prefixStart));
 
                 if (maxML > sufficient_len) {
                     lastSequence.litlen = litlen;
                     lastSequence.mlen = maxML;
-                    lastSequence.off = maxOffset;
+                    lastSequence.off = maxOffBase;
                     DEBUGLOG(6, "large match (%u>%u), immediate encoding",
                                 maxML, sufficient_len);
                     cur = 0;
@@ -1005,24 +1140,25 @@
             }   }
 
             /* set prices for first matches starting position == 0 */
-            {   U32 const literalsPrice = opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
+            assert(opt[0].price >= 0);
+            {   U32 const literalsPrice = (U32)opt[0].price + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
                 U32 pos;
                 U32 matchNb;
                 for (pos = 1; pos < minMatch; pos++) {
                     opt[pos].price = ZSTD_MAX_PRICE;   /* mlen, litlen and price will be fixed during forward scanning */
                 }
                 for (matchNb = 0; matchNb < nbMatches; matchNb++) {
-                    U32 const offset = matches[matchNb].off;
+                    U32 const offBase = matches[matchNb].off;
                     U32 const end = matches[matchNb].len;
                     for ( ; pos <= end ; pos++ ) {
-                        U32 const matchPrice = ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel);
+                        U32 const matchPrice = ZSTD_getMatchPrice(offBase, pos, optStatePtr, optLevel);
                         U32 const sequencePrice = literalsPrice + matchPrice;
                         DEBUGLOG(7, "rPos:%u => set initial price : %.2f",
-                                    pos, ZSTD_fCost(sequencePrice));
+                                    pos, ZSTD_fCost((int)sequencePrice));
                         opt[pos].mlen = pos;
-                        opt[pos].off = offset;
+                        opt[pos].off = offBase;
                         opt[pos].litlen = litlen;
-                        opt[pos].price = sequencePrice;
+                        opt[pos].price = (int)sequencePrice;
                 }   }
                 last_pos = pos-1;
             }
@@ -1037,9 +1173,9 @@
             /* Fix current position with one literal if cheaper */
             {   U32 const litlen = (opt[cur-1].mlen == 0) ? opt[cur-1].litlen + 1 : 1;
                 int const price = opt[cur-1].price
-                                + ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel)
-                                + ZSTD_litLengthPrice(litlen, optStatePtr, optLevel)
-                                - ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel);
+                                + (int)ZSTD_rawLiteralsCost(ip+cur-1, 1, optStatePtr, optLevel)
+                                + (int)ZSTD_litLengthPrice(litlen, optStatePtr, optLevel)
+                                - (int)ZSTD_litLengthPrice(litlen-1, optStatePtr, optLevel);
                 assert(price < 1000000000); /* overflow check */
                 if (price <= opt[cur].price) {
                     DEBUGLOG(7, "cPos:%zi==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)",
@@ -1065,7 +1201,7 @@
             assert(cur >= opt[cur].mlen);
             if (opt[cur].mlen != 0) {
                 U32 const prev = cur - opt[cur].mlen;
-                repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0);
+                repcodes_t const newReps = ZSTD_newRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0);
                 ZSTD_memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t));
             } else {
                 ZSTD_memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t));
@@ -1082,11 +1218,12 @@
                 continue;  /* skip unpromising positions; about ~+6% speed, -0.01 ratio */
             }
 
+            assert(opt[cur].price >= 0);
             {   U32 const ll0 = (opt[cur].mlen != 0);
                 U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0;
-                U32 const previousPrice = opt[cur].price;
+                U32 const previousPrice = (U32)opt[cur].price;
                 U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
-                U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch);
+                U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, inr, iend, opt[cur].rep, ll0, minMatch);
                 U32 matchNb;
 
                 ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,
@@ -1119,12 +1256,12 @@
                     U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch;
                     U32 mlen;
 
-                    DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u",
+                    DEBUGLOG(7, "testing match %u => offBase=%4u, mlen=%2u, llen=%2u",
                                 matchNb, matches[matchNb].off, lastML, litlen);
 
                     for (mlen = lastML; mlen >= startML; mlen--) {  /* scan downward */
                         U32 const pos = cur + mlen;
-                        int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel);
+                        int const price = (int)basePrice + (int)ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel);
 
                         if ((pos > last_pos) || (price < opt[pos].price)) {
                             DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)",
@@ -1154,7 +1291,7 @@
          * update them while traversing the sequences.
          */
         if (lastSequence.mlen != 0) {
-            repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0);
+            repcodes_t const reps = ZSTD_newRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0);
             ZSTD_memcpy(rep, &reps, sizeof(reps));
         } else {
             ZSTD_memcpy(rep, opt[cur].rep, sizeof(repcodes_t));
@@ -1185,7 +1322,7 @@
                 for (storePos=storeStart; storePos <= storeEnd; storePos++) {
                     U32 const llen = opt[storePos].litlen;
                     U32 const mlen = opt[storePos].mlen;
-                    U32 const offCode = opt[storePos].off;
+                    U32 const offBase = opt[storePos].off;
                     U32 const advance = llen + mlen;
                     DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u",
                                 anchor - istart, (unsigned)llen, (unsigned)mlen);
@@ -1197,8 +1334,8 @@
                     }
 
                     assert(anchor + llen <= iend);
-                    ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen);
-                    ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen-MINMATCH);
+                    ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen);
+                    ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen);
                     anchor += advance;
                     ip = anchor;
             }   }
@@ -1210,43 +1347,35 @@
     return (size_t)(iend - anchor);
 }
 
+static size_t ZSTD_compressBlock_opt0(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode)
+{
+    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /* optLevel */, dictMode);
+}
+
+static size_t ZSTD_compressBlock_opt2(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode)
+{
+    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /* optLevel */, dictMode);
+}
 
 size_t ZSTD_compressBlock_btopt(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
     DEBUGLOG(5, "ZSTD_compressBlock_btopt");
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_noDict);
+    return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_noDict);
 }
 
 
-/* used in 2-pass strategy */
-static U32 ZSTD_upscaleStat(unsigned* table, U32 lastEltIndex, int bonus)
-{
-    U32 s, sum=0;
-    assert(ZSTD_FREQ_DIV+bonus >= 0);
-    for (s=0; s<lastEltIndex+1; s++) {
-        table[s] <<= ZSTD_FREQ_DIV+bonus;
-        table[s]--;
-        sum += table[s];
-    }
-    return sum;
-}
 
-/* used in 2-pass strategy */
-MEM_STATIC void ZSTD_upscaleStats(optState_t* optPtr)
-{
-    if (ZSTD_compressedLiterals(optPtr))
-        optPtr->litSum = ZSTD_upscaleStat(optPtr->litFreq, MaxLit, 0);
-    optPtr->litLengthSum = ZSTD_upscaleStat(optPtr->litLengthFreq, MaxLL, 0);
-    optPtr->matchLengthSum = ZSTD_upscaleStat(optPtr->matchLengthFreq, MaxML, 0);
-    optPtr->offCodeSum = ZSTD_upscaleStat(optPtr->offCodeFreq, MaxOff, 0);
-}
 
 /* ZSTD_initStats_ultra():
  * make a first compression pass, just to seed stats with more accurate starting values.
  * only works on first block, with no dictionary and no ldm.
- * this function cannot error, hence its contract must be respected.
+ * this function cannot error out, its narrow contract must be respected.
  */
 static void
 ZSTD_initStats_ultra(ZSTD_matchState_t* ms,
@@ -1263,17 +1392,15 @@
     assert(ms->window.dictLimit == ms->window.lowLimit);   /* no dictionary */
     assert(ms->window.dictLimit - ms->nextToUpdate <= 1);  /* no prefix (note: intentional overflow, defined as 2-complement) */
 
-    ZSTD_compressBlock_opt_generic(ms, seqStore, tmpRep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict);   /* generate stats into ms->opt*/
+    ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_noDict);   /* generate stats into ms->opt*/
 
-    /* invalidate first scan from history */
+    /* invalidate first scan from history, only keep entropy stats */
     ZSTD_resetSeqStore(seqStore);
     ms->window.base -= srcSize;
     ms->window.dictLimit += (U32)srcSize;
     ms->window.lowLimit = ms->window.dictLimit;
     ms->nextToUpdate = ms->window.dictLimit;
 
-    /* re-inforce weight of collected statistics */
-    ZSTD_upscaleStats(&ms->opt);
 }
 
 size_t ZSTD_compressBlock_btultra(
@@ -1281,7 +1408,7 @@
         const void* src, size_t srcSize)
 {
     DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize);
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict);
+    return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict);
 }
 
 size_t ZSTD_compressBlock_btultra2(
@@ -1291,53 +1418,53 @@
     U32 const curr = (U32)((const BYTE*)src - ms->window.base);
     DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize);
 
-    /* 2-pass strategy:
+    /* 2-passes strategy:
      * this strategy makes a first pass over first block to collect statistics
-     * and seed next round's statistics with it.
-     * After 1st pass, function forgets everything, and starts a new block.
+     * in order to seed next round's statistics with it.
+     * After 1st pass, function forgets history, and starts a new block.
      * Consequently, this can only work if no data has been previously loaded in tables,
      * aka, no dictionary, no prefix, no ldm preprocessing.
      * The compression ratio gain is generally small (~0.5% on first block),
-     * the cost is 2x cpu time on first block. */
+    ** the cost is 2x cpu time on first block. */
     assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
     if ( (ms->opt.litLengthSum==0)   /* first block */
       && (seqStore->sequences == seqStore->sequencesStart)  /* no ldm */
       && (ms->window.dictLimit == ms->window.lowLimit)   /* no dictionary */
-      && (curr == ms->window.dictLimit)   /* start of frame, nothing already loaded nor skipped */
-      && (srcSize > ZSTD_PREDEF_THRESHOLD)
+      && (curr == ms->window.dictLimit)    /* start of frame, nothing already loaded nor skipped */
+      && (srcSize > ZSTD_PREDEF_THRESHOLD) /* input large enough to not employ default stats */
       ) {
         ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize);
     }
 
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_noDict);
+    return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict);
 }
 
 size_t ZSTD_compressBlock_btopt_dictMatchState(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_dictMatchState);
+    return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState);
 }
 
 size_t ZSTD_compressBlock_btultra_dictMatchState(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_dictMatchState);
+    return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState);
 }
 
 size_t ZSTD_compressBlock_btopt_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /*optLevel*/, ZSTD_extDict);
+    return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_extDict);
 }
 
 size_t ZSTD_compressBlock_btultra_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
-    return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /*optLevel*/, ZSTD_extDict);
+    return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_extDict);
 }
 
 /* note : no btultra2 variant for extDict nor dictMatchState,
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.h b/Utilities/cmzstd/lib/compress/zstd_opt.h
index 627255f..342e5a3 100644
--- a/Utilities/cmzstd/lib/compress/zstd_opt.h
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.c b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
index 22aa3e1..6786075 100644
--- a/Utilities/cmzstd/lib/compress/zstdmt_compress.c
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -20,6 +20,7 @@
 
 
 /* ======   Dependencies   ====== */
+#include "../common/allocations.h"  /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */
 #include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memset, INT_MAX, UINT_MAX */
 #include "../common/mem.h"         /* MEM_STATIC */
 #include "../common/pool.h"        /* threadpool */
@@ -102,9 +103,8 @@
     buffer_t bTable[1];   /* variable size */
 } ZSTDMT_bufferPool;
 
-static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem)
+static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned maxNbBuffers, ZSTD_customMem cMem)
 {
-    unsigned const maxNbBuffers = 2*nbWorkers + 3;
     ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_customCalloc(
         sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem);
     if (bufPool==NULL) return NULL;
@@ -160,9 +160,8 @@
 }
 
 
-static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, U32 nbWorkers)
+static ZSTDMT_bufferPool* ZSTDMT_expandBufferPool(ZSTDMT_bufferPool* srcBufPool, unsigned maxNbBuffers)
 {
-    unsigned const maxNbBuffers = 2*nbWorkers + 3;
     if (srcBufPool==NULL) return NULL;
     if (srcBufPool->totalBuffers >= maxNbBuffers) /* good enough */
         return srcBufPool;
@@ -171,7 +170,7 @@
         size_t const bSize = srcBufPool->bufferSize;   /* forward parameters */
         ZSTDMT_bufferPool* newBufPool;
         ZSTDMT_freeBufferPool(srcBufPool);
-        newBufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+        newBufPool = ZSTDMT_createBufferPool(maxNbBuffers, cMem);
         if (newBufPool==NULL) return newBufPool;
         ZSTDMT_setBufferSize(newBufPool, bSize);
         return newBufPool;
@@ -263,6 +262,16 @@
     ZSTD_customFree(buf.start, bufPool->cMem);
 }
 
+/* We need 2 output buffers per worker since each dstBuff must be flushed after it is released.
+ * The 3 additional buffers are as follows:
+ *   1 buffer for input loading
+ *   1 buffer for "next input" when submitting current one
+ *   1 buffer stuck in queue */
+#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) (2*(nbWorkers) + 3)
+
+/* After a worker releases its rawSeqStore, it is immediately ready for reuse.
+ * So we only need one seq buffer per worker. */
+#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) (nbWorkers)
 
 /* =====   Seq Pool Wrapper   ====== */
 
@@ -316,7 +325,7 @@
 
 static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem)
 {
-    ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+    ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(SEQ_POOL_MAX_NB_BUFFERS(nbWorkers), cMem);
     if (seqPool == NULL) return NULL;
     ZSTDMT_setNbSeq(seqPool, 0);
     return seqPool;
@@ -329,7 +338,7 @@
 
 static ZSTDMT_seqPool* ZSTDMT_expandSeqPool(ZSTDMT_seqPool* pool, U32 nbWorkers)
 {
-    return ZSTDMT_expandBufferPool(pool, nbWorkers);
+    return ZSTDMT_expandBufferPool(pool, SEQ_POOL_MAX_NB_BUFFERS(nbWorkers));
 }
 
 
@@ -467,7 +476,7 @@
                          ZSTD_dictContentType_e dictContentType)
 {
     /* Adjust parameters */
-    if (params.ldmParams.enableLdm) {
+    if (params.ldmParams.enableLdm == ZSTD_ps_enable) {
         DEBUGLOG(4, "LDM window size = %u KB", (1U << params.cParams.windowLog) >> 10);
         ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
         assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
@@ -478,7 +487,7 @@
     serialState->nextJobID = 0;
     if (params.fParams.checksumFlag)
         XXH64_reset(&serialState->xxhState, 0);
-    if (params.ldmParams.enableLdm) {
+    if (params.ldmParams.enableLdm == ZSTD_ps_enable) {
         ZSTD_customMem cMem = params.customMem;
         unsigned const hashLog = params.ldmParams.hashLog;
         size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t);
@@ -564,7 +573,7 @@
     /* A future job may error and skip our job */
     if (serialState->nextJobID == jobID) {
         /* It is now our turn, do any processing necessary */
-        if (serialState->params.ldmParams.enableLdm) {
+        if (serialState->params.ldmParams.enableLdm == ZSTD_ps_enable) {
             size_t error;
             assert(seqStore.seq != NULL && seqStore.pos == 0 &&
                    seqStore.size == 0 && seqStore.capacity > 0);
@@ -594,7 +603,7 @@
     if (seqStore.size > 0) {
         size_t const err = ZSTD_referenceExternalSequences(
             jobCCtx, seqStore.seq, seqStore.size);
-        assert(serialState->params.ldmParams.enableLdm);
+        assert(serialState->params.ldmParams.enableLdm == ZSTD_ps_enable);
         assert(!ZSTD_isError(err));
         (void)err;
     }
@@ -672,7 +681,7 @@
         if (dstBuff.start==NULL) JOB_ERROR(ERROR(memory_allocation));
         job->dstBuff = dstBuff;   /* this value can be read in ZSTDMT_flush, when it copies the whole job */
     }
-    if (jobParams.ldmParams.enableLdm && rawSeqStore.seq == NULL)
+    if (jobParams.ldmParams.enableLdm == ZSTD_ps_enable && rawSeqStore.seq == NULL)
         JOB_ERROR(ERROR(memory_allocation));
 
     /* Don't compute the checksum for chunks, since we compute it externally,
@@ -680,7 +689,7 @@
      */
     if (job->jobID != 0) jobParams.fParams.checksumFlag = 0;
     /* Don't run LDM for the chunks, since we handle it externally */
-    jobParams.ldmParams.enableLdm = 0;
+    jobParams.ldmParams.enableLdm = ZSTD_ps_disable;
     /* Correct nbWorkers to 0. */
     jobParams.nbWorkers = 0;
 
@@ -711,7 +720,7 @@
     ZSTDMT_serialState_update(job->serial, cctx, rawSeqStore, job->src, job->jobID);
 
     if (!job->firstJob) {  /* flush and overwrite frame header when it's not first job */
-        size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
+        size_t const hSize = ZSTD_compressContinue_public(cctx, dstBuff.start, dstBuff.capacity, job->src.start, 0);
         if (ZSTD_isError(hSize)) JOB_ERROR(hSize);
         DEBUGLOG(5, "ZSTDMT_compressionJob: flush and overwrite %u bytes of frame header (not first job)", (U32)hSize);
         ZSTD_invalidateRepCodes(cctx);
@@ -729,7 +738,7 @@
         DEBUGLOG(5, "ZSTDMT_compressionJob: compress %u bytes in %i blocks", (U32)job->src.size, nbChunks);
         assert(job->cSize == 0);
         for (chunkNb = 1; chunkNb < nbChunks; chunkNb++) {
-            size_t const cSize = ZSTD_compressContinue(cctx, op, oend-op, ip, chunkSize);
+            size_t const cSize = ZSTD_compressContinue_public(cctx, op, oend-op, ip, chunkSize);
             if (ZSTD_isError(cSize)) JOB_ERROR(cSize);
             ip += chunkSize;
             op += cSize; assert(op < oend);
@@ -749,8 +758,8 @@
             size_t const lastBlockSize1 = job->src.size & (chunkSize-1);
             size_t const lastBlockSize = ((lastBlockSize1==0) & (job->src.size>=chunkSize)) ? chunkSize : lastBlockSize1;
             size_t const cSize = (job->lastJob) ?
-                 ZSTD_compressEnd     (cctx, op, oend-op, ip, lastBlockSize) :
-                 ZSTD_compressContinue(cctx, op, oend-op, ip, lastBlockSize);
+                 ZSTD_compressEnd_public(cctx, op, oend-op, ip, lastBlockSize) :
+                 ZSTD_compressContinue_public(cctx, op, oend-op, ip, lastBlockSize);
             if (ZSTD_isError(cSize)) JOB_ERROR(cSize);
             lastCBlockSize = cSize;
     }   }
@@ -807,6 +816,15 @@
 static const roundBuff_t kNullRoundBuff = {NULL, 0, 0};
 
 #define RSYNC_LENGTH 32
+/* Don't create chunks smaller than the zstd block size.
+ * This stops us from regressing compression ratio too much,
+ * and ensures our output fits in ZSTD_compressBound().
+ *
+ * If this is shrunk < ZSTD_BLOCKSIZELOG_MIN then
+ * ZSTD_COMPRESSBOUND() will need to be updated.
+ */
+#define RSYNC_MIN_BLOCK_LOG ZSTD_BLOCKSIZELOG_MAX
+#define RSYNC_MIN_BLOCK_SIZE (1<<RSYNC_MIN_BLOCK_LOG)
 
 typedef struct {
   U64 hash;
@@ -927,7 +945,7 @@
     mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
     assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0);  /* ensure nbJobs is a power of 2 */
     mtctx->jobIDMask = nbJobs - 1;
-    mtctx->bufPool = ZSTDMT_createBufferPool(nbWorkers, cMem);
+    mtctx->bufPool = ZSTDMT_createBufferPool(BUF_POOL_MAX_NB_BUFFERS(nbWorkers), cMem);
     mtctx->cctxPool = ZSTDMT_createCCtxPool(nbWorkers, cMem);
     mtctx->seqPool = ZSTDMT_createSeqPool(nbWorkers, cMem);
     initError = ZSTDMT_serialState_init(&mtctx->serial);
@@ -1030,7 +1048,7 @@
 {
     if (POOL_resize(mtctx->factory, nbWorkers)) return ERROR(memory_allocation);
     FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbWorkers) , "");
-    mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, nbWorkers);
+    mtctx->bufPool = ZSTDMT_expandBufferPool(mtctx->bufPool, BUF_POOL_MAX_NB_BUFFERS(nbWorkers));
     if (mtctx->bufPool == NULL) return ERROR(memory_allocation);
     mtctx->cctxPool = ZSTDMT_expandCCtxPool(mtctx->cctxPool, nbWorkers);
     if (mtctx->cctxPool == NULL) return ERROR(memory_allocation);
@@ -1135,7 +1153,7 @@
 static unsigned ZSTDMT_computeTargetJobLog(const ZSTD_CCtx_params* params)
 {
     unsigned jobLog;
-    if (params->ldmParams.enableLdm) {
+    if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
         /* In Long Range Mode, the windowLog is typically oversized.
          * In which case, it's preferable to determine the jobSize
          * based on cycleLog instead. */
@@ -1179,7 +1197,7 @@
     int const overlapRLog = 9 - ZSTDMT_overlapLog(params->overlapLog, params->cParams.strategy);
     int ovLog = (overlapRLog >= 8) ? 0 : (params->cParams.windowLog - overlapRLog);
     assert(0 <= overlapRLog && overlapRLog <= 8);
-    if (params->ldmParams.enableLdm) {
+    if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
         /* In Long Range Mode, the windowLog is typically oversized.
          * In which case, it's preferable to determine the jobSize
          * based on chainLog instead.
@@ -1252,6 +1270,9 @@
         /* Aim for the targetsectionSize as the average job size. */
         U32 const jobSizeKB = (U32)(mtctx->targetSectionSize >> 10);
         U32 const rsyncBits = (assert(jobSizeKB >= 1), ZSTD_highbit32(jobSizeKB) + 10);
+        /* We refuse to create jobs < RSYNC_MIN_BLOCK_SIZE bytes, so make sure our
+         * expected job size is at least 4x larger. */
+        assert(rsyncBits >= RSYNC_MIN_BLOCK_LOG + 2);
         DEBUGLOG(4, "rsyncLog = %u", rsyncBits);
         mtctx->rsync.hash = 0;
         mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1;
@@ -1263,7 +1284,7 @@
     ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(mtctx->targetSectionSize));
     {
         /* If ldm is enabled we need windowSize space. */
-        size_t const windowSize = mtctx->params.ldmParams.enableLdm ? (1U << mtctx->params.cParams.windowLog) : 0;
+        size_t const windowSize = mtctx->params.ldmParams.enableLdm == ZSTD_ps_enable ? (1U << mtctx->params.cParams.windowLog) : 0;
         /* Two buffers of slack, plus extra space for the overlap
          * This is the minimum slack that LDM works with. One extra because
          * flush might waste up to targetSectionSize-1 bytes. Another extra
@@ -1538,17 +1559,21 @@
 static int ZSTDMT_isOverlapped(buffer_t buffer, range_t range)
 {
     BYTE const* const bufferStart = (BYTE const*)buffer.start;
-    BYTE const* const bufferEnd = bufferStart + buffer.capacity;
     BYTE const* const rangeStart = (BYTE const*)range.start;
-    BYTE const* const rangeEnd = range.size != 0 ? rangeStart + range.size : rangeStart;
 
     if (rangeStart == NULL || bufferStart == NULL)
         return 0;
-    /* Empty ranges cannot overlap */
-    if (bufferStart == bufferEnd || rangeStart == rangeEnd)
-        return 0;
 
-    return bufferStart < rangeEnd && rangeStart < bufferEnd;
+    {
+        BYTE const* const bufferEnd = bufferStart + buffer.capacity;
+        BYTE const* const rangeEnd = rangeStart + range.size;
+
+        /* Empty ranges cannot overlap */
+        if (bufferStart == bufferEnd || rangeStart == rangeEnd)
+            return 0;
+
+        return bufferStart < rangeEnd && rangeStart < bufferEnd;
+    }
 }
 
 static int ZSTDMT_doesOverlapWindow(buffer_t buffer, ZSTD_window_t window)
@@ -1575,7 +1600,7 @@
 
 static void ZSTDMT_waitForLdmComplete(ZSTDMT_CCtx* mtctx, buffer_t buffer)
 {
-    if (mtctx->params.ldmParams.enableLdm) {
+    if (mtctx->params.ldmParams.enableLdm == ZSTD_ps_enable) {
         ZSTD_pthread_mutex_t* mutex = &mtctx->serial.ldmWindowMutex;
         DEBUGLOG(5, "ZSTDMT_waitForLdmComplete");
         DEBUGLOG(5, "source  [0x%zx, 0x%zx)",
@@ -1678,6 +1703,11 @@
     if (!mtctx->params.rsyncable)
         /* Rsync is disabled. */
         return syncPoint;
+    if (mtctx->inBuff.filled + input.size - input.pos < RSYNC_MIN_BLOCK_SIZE)
+        /* We don't emit synchronization points if it would produce too small blocks.
+         * We don't have enough input to find a synchronization point, so don't look.
+         */
+        return syncPoint;
     if (mtctx->inBuff.filled + syncPoint.toLoad < RSYNC_LENGTH)
         /* Not enough to compute the hash.
          * We will miss any synchronization points in this RSYNC_LENGTH byte
@@ -1688,10 +1718,28 @@
          */
         return syncPoint;
     /* Initialize the loop variables. */
-    if (mtctx->inBuff.filled >= RSYNC_LENGTH) {
-        /* We have enough bytes buffered to initialize the hash.
+    if (mtctx->inBuff.filled < RSYNC_MIN_BLOCK_SIZE) {
+        /* We don't need to scan the first RSYNC_MIN_BLOCK_SIZE positions
+         * because they can't possibly be a sync point. So we can start
+         * part way through the input buffer.
+         */
+        pos = RSYNC_MIN_BLOCK_SIZE - mtctx->inBuff.filled;
+        if (pos >= RSYNC_LENGTH) {
+            prev = istart + pos - RSYNC_LENGTH;
+            hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH);
+        } else {
+            assert(mtctx->inBuff.filled >= RSYNC_LENGTH);
+            prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH;
+            hash = ZSTD_rollingHash_compute(prev + pos, (RSYNC_LENGTH - pos));
+            hash = ZSTD_rollingHash_append(hash, istart, pos);
+        }
+    } else {
+        /* We have enough bytes buffered to initialize the hash,
+         * and have processed enough bytes to find a sync point.
          * Start scanning at the beginning of the input.
          */
+        assert(mtctx->inBuff.filled >= RSYNC_MIN_BLOCK_SIZE);
+        assert(RSYNC_MIN_BLOCK_SIZE >= RSYNC_LENGTH);
         pos = 0;
         prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH;
         hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH);
@@ -1705,16 +1753,6 @@
             syncPoint.flush = 1;
             return syncPoint;
         }
-    } else {
-        /* We don't have enough bytes buffered to initialize the hash, but
-         * we know we have at least RSYNC_LENGTH bytes total.
-         * Start scanning after the first RSYNC_LENGTH bytes less the bytes
-         * already buffered.
-         */
-        pos = RSYNC_LENGTH - mtctx->inBuff.filled;
-        prev = (BYTE const*)mtctx->inBuff.buffer.start - pos;
-        hash = ZSTD_rollingHash_compute(mtctx->inBuff.buffer.start, mtctx->inBuff.filled);
-        hash = ZSTD_rollingHash_append(hash, istart, pos);
     }
     /* Starting with the hash of the previous RSYNC_LENGTH bytes, roll
      * through the input. If we hit a synchronization point, then cut the
@@ -1724,16 +1762,24 @@
      * then a block will be emitted anyways, but this is okay, since if we
      * are already synchronized we will remain synchronized.
      */
+    assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
     for (; pos < syncPoint.toLoad; ++pos) {
         BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH];
-        /* if (pos >= RSYNC_LENGTH) assert(ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash); */
+        /* This assert is very expensive, and Debian compiles with asserts enabled.
+         * So disable it for now. We can get similar coverage by checking it at the
+         * beginning & end of the loop.
+         * assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
+         */
         hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower);
+        assert(mtctx->inBuff.filled + pos >= RSYNC_MIN_BLOCK_SIZE);
         if ((hash & hitMask) == hitMask) {
             syncPoint.toLoad = pos + 1;
             syncPoint.flush = 1;
+            ++pos; /* for assert */
             break;
         }
     }
+    assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
     return syncPoint;
 }
 
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.h b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
index 2fee2ec..ed4dc0e 100644
--- a/Utilities/cmzstd/lib/compress/zstdmt_compress.h
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -65,8 +65,11 @@
  *  Private use only. Init streaming operation.
  *  expects params to be valid.
  *  must receive dict, or cdict, or none, but not both.
+ *  mtctx can be freshly constructed or reused from a prior compression.
+ *  If mtctx is reused, memory allocations from the prior compression may not be freed,
+ *  even if they are not needed for the current compression.
  *  @return : 0, or an error code */
-size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
+size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* mtctx,
                     const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
                     const ZSTD_CDict* cdict,
                     ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
diff --git a/Utilities/cmzstd/lib/decompress/huf_decompress.c b/Utilities/cmzstd/lib/decompress/huf_decompress.c
index b93c9a0..5b217ac 100644
--- a/Utilities/cmzstd/lib/decompress/huf_decompress.c
+++ b/Utilities/cmzstd/lib/decompress/huf_decompress.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * huff0 huffman decoder,
  * part of Finite State Entropy library
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -19,9 +19,16 @@
 #include "../common/compiler.h"
 #include "../common/bitstream.h"  /* BIT_* */
 #include "../common/fse.h"        /* to compress headers */
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "../common/error_private.h"
+#include "../common/zstd_internal.h"
+#include "../common/bits.h"       /* ZSTD_highbit32, ZSTD_countTrailingZeros64 */
+
+/* **************************************************************
+*  Constants
+****************************************************************/
+
+#define HUF_DECODER_FAST_TABLELOG 11
 
 /* **************************************************************
 *  Macros
@@ -36,6 +43,28 @@
 #error "Cannot force the use of the X1 and X2 decoders at the same time!"
 #endif
 
+/* When DYNAMIC_BMI2 is enabled, fast decoders are only called when bmi2 is
+ * supported at runtime, so we can add the BMI2 target attribute.
+ * When it is disabled, we will still get BMI2 if it is enabled statically.
+ */
+#if DYNAMIC_BMI2
+# define HUF_FAST_BMI2_ATTRS BMI2_TARGET_ATTRIBUTE
+#else
+# define HUF_FAST_BMI2_ATTRS
+#endif
+
+#ifdef __cplusplus
+# define HUF_EXTERN_C extern "C"
+#else
+# define HUF_EXTERN_C
+#endif
+#define HUF_ASM_DECL HUF_EXTERN_C
+
+#if DYNAMIC_BMI2
+# define HUF_NEED_BMI2_FUNCTION 1
+#else
+# define HUF_NEED_BMI2_FUNCTION 0
+#endif
 
 /* **************************************************************
 *  Error Management
@@ -53,6 +82,11 @@
 /* **************************************************************
 *  BMI2 Variant Wrappers
 ****************************************************************/
+typedef size_t (*HUF_DecompressUsingDTableFn)(void *dst, size_t dstSize,
+                                              const void *cSrc,
+                                              size_t cSrcSize,
+                                              const HUF_DTable *DTable);
+
 #if DYNAMIC_BMI2
 
 #define HUF_DGEN(fn)                                                        \
@@ -65,7 +99,7 @@
         return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable);             \
     }                                                                       \
                                                                             \
-    static TARGET_ATTRIBUTE("bmi2") size_t fn##_bmi2(                       \
+    static BMI2_TARGET_ATTRIBUTE size_t fn##_bmi2(                          \
                   void* dst,  size_t dstSize,                               \
             const void* cSrc, size_t cSrcSize,                              \
             const HUF_DTable* DTable)                                       \
@@ -74,9 +108,9 @@
     }                                                                       \
                                                                             \
     static size_t fn(void* dst, size_t dstSize, void const* cSrc,           \
-                     size_t cSrcSize, HUF_DTable const* DTable, int bmi2)   \
+                     size_t cSrcSize, HUF_DTable const* DTable, int flags)  \
     {                                                                       \
-        if (bmi2) {                                                         \
+        if (flags & HUF_flags_bmi2) {                                       \
             return fn##_bmi2(dst, dstSize, cSrc, cSrcSize, DTable);         \
         }                                                                   \
         return fn##_default(dst, dstSize, cSrc, cSrcSize, DTable);          \
@@ -86,9 +120,9 @@
 
 #define HUF_DGEN(fn)                                                        \
     static size_t fn(void* dst, size_t dstSize, void const* cSrc,           \
-                     size_t cSrcSize, HUF_DTable const* DTable, int bmi2)   \
+                     size_t cSrcSize, HUF_DTable const* DTable, int flags)  \
     {                                                                       \
-        (void)bmi2;                                                         \
+        (void)flags;                                                        \
         return fn##_body(dst, dstSize, cSrc, cSrcSize, DTable);             \
     }
 
@@ -107,13 +141,164 @@
     return dtd;
 }
 
+static size_t HUF_initFastDStream(BYTE const* ip) {
+    BYTE const lastByte = ip[7];
+    size_t const bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;
+    size_t const value = MEM_readLEST(ip) | 1;
+    assert(bitsConsumed <= 8);
+    assert(sizeof(size_t) == 8);
+    return value << bitsConsumed;
+}
+
+
+/**
+ * The input/output arguments to the Huffman fast decoding loop:
+ *
+ * ip [in/out] - The input pointers, must be updated to reflect what is consumed.
+ * op [in/out] - The output pointers, must be updated to reflect what is written.
+ * bits [in/out] - The bitstream containers, must be updated to reflect the current state.
+ * dt [in] - The decoding table.
+ * ilimit [in] - The input limit, stop when any input pointer is below ilimit.
+ * oend [in] - The end of the output stream. op[3] must not cross oend.
+ * iend [in] - The end of each input stream. ip[i] may cross iend[i],
+ *             as long as it is above ilimit, but that indicates corruption.
+ */
+typedef struct {
+    BYTE const* ip[4];
+    BYTE* op[4];
+    U64 bits[4];
+    void const* dt;
+    BYTE const* ilimit;
+    BYTE* oend;
+    BYTE const* iend[4];
+} HUF_DecompressFastArgs;
+
+typedef void (*HUF_DecompressFastLoopFn)(HUF_DecompressFastArgs*);
+
+/**
+ * Initializes args for the fast decoding loop.
+ * @returns 1 on success
+ *          0 if the fallback implementation should be used.
+ *          Or an error code on failure.
+ */
+static size_t HUF_DecompressFastArgs_init(HUF_DecompressFastArgs* args, void* dst, size_t dstSize, void const* src, size_t srcSize, const HUF_DTable* DTable)
+{
+    void const* dt = DTable + 1;
+    U32 const dtLog = HUF_getDTableDesc(DTable).tableLog;
+
+    const BYTE* const ilimit = (const BYTE*)src + 6 + 8;
+
+    BYTE* const oend = (BYTE*)dst + dstSize;
+
+    /* The fast decoding loop assumes 64-bit little-endian.
+     * This condition is false on x32.
+     */
+    if (!MEM_isLittleEndian() || MEM_32bits())
+        return 0;
+
+    /* strict minimum : jump table + 1 byte per stream */
+    if (srcSize < 10)
+        return ERROR(corruption_detected);
+
+    /* Must have at least 8 bytes per stream because we don't handle initializing smaller bit containers.
+     * If table log is not correct at this point, fallback to the old decoder.
+     * On small inputs we don't have enough data to trigger the fast loop, so use the old decoder.
+     */
+    if (dtLog != HUF_DECODER_FAST_TABLELOG)
+        return 0;
+
+    /* Read the jump table. */
+    {
+        const BYTE* const istart = (const BYTE*)src;
+        size_t const length1 = MEM_readLE16(istart);
+        size_t const length2 = MEM_readLE16(istart+2);
+        size_t const length3 = MEM_readLE16(istart+4);
+        size_t const length4 = srcSize - (length1 + length2 + length3 + 6);
+        args->iend[0] = istart + 6;  /* jumpTable */
+        args->iend[1] = args->iend[0] + length1;
+        args->iend[2] = args->iend[1] + length2;
+        args->iend[3] = args->iend[2] + length3;
+
+        /* HUF_initFastDStream() requires this, and this small of an input
+         * won't benefit from the ASM loop anyways.
+         * length1 must be >= 16 so that ip[0] >= ilimit before the loop
+         * starts.
+         */
+        if (length1 < 16 || length2 < 8 || length3 < 8 || length4 < 8)
+            return 0;
+        if (length4 > srcSize) return ERROR(corruption_detected);   /* overflow */
+    }
+    /* ip[] contains the position that is currently loaded into bits[]. */
+    args->ip[0] = args->iend[1] - sizeof(U64);
+    args->ip[1] = args->iend[2] - sizeof(U64);
+    args->ip[2] = args->iend[3] - sizeof(U64);
+    args->ip[3] = (BYTE const*)src + srcSize - sizeof(U64);
+
+    /* op[] contains the output pointers. */
+    args->op[0] = (BYTE*)dst;
+    args->op[1] = args->op[0] + (dstSize+3)/4;
+    args->op[2] = args->op[1] + (dstSize+3)/4;
+    args->op[3] = args->op[2] + (dstSize+3)/4;
+
+    /* No point to call the ASM loop for tiny outputs. */
+    if (args->op[3] >= oend)
+        return 0;
+
+    /* bits[] is the bit container.
+        * It is read from the MSB down to the LSB.
+        * It is shifted left as it is read, and zeros are
+        * shifted in. After the lowest valid bit a 1 is
+        * set, so that CountTrailingZeros(bits[]) can be used
+        * to count how many bits we've consumed.
+        */
+    args->bits[0] = HUF_initFastDStream(args->ip[0]);
+    args->bits[1] = HUF_initFastDStream(args->ip[1]);
+    args->bits[2] = HUF_initFastDStream(args->ip[2]);
+    args->bits[3] = HUF_initFastDStream(args->ip[3]);
+
+    /* If ip[] >= ilimit, it is guaranteed to be safe to
+        * reload bits[]. It may be beyond its section, but is
+        * guaranteed to be valid (>= istart).
+        */
+    args->ilimit = ilimit;
+
+    args->oend = oend;
+    args->dt = dt;
+
+    return 1;
+}
+
+static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressFastArgs const* args, int stream, BYTE* segmentEnd)
+{
+    /* Validate that we haven't overwritten. */
+    if (args->op[stream] > segmentEnd)
+        return ERROR(corruption_detected);
+    /* Validate that we haven't read beyond iend[].
+        * Note that ip[] may be < iend[] because the MSB is
+        * the next bit to read, and we may have consumed 100%
+        * of the stream, so down to iend[i] - 8 is valid.
+        */
+    if (args->ip[stream] < args->iend[stream] - 8)
+        return ERROR(corruption_detected);
+
+    /* Construct the BIT_DStream_t. */
+    assert(sizeof(size_t) == 8);
+    bit->bitContainer = MEM_readLEST(args->ip[stream]);
+    bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]);
+    bit->start = (const char*)args->iend[0];
+    bit->limitPtr = bit->start + sizeof(size_t);
+    bit->ptr = (const char*)args->ip[stream];
+
+    return 0;
+}
+
 
 #ifndef HUF_FORCE_DECOMPRESS_X2
 
 /*-***************************/
 /*  single-symbol decoding   */
 /*-***************************/
-typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1;   /* single-symbol decoding */
+typedef struct { BYTE nbBits; BYTE byte; } HUF_DEltX1;   /* single-symbol decoding */
 
 /**
  * Packs 4 HUF_DEltX1 structs into a U64. This is used to lay down 4 entries at
@@ -122,14 +307,45 @@
 static U64 HUF_DEltX1_set4(BYTE symbol, BYTE nbBits) {
     U64 D4;
     if (MEM_isLittleEndian()) {
-        D4 = symbol + (nbBits << 8);
+        D4 = (U64)((symbol << 8) + nbBits);
     } else {
-        D4 = (symbol << 8) + nbBits;
+        D4 = (U64)(symbol + (nbBits << 8));
     }
+    assert(D4 < (1U << 16));
     D4 *= 0x0001000100010001ULL;
     return D4;
 }
 
+/**
+ * Increase the tableLog to targetTableLog and rescales the stats.
+ * If tableLog > targetTableLog this is a no-op.
+ * @returns New tableLog
+ */
+static U32 HUF_rescaleStats(BYTE* huffWeight, U32* rankVal, U32 nbSymbols, U32 tableLog, U32 targetTableLog)
+{
+    if (tableLog > targetTableLog)
+        return tableLog;
+    if (tableLog < targetTableLog) {
+        U32 const scale = targetTableLog - tableLog;
+        U32 s;
+        /* Increase the weight for all non-zero probability symbols by scale. */
+        for (s = 0; s < nbSymbols; ++s) {
+            huffWeight[s] += (BYTE)((huffWeight[s] == 0) ? 0 : scale);
+        }
+        /* Update rankVal to reflect the new weights.
+         * All weights except 0 get moved to weight + scale.
+         * Weights [1, scale] are empty.
+         */
+        for (s = targetTableLog; s > scale; --s) {
+            rankVal[s] = rankVal[s - scale];
+        }
+        for (s = scale; s > 0; --s) {
+            rankVal[s] = 0;
+        }
+    }
+    return targetTableLog;
+}
+
 typedef struct {
         U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1];
         U32 rankStart[HUF_TABLELOG_ABSOLUTEMAX + 1];
@@ -138,13 +354,7 @@
         BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1];
 } HUF_ReadDTableX1_Workspace;
 
-
-size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize)
-{
-    return HUF_readDTableX1_wksp_bmi2(DTable, src, srcSize, workSpace, wkspSize, /* bmi2 */ 0);
-}
-
-size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2)
+size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags)
 {
     U32 tableLog = 0;
     U32 nbSymbols = 0;
@@ -159,11 +369,15 @@
     DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable));
     /* ZSTD_memset(huffWeight, 0, sizeof(huffWeight)); */   /* is not necessary, even though some analyzer complain ... */
 
-    iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), bmi2);
+    iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), flags);
     if (HUF_isError(iSize)) return iSize;
 
+
     /* Table header */
     {   DTableDesc dtd = HUF_getDTableDesc(DTable);
+        U32 const maxTableLog = dtd.maxTableLog + 1;
+        U32 const targetTableLog = MIN(maxTableLog, HUF_DECODER_FAST_TABLELOG);
+        tableLog = HUF_rescaleStats(wksp->huffWeight, wksp->rankVal, nbSymbols, tableLog, targetTableLog);
         if (tableLog > (U32)(dtd.maxTableLog+1)) return ERROR(tableLog_tooLarge);   /* DTable too small, Huffman tree cannot fit in */
         dtd.tableType = 0;
         dtd.tableLog = (BYTE)tableLog;
@@ -182,9 +396,8 @@
      * rankStart[0] is not filled because there are no entries in the table for
      * weight 0.
      */
-    {
-        int n;
-        int nextRankStart = 0;
+    {   int n;
+        U32 nextRankStart = 0;
         int const unroll = 4;
         int const nLimit = (int)nbSymbols - unroll + 1;
         for (n=0; n<(int)tableLog+1; n++) {
@@ -207,14 +420,13 @@
 
     /* fill DTable
      * We fill all entries of each weight in order.
-     * That way length is a constant for each iteration of the outter loop.
+     * That way length is a constant for each iteration of the outer loop.
      * We can switch based on the length to a different inner loop which is
      * optimized for that particular case.
      */
-    {
-        U32 w;
-        int symbol=wksp->rankVal[0];
-        int rankStart=0;
+    {   U32 w;
+        int symbol = wksp->rankVal[0];
+        int rankStart = 0;
         for (w=1; w<tableLog+1; ++w) {
             int const symbolCount = wksp->rankVal[w];
             int const length = (1 << w) >> 1;
@@ -304,11 +516,15 @@
     BYTE* const pStart = p;
 
     /* up to 4 symbols at a time */
-    while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) {
-        HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
-        HUF_DECODE_SYMBOLX1_1(p, bitDPtr);
-        HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
-        HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
+    if ((pEnd - p) > 3) {
+        while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-3)) {
+            HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
+            HUF_DECODE_SYMBOLX1_1(p, bitDPtr);
+            HUF_DECODE_SYMBOLX1_2(p, bitDPtr);
+            HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
+        }
+    } else {
+        BIT_reloadDStream(bitDPtr);
     }
 
     /* [0-3] symbols remaining */
@@ -320,7 +536,7 @@
     while (p < pEnd)
         HUF_DECODE_SYMBOLX1_0(p, bitDPtr);
 
-    return pEnd-pStart;
+    return (size_t)(pEnd-pStart);
 }
 
 FORCE_INLINE_TEMPLATE size_t
@@ -346,6 +562,10 @@
     return dstSize;
 }
 
+/* HUF_decompress4X1_usingDTable_internal_body():
+ * Conditions :
+ * @dstSize >= 6
+ */
 FORCE_INLINE_TEMPLATE size_t
 HUF_decompress4X1_usingDTable_internal_body(
           void* dst,  size_t dstSize,
@@ -388,33 +608,37 @@
         U32 endSignal = 1;
 
         if (length4 > cSrcSize) return ERROR(corruption_detected);   /* overflow */
+        if (opStart4 > oend) return ERROR(corruption_detected);      /* overflow */
+        if (dstSize < 6) return ERROR(corruption_detected);         /* stream 4-split doesn't work */
         CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
         CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
         CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
         CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
 
         /* up to 16 symbols per loop (4 symbols per stream) in 64-bit mode */
-        for ( ; (endSignal) & (op4 < olimit) ; ) {
-            HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX1_1(op1, &bitD1);
-            HUF_DECODE_SYMBOLX1_1(op2, &bitD2);
-            HUF_DECODE_SYMBOLX1_1(op3, &bitD3);
-            HUF_DECODE_SYMBOLX1_1(op4, &bitD4);
-            HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX1_0(op1, &bitD1);
-            HUF_DECODE_SYMBOLX1_0(op2, &bitD2);
-            HUF_DECODE_SYMBOLX1_0(op3, &bitD3);
-            HUF_DECODE_SYMBOLX1_0(op4, &bitD4);
-            endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;
-            endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished;
-            endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;
-            endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished;
+        if ((size_t)(oend - op4) >= sizeof(size_t)) {
+            for ( ; (endSignal) & (op4 < olimit) ; ) {
+                HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX1_1(op1, &bitD1);
+                HUF_DECODE_SYMBOLX1_1(op2, &bitD2);
+                HUF_DECODE_SYMBOLX1_1(op3, &bitD3);
+                HUF_DECODE_SYMBOLX1_1(op4, &bitD4);
+                HUF_DECODE_SYMBOLX1_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX1_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX1_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX1_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX1_0(op1, &bitD1);
+                HUF_DECODE_SYMBOLX1_0(op2, &bitD2);
+                HUF_DECODE_SYMBOLX1_0(op3, &bitD3);
+                HUF_DECODE_SYMBOLX1_0(op4, &bitD4);
+                endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;
+                endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished;
+                endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;
+                endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished;
+            }
         }
 
         /* check corruption */
@@ -440,74 +664,232 @@
     }
 }
 
+#if HUF_NEED_BMI2_FUNCTION
+static BMI2_TARGET_ATTRIBUTE
+size_t HUF_decompress4X1_usingDTable_internal_bmi2(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable) {
+    return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable);
+}
+#endif
 
-typedef size_t (*HUF_decompress_usingDTable_t)(void *dst, size_t dstSize,
-                                               const void *cSrc,
-                                               size_t cSrcSize,
-                                               const HUF_DTable *DTable);
+static
+size_t HUF_decompress4X1_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable) {
+    return HUF_decompress4X1_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable);
+}
+
+#if ZSTD_ENABLE_ASM_X86_64_BMI2
+
+HUF_ASM_DECL void HUF_decompress4X1_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN;
+
+#endif
+
+static HUF_FAST_BMI2_ATTRS
+void HUF_decompress4X1_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args)
+{
+    U64 bits[4];
+    BYTE const* ip[4];
+    BYTE* op[4];
+    U16 const* const dtable = (U16 const*)args->dt;
+    BYTE* const oend = args->oend;
+    BYTE const* const ilimit = args->ilimit;
+
+    /* Copy the arguments to local variables */
+    ZSTD_memcpy(&bits, &args->bits, sizeof(bits));
+    ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip));
+    ZSTD_memcpy(&op, &args->op, sizeof(op));
+
+    assert(MEM_isLittleEndian());
+    assert(!MEM_32bits());
+
+    for (;;) {
+        BYTE* olimit;
+        int stream;
+        int symbol;
+
+        /* Assert loop preconditions */
+#ifndef NDEBUG
+        for (stream = 0; stream < 4; ++stream) {
+            assert(op[stream] <= (stream == 3 ? oend : op[stream + 1]));
+            assert(ip[stream] >= ilimit);
+        }
+#endif
+        /* Compute olimit */
+        {
+            /* Each iteration produces 5 output symbols per stream */
+            size_t const oiters = (size_t)(oend - op[3]) / 5;
+            /* Each iteration consumes up to 11 bits * 5 = 55 bits < 7 bytes
+             * per stream.
+             */
+            size_t const iiters = (size_t)(ip[0] - ilimit) / 7;
+            /* We can safely run iters iterations before running bounds checks */
+            size_t const iters = MIN(oiters, iiters);
+            size_t const symbols = iters * 5;
+
+            /* We can simply check that op[3] < olimit, instead of checking all
+             * of our bounds, since we can't hit the other bounds until we've run
+             * iters iterations, which only happens when op[3] == olimit.
+             */
+            olimit = op[3] + symbols;
+
+            /* Exit fast decoding loop once we get close to the end. */
+            if (op[3] + 20 > olimit)
+                break;
+
+            /* Exit the decoding loop if any input pointer has crossed the
+             * previous one. This indicates corruption, and a precondition
+             * to our loop is that ip[i] >= ip[0].
+             */
+            for (stream = 1; stream < 4; ++stream) {
+                if (ip[stream] < ip[stream - 1])
+                    goto _out;
+            }
+        }
+
+#ifndef NDEBUG
+        for (stream = 1; stream < 4; ++stream) {
+            assert(ip[stream] >= ip[stream - 1]);
+        }
+#endif
+
+        do {
+            /* Decode 5 symbols in each of the 4 streams */
+            for (symbol = 0; symbol < 5; ++symbol) {
+                for (stream = 0; stream < 4; ++stream) {
+                    int const index = (int)(bits[stream] >> 53);
+                    int const entry = (int)dtable[index];
+                    bits[stream] <<= (entry & 63);
+                    op[stream][symbol] = (BYTE)((entry >> 8) & 0xFF);
+                }
+            }
+            /* Reload the bitstreams */
+            for (stream = 0; stream < 4; ++stream) {
+                int const ctz = ZSTD_countTrailingZeros64(bits[stream]);
+                int const nbBits = ctz & 7;
+                int const nbBytes = ctz >> 3;
+                op[stream] += 5;
+                ip[stream] -= nbBytes;
+                bits[stream] = MEM_read64(ip[stream]) | 1;
+                bits[stream] <<= nbBits;
+            }
+        } while (op[3] < olimit);
+    }
+
+_out:
+
+    /* Save the final values of each of the state variables back to args. */
+    ZSTD_memcpy(&args->bits, &bits, sizeof(bits));
+    ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip));
+    ZSTD_memcpy(&args->op, &op, sizeof(op));
+}
+
+/**
+ * @returns @p dstSize on success (>= 6)
+ *          0 if the fallback implementation should be used
+ *          An error if an error occurred
+ */
+static HUF_FAST_BMI2_ATTRS
+size_t
+HUF_decompress4X1_usingDTable_internal_fast(
+          void* dst,  size_t dstSize,
+    const void* cSrc, size_t cSrcSize,
+    const HUF_DTable* DTable,
+    HUF_DecompressFastLoopFn loopFn)
+{
+    void const* dt = DTable + 1;
+    const BYTE* const iend = (const BYTE*)cSrc + 6;
+    BYTE* const oend = (BYTE*)dst + dstSize;
+    HUF_DecompressFastArgs args;
+    {   size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable);
+        FORWARD_IF_ERROR(ret, "Failed to init fast loop args");
+        if (ret == 0)
+            return 0;
+    }
+
+    assert(args.ip[0] >= args.ilimit);
+    loopFn(&args);
+
+    /* Our loop guarantees that ip[] >= ilimit and that we haven't
+    * overwritten any op[].
+    */
+    assert(args.ip[0] >= iend);
+    assert(args.ip[1] >= iend);
+    assert(args.ip[2] >= iend);
+    assert(args.ip[3] >= iend);
+    assert(args.op[3] <= oend);
+    (void)iend;
+
+    /* finish bit streams one by one. */
+    {   size_t const segmentSize = (dstSize+3) / 4;
+        BYTE* segmentEnd = (BYTE*)dst;
+        int i;
+        for (i = 0; i < 4; ++i) {
+            BIT_DStream_t bit;
+            if (segmentSize <= (size_t)(oend - segmentEnd))
+                segmentEnd += segmentSize;
+            else
+                segmentEnd = oend;
+            FORWARD_IF_ERROR(HUF_initRemainingDStream(&bit, &args, i, segmentEnd), "corruption");
+            /* Decompress and validate that we've produced exactly the expected length. */
+            args.op[i] += HUF_decodeStreamX1(args.op[i], &bit, segmentEnd, (HUF_DEltX1 const*)dt, HUF_DECODER_FAST_TABLELOG);
+            if (args.op[i] != segmentEnd) return ERROR(corruption_detected);
+        }
+    }
+
+    /* decoded size */
+    assert(dstSize != 0);
+    return dstSize;
+}
 
 HUF_DGEN(HUF_decompress1X1_usingDTable_internal)
-HUF_DGEN(HUF_decompress4X1_usingDTable_internal)
 
-
-
-size_t HUF_decompress1X1_usingDTable(
-          void* dst,  size_t dstSize,
-    const void* cSrc, size_t cSrcSize,
-    const HUF_DTable* DTable)
+static size_t HUF_decompress4X1_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable, int flags)
 {
-    DTableDesc dtd = HUF_getDTableDesc(DTable);
-    if (dtd.tableType != 0) return ERROR(GENERIC);
-    return HUF_decompress1X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+    HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X1_usingDTable_internal_default;
+    HUF_DecompressFastLoopFn loopFn = HUF_decompress4X1_usingDTable_internal_fast_c_loop;
+
+#if DYNAMIC_BMI2
+    if (flags & HUF_flags_bmi2) {
+        fallbackFn = HUF_decompress4X1_usingDTable_internal_bmi2;
+# if ZSTD_ENABLE_ASM_X86_64_BMI2
+        if (!(flags & HUF_flags_disableAsm)) {
+            loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop;
+        }
+# endif
+    } else {
+        return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable);
+    }
+#endif
+
+#if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__)
+    if (!(flags & HUF_flags_disableAsm)) {
+        loopFn = HUF_decompress4X1_usingDTable_internal_fast_asm_loop;
+    }
+#endif
+
+    if (!(flags & HUF_flags_disableFast)) {
+        size_t const ret = HUF_decompress4X1_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn);
+        if (ret != 0)
+            return ret;
+    }
+    return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable);
 }
 
-size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
+static size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
                                    const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize)
+                                   void* workSpace, size_t wkspSize, int flags)
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
-    size_t const hSize = HUF_readDTableX1_wksp(DCtx, cSrc, cSrcSize, workSpace, wkspSize);
+    size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
 
-    return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
+    return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags);
 }
 
-
-size_t HUF_decompress4X1_usingDTable(
-          void* dst,  size_t dstSize,
-    const void* cSrc, size_t cSrcSize,
-    const HUF_DTable* DTable)
-{
-    DTableDesc dtd = HUF_getDTableDesc(DTable);
-    if (dtd.tableType != 0) return ERROR(GENERIC);
-    return HUF_decompress4X1_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-}
-
-static size_t HUF_decompress4X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
-                                   const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize, int bmi2)
-{
-    const BYTE* ip = (const BYTE*) cSrc;
-
-    size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
-    if (HUF_isError(hSize)) return hSize;
-    if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
-    ip += hSize; cSrcSize -= hSize;
-
-    return HUF_decompress4X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
-}
-
-size_t HUF_decompress4X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
-                                   const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize)
-{
-    return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, 0);
-}
-
-
 #endif /* HUF_FORCE_DECOMPRESS_X2 */
 
 
@@ -518,106 +900,226 @@
 /* *************************/
 
 typedef struct { U16 sequence; BYTE nbBits; BYTE length; } HUF_DEltX2;  /* double-symbols decoding */
-typedef struct { BYTE symbol; BYTE weight; } sortedSymbol_t;
+typedef struct { BYTE symbol; } sortedSymbol_t;
 typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1];
 typedef rankValCol_t rankVal_t[HUF_TABLELOG_MAX];
 
+/**
+ * Constructs a HUF_DEltX2 in a U32.
+ */
+static U32 HUF_buildDEltX2U32(U32 symbol, U32 nbBits, U32 baseSeq, int level)
+{
+    U32 seq;
+    DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, sequence) == 0);
+    DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, nbBits) == 2);
+    DEBUG_STATIC_ASSERT(offsetof(HUF_DEltX2, length) == 3);
+    DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(U32));
+    if (MEM_isLittleEndian()) {
+        seq = level == 1 ? symbol : (baseSeq + (symbol << 8));
+        return seq + (nbBits << 16) + ((U32)level << 24);
+    } else {
+        seq = level == 1 ? (symbol << 8) : ((baseSeq << 8) + symbol);
+        return (seq << 16) + (nbBits << 8) + (U32)level;
+    }
+}
+
+/**
+ * Constructs a HUF_DEltX2.
+ */
+static HUF_DEltX2 HUF_buildDEltX2(U32 symbol, U32 nbBits, U32 baseSeq, int level)
+{
+    HUF_DEltX2 DElt;
+    U32 const val = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level);
+    DEBUG_STATIC_ASSERT(sizeof(DElt) == sizeof(val));
+    ZSTD_memcpy(&DElt, &val, sizeof(val));
+    return DElt;
+}
+
+/**
+ * Constructs 2 HUF_DEltX2s and packs them into a U64.
+ */
+static U64 HUF_buildDEltX2U64(U32 symbol, U32 nbBits, U16 baseSeq, int level)
+{
+    U32 DElt = HUF_buildDEltX2U32(symbol, nbBits, baseSeq, level);
+    return (U64)DElt + ((U64)DElt << 32);
+}
+
+/**
+ * Fills the DTable rank with all the symbols from [begin, end) that are each
+ * nbBits long.
+ *
+ * @param DTableRank The start of the rank in the DTable.
+ * @param begin The first symbol to fill (inclusive).
+ * @param end The last symbol to fill (exclusive).
+ * @param nbBits Each symbol is nbBits long.
+ * @param tableLog The table log.
+ * @param baseSeq If level == 1 { 0 } else { the first level symbol }
+ * @param level The level in the table. Must be 1 or 2.
+ */
+static void HUF_fillDTableX2ForWeight(
+    HUF_DEltX2* DTableRank,
+    sortedSymbol_t const* begin, sortedSymbol_t const* end,
+    U32 nbBits, U32 tableLog,
+    U16 baseSeq, int const level)
+{
+    U32 const length = 1U << ((tableLog - nbBits) & 0x1F /* quiet static-analyzer */);
+    const sortedSymbol_t* ptr;
+    assert(level >= 1 && level <= 2);
+    switch (length) {
+    case 1:
+        for (ptr = begin; ptr != end; ++ptr) {
+            HUF_DEltX2 const DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level);
+            *DTableRank++ = DElt;
+        }
+        break;
+    case 2:
+        for (ptr = begin; ptr != end; ++ptr) {
+            HUF_DEltX2 const DElt = HUF_buildDEltX2(ptr->symbol, nbBits, baseSeq, level);
+            DTableRank[0] = DElt;
+            DTableRank[1] = DElt;
+            DTableRank += 2;
+        }
+        break;
+    case 4:
+        for (ptr = begin; ptr != end; ++ptr) {
+            U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level);
+            ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2));
+            ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2));
+            DTableRank += 4;
+        }
+        break;
+    case 8:
+        for (ptr = begin; ptr != end; ++ptr) {
+            U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level);
+            ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2));
+            ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2));
+            ZSTD_memcpy(DTableRank + 4, &DEltX2, sizeof(DEltX2));
+            ZSTD_memcpy(DTableRank + 6, &DEltX2, sizeof(DEltX2));
+            DTableRank += 8;
+        }
+        break;
+    default:
+        for (ptr = begin; ptr != end; ++ptr) {
+            U64 const DEltX2 = HUF_buildDEltX2U64(ptr->symbol, nbBits, baseSeq, level);
+            HUF_DEltX2* const DTableRankEnd = DTableRank + length;
+            for (; DTableRank != DTableRankEnd; DTableRank += 8) {
+                ZSTD_memcpy(DTableRank + 0, &DEltX2, sizeof(DEltX2));
+                ZSTD_memcpy(DTableRank + 2, &DEltX2, sizeof(DEltX2));
+                ZSTD_memcpy(DTableRank + 4, &DEltX2, sizeof(DEltX2));
+                ZSTD_memcpy(DTableRank + 6, &DEltX2, sizeof(DEltX2));
+            }
+        }
+        break;
+    }
+}
 
 /* HUF_fillDTableX2Level2() :
  * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */
-static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed,
-                           const U32* rankValOrigin, const int minWeight,
-                           const sortedSymbol_t* sortedSymbols, const U32 sortedListSize,
-                           U32 nbBitsBaseline, U16 baseSeq, U32* wksp, size_t wkspSize)
+static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 targetLog, const U32 consumedBits,
+                           const U32* rankVal, const int minWeight, const int maxWeight1,
+                           const sortedSymbol_t* sortedSymbols, U32 const* rankStart,
+                           U32 nbBitsBaseline, U16 baseSeq)
 {
-    HUF_DEltX2 DElt;
-    U32* rankVal = wksp;
-
-    assert(wkspSize >= HUF_TABLELOG_MAX + 1);
-    (void)wkspSize;
-    /* get pre-calculated rankVal */
-    ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1));
-
-    /* fill skipped values */
+    /* Fill skipped values (all positions up to rankVal[minWeight]).
+     * These are positions only get a single symbol because the combined weight
+     * is too large.
+     */
     if (minWeight>1) {
-        U32 i, skipSize = rankVal[minWeight];
-        MEM_writeLE16(&(DElt.sequence), baseSeq);
-        DElt.nbBits   = (BYTE)(consumed);
-        DElt.length   = 1;
-        for (i = 0; i < skipSize; i++)
-            DTable[i] = DElt;
+        U32 const length = 1U << ((targetLog - consumedBits) & 0x1F /* quiet static-analyzer */);
+        U64 const DEltX2 = HUF_buildDEltX2U64(baseSeq, consumedBits, /* baseSeq */ 0, /* level */ 1);
+        int const skipSize = rankVal[minWeight];
+        assert(length > 1);
+        assert((U32)skipSize < length);
+        switch (length) {
+        case 2:
+            assert(skipSize == 1);
+            ZSTD_memcpy(DTable, &DEltX2, sizeof(DEltX2));
+            break;
+        case 4:
+            assert(skipSize <= 4);
+            ZSTD_memcpy(DTable + 0, &DEltX2, sizeof(DEltX2));
+            ZSTD_memcpy(DTable + 2, &DEltX2, sizeof(DEltX2));
+            break;
+        default:
+            {
+                int i;
+                for (i = 0; i < skipSize; i += 8) {
+                    ZSTD_memcpy(DTable + i + 0, &DEltX2, sizeof(DEltX2));
+                    ZSTD_memcpy(DTable + i + 2, &DEltX2, sizeof(DEltX2));
+                    ZSTD_memcpy(DTable + i + 4, &DEltX2, sizeof(DEltX2));
+                    ZSTD_memcpy(DTable + i + 6, &DEltX2, sizeof(DEltX2));
+                }
+            }
+        }
     }
 
-    /* fill DTable */
-    {   U32 s; for (s=0; s<sortedListSize; s++) {   /* note : sortedSymbols already skipped */
-            const U32 symbol = sortedSymbols[s].symbol;
-            const U32 weight = sortedSymbols[s].weight;
-            const U32 nbBits = nbBitsBaseline - weight;
-            const U32 length = 1 << (sizeLog-nbBits);
-            const U32 start = rankVal[weight];
-            U32 i = start;
-            const U32 end = start + length;
-
-            MEM_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8)));
-            DElt.nbBits = (BYTE)(nbBits + consumed);
-            DElt.length = 2;
-            do { DTable[i++] = DElt; } while (i<end);   /* since length >= 1 */
-
-            rankVal[weight] += length;
-    }   }
+    /* Fill each of the second level symbols by weight. */
+    {
+        int w;
+        for (w = minWeight; w < maxWeight1; ++w) {
+            int const begin = rankStart[w];
+            int const end = rankStart[w+1];
+            U32 const nbBits = nbBitsBaseline - w;
+            U32 const totalBits = nbBits + consumedBits;
+            HUF_fillDTableX2ForWeight(
+                DTable + rankVal[w],
+                sortedSymbols + begin, sortedSymbols + end,
+                totalBits, targetLog,
+                baseSeq, /* level */ 2);
+        }
+    }
 }
 
-
 static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog,
-                           const sortedSymbol_t* sortedList, const U32 sortedListSize,
-                           const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight,
-                           const U32 nbBitsBaseline, U32* wksp, size_t wkspSize)
+                           const sortedSymbol_t* sortedList,
+                           const U32* rankStart, rankValCol_t* rankValOrigin, const U32 maxWeight,
+                           const U32 nbBitsBaseline)
 {
-    U32* rankVal = wksp;
+    U32* const rankVal = rankValOrigin[0];
     const int scaleLog = nbBitsBaseline - targetLog;   /* note : targetLog >= srcLog, hence scaleLog <= 1 */
     const U32 minBits  = nbBitsBaseline - maxWeight;
-    U32 s;
+    int w;
+    int const wEnd = (int)maxWeight + 1;
 
-    assert(wkspSize >= HUF_TABLELOG_MAX + 1);
-    wksp += HUF_TABLELOG_MAX + 1;
-    wkspSize -= HUF_TABLELOG_MAX + 1;
+    /* Fill DTable in order of weight. */
+    for (w = 1; w < wEnd; ++w) {
+        int const begin = (int)rankStart[w];
+        int const end = (int)rankStart[w+1];
+        U32 const nbBits = nbBitsBaseline - w;
 
-    ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1));
-
-    /* fill DTable */
-    for (s=0; s<sortedListSize; s++) {
-        const U16 symbol = sortedList[s].symbol;
-        const U32 weight = sortedList[s].weight;
-        const U32 nbBits = nbBitsBaseline - weight;
-        const U32 start = rankVal[weight];
-        const U32 length = 1 << (targetLog-nbBits);
-
-        if (targetLog-nbBits >= minBits) {   /* enough room for a second symbol */
-            U32 sortedRank;
+        if (targetLog-nbBits >= minBits) {
+            /* Enough room for a second symbol. */
+            int start = rankVal[w];
+            U32 const length = 1U << ((targetLog - nbBits) & 0x1F /* quiet static-analyzer */);
             int minWeight = nbBits + scaleLog;
+            int s;
             if (minWeight < 1) minWeight = 1;
-            sortedRank = rankStart[minWeight];
-            HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits,
-                           rankValOrigin[nbBits], minWeight,
-                           sortedList+sortedRank, sortedListSize-sortedRank,
-                           nbBitsBaseline, symbol, wksp, wkspSize);
+            /* Fill the DTable for every symbol of weight w.
+             * These symbols get at least 1 second symbol.
+             */
+            for (s = begin; s != end; ++s) {
+                HUF_fillDTableX2Level2(
+                    DTable + start, targetLog, nbBits,
+                    rankValOrigin[nbBits], minWeight, wEnd,
+                    sortedList, rankStart,
+                    nbBitsBaseline, sortedList[s].symbol);
+                start += length;
+            }
         } else {
-            HUF_DEltX2 DElt;
-            MEM_writeLE16(&(DElt.sequence), symbol);
-            DElt.nbBits = (BYTE)(nbBits);
-            DElt.length = 1;
-            {   U32 const end = start + length;
-                U32 u;
-                for (u = start; u < end; u++) DTable[u] = DElt;
-        }   }
-        rankVal[weight] += length;
+            /* Only a single symbol. */
+            HUF_fillDTableX2ForWeight(
+                DTable + rankVal[w],
+                sortedList + begin, sortedList + end,
+                nbBits, targetLog,
+                /* baseSeq */ 0, /* level */ 1);
+        }
     }
 }
 
 typedef struct {
     rankValCol_t rankVal[HUF_TABLELOG_MAX];
     U32 rankStats[HUF_TABLELOG_MAX + 1];
-    U32 rankStart0[HUF_TABLELOG_MAX + 2];
+    U32 rankStart0[HUF_TABLELOG_MAX + 3];
     sortedSymbol_t sortedSymbol[HUF_SYMBOLVALUE_MAX + 1];
     BYTE weightList[HUF_SYMBOLVALUE_MAX + 1];
     U32 calleeWksp[HUF_READ_STATS_WORKSPACE_SIZE_U32];
@@ -625,11 +1127,11 @@
 
 size_t HUF_readDTableX2_wksp(HUF_DTable* DTable,
                        const void* src, size_t srcSize,
-                             void* workSpace, size_t wkspSize)
+                             void* workSpace, size_t wkspSize, int flags)
 {
-    U32 tableLog, maxW, sizeOfSort, nbSymbols;
+    U32 tableLog, maxW, nbSymbols;
     DTableDesc dtd = HUF_getDTableDesc(DTable);
-    U32 const maxTableLog = dtd.maxTableLog;
+    U32 maxTableLog = dtd.maxTableLog;
     size_t iSize;
     void* dtPtr = DTable+1;   /* force compiler to avoid strict-aliasing */
     HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr;
@@ -647,11 +1149,12 @@
     if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
     /* ZSTD_memset(weightList, 0, sizeof(weightList)); */  /* is not necessary, even though some analyzer complain ... */
 
-    iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), /* bmi2 */ 0);
+    iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), flags);
     if (HUF_isError(iSize)) return iSize;
 
     /* check result */
     if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge);   /* DTable can't fit code depth */
+    if (tableLog <= HUF_DECODER_FAST_TABLELOG && maxTableLog > HUF_DECODER_FAST_TABLELOG) maxTableLog = HUF_DECODER_FAST_TABLELOG;
 
     /* find maxWeight */
     for (maxW = tableLog; wksp->rankStats[maxW]==0; maxW--) {}  /* necessarily finds a solution before 0 */
@@ -664,7 +1167,7 @@
             rankStart[w] = curr;
         }
         rankStart[0] = nextRankStart;   /* put all 0w symbols at the end of sorted list*/
-        sizeOfSort = nextRankStart;
+        rankStart[maxW+1] = nextRankStart;
     }
 
     /* sort symbols by weight */
@@ -673,7 +1176,6 @@
             U32 const w = wksp->weightList[s];
             U32 const r = rankStart[w]++;
             wksp->sortedSymbol[r].symbol = (BYTE)s;
-            wksp->sortedSymbol[r].weight = (BYTE)w;
         }
         rankStart[0] = 0;   /* forget 0w symbols; this is beginning of weight(1) */
     }
@@ -698,10 +1200,9 @@
     }   }   }   }
 
     HUF_fillDTableX2(dt, maxTableLog,
-                   wksp->sortedSymbol, sizeOfSort,
+                   wksp->sortedSymbol,
                    wksp->rankStart0, wksp->rankVal, maxW,
-                   tableLog+1,
-                   wksp->calleeWksp, sizeof(wksp->calleeWksp) / sizeof(U32));
+                   tableLog+1);
 
     dtd.tableLog = (BYTE)maxTableLog;
     dtd.tableType = 1;
@@ -714,7 +1215,7 @@
 HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
 {
     size_t const val = BIT_lookBitsFast(DStream, dtLog);   /* note : dtLog >= 1 */
-    ZSTD_memcpy(op, dt+val, 2);
+    ZSTD_memcpy(op, &dt[val].sequence, 2);
     BIT_skipBits(DStream, dt[val].nbBits);
     return dt[val].length;
 }
@@ -723,15 +1224,17 @@
 HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
 {
     size_t const val = BIT_lookBitsFast(DStream, dtLog);   /* note : dtLog >= 1 */
-    ZSTD_memcpy(op, dt+val, 1);
-    if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits);
-    else {
+    ZSTD_memcpy(op, &dt[val].sequence, 1);
+    if (dt[val].length==1) {
+        BIT_skipBits(DStream, dt[val].nbBits);
+    } else {
         if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) {
             BIT_skipBits(DStream, dt[val].nbBits);
             if (DStream->bitsConsumed > (sizeof(DStream->bitContainer)*8))
                 /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */
                 DStream->bitsConsumed = (sizeof(DStream->bitContainer)*8);
-    }   }
+        }
+    }
     return 1;
 }
 
@@ -753,19 +1256,37 @@
     BYTE* const pStart = p;
 
     /* up to 8 symbols at a time */
-    while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) {
-        HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
-        HUF_DECODE_SYMBOLX2_1(p, bitDPtr);
-        HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
-        HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+    if ((size_t)(pEnd - p) >= sizeof(bitDPtr->bitContainer)) {
+        if (dtLog <= 11 && MEM_64bits()) {
+            /* up to 10 symbols at a time */
+            while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-9)) {
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+            }
+        } else {
+            /* up to 8 symbols at a time */
+            while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd-(sizeof(bitDPtr->bitContainer)-1))) {
+                HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_1(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_2(p, bitDPtr);
+                HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+            }
+        }
+    } else {
+        BIT_reloadDStream(bitDPtr);
     }
 
     /* closer to end : up to 2 symbols at a time */
-    while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2))
-        HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
+    if ((size_t)(pEnd - p) >= 2) {
+        while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd-2))
+            HUF_DECODE_SYMBOLX2_0(p, bitDPtr);
 
-    while (p <= pEnd-2)
-        HUF_DECODE_SYMBOLX2_0(p, bitDPtr);   /* no need to reload : reached the end of DStream */
+        while (p <= pEnd-2)
+            HUF_DECODE_SYMBOLX2_0(p, bitDPtr);   /* no need to reload : reached the end of DStream */
+    }
 
     if (p < pEnd)
         p += HUF_decodeLastSymbolX2(p, bitDPtr, dt, dtLog);
@@ -800,6 +1321,10 @@
     return dstSize;
 }
 
+/* HUF_decompress4X2_usingDTable_internal_body():
+ * Conditions:
+ * @dstSize >= 6
+ */
 FORCE_INLINE_TEMPLATE size_t
 HUF_decompress4X2_usingDTable_internal_body(
           void* dst,  size_t dstSize,
@@ -840,58 +1365,62 @@
         DTableDesc const dtd = HUF_getDTableDesc(DTable);
         U32 const dtLog = dtd.tableLog;
 
-        if (length4 > cSrcSize) return ERROR(corruption_detected);   /* overflow */
+        if (length4 > cSrcSize) return ERROR(corruption_detected);  /* overflow */
+        if (opStart4 > oend) return ERROR(corruption_detected);     /* overflow */
+        if (dstSize < 6) return ERROR(corruption_detected);         /* stream 4-split doesn't work */
         CHECK_F( BIT_initDStream(&bitD1, istart1, length1) );
         CHECK_F( BIT_initDStream(&bitD2, istart2, length2) );
         CHECK_F( BIT_initDStream(&bitD3, istart3, length3) );
         CHECK_F( BIT_initDStream(&bitD4, istart4, length4) );
 
         /* 16-32 symbols per loop (4-8 symbols per stream) */
-        for ( ; (endSignal) & (op4 < olimit); ) {
+        if ((size_t)(oend - op4) >= sizeof(size_t)) {
+            for ( ; (endSignal) & (op4 < olimit); ) {
 #if defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
-            HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
-            endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;
-            endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished;
-            HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
-            endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;
-            endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished;
+                HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
+                endSignal &= BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished;
+                endSignal &= BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished;
+                HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
+                endSignal &= BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished;
+                endSignal &= BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished;
 #else
-            HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
-            HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
-            HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
-            HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
-            HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
-            endSignal = (U32)LIKELY(
-                        (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished)
-                      & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished)
-                      & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished)
-                      & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished));
+                HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_1(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_1(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_1(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_1(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_2(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_2(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_2(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_2(op4, &bitD4);
+                HUF_DECODE_SYMBOLX2_0(op1, &bitD1);
+                HUF_DECODE_SYMBOLX2_0(op2, &bitD2);
+                HUF_DECODE_SYMBOLX2_0(op3, &bitD3);
+                HUF_DECODE_SYMBOLX2_0(op4, &bitD4);
+                endSignal = (U32)LIKELY((U32)
+                            (BIT_reloadDStreamFast(&bitD1) == BIT_DStream_unfinished)
+                        & (BIT_reloadDStreamFast(&bitD2) == BIT_DStream_unfinished)
+                        & (BIT_reloadDStreamFast(&bitD3) == BIT_DStream_unfinished)
+                        & (BIT_reloadDStreamFast(&bitD4) == BIT_DStream_unfinished));
 #endif
+            }
         }
 
         /* check corruption */
@@ -915,68 +1444,281 @@
     }
 }
 
-HUF_DGEN(HUF_decompress1X2_usingDTable_internal)
-HUF_DGEN(HUF_decompress4X2_usingDTable_internal)
+#if HUF_NEED_BMI2_FUNCTION
+static BMI2_TARGET_ATTRIBUTE
+size_t HUF_decompress4X2_usingDTable_internal_bmi2(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable) {
+    return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable);
+}
+#endif
 
-size_t HUF_decompress1X2_usingDTable(
+static
+size_t HUF_decompress4X2_usingDTable_internal_default(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable) {
+    return HUF_decompress4X2_usingDTable_internal_body(dst, dstSize, cSrc, cSrcSize, DTable);
+}
+
+#if ZSTD_ENABLE_ASM_X86_64_BMI2
+
+HUF_ASM_DECL void HUF_decompress4X2_usingDTable_internal_fast_asm_loop(HUF_DecompressFastArgs* args) ZSTDLIB_HIDDEN;
+
+#endif
+
+static HUF_FAST_BMI2_ATTRS
+void HUF_decompress4X2_usingDTable_internal_fast_c_loop(HUF_DecompressFastArgs* args)
+{
+    U64 bits[4];
+    BYTE const* ip[4];
+    BYTE* op[4];
+    BYTE* oend[4];
+    HUF_DEltX2 const* const dtable = (HUF_DEltX2 const*)args->dt;
+    BYTE const* const ilimit = args->ilimit;
+
+    /* Copy the arguments to local registers. */
+    ZSTD_memcpy(&bits, &args->bits, sizeof(bits));
+    ZSTD_memcpy((void*)(&ip), &args->ip, sizeof(ip));
+    ZSTD_memcpy(&op, &args->op, sizeof(op));
+
+    oend[0] = op[1];
+    oend[1] = op[2];
+    oend[2] = op[3];
+    oend[3] = args->oend;
+
+    assert(MEM_isLittleEndian());
+    assert(!MEM_32bits());
+
+    for (;;) {
+        BYTE* olimit;
+        int stream;
+        int symbol;
+
+        /* Assert loop preconditions */
+#ifndef NDEBUG
+        for (stream = 0; stream < 4; ++stream) {
+            assert(op[stream] <= oend[stream]);
+            assert(ip[stream] >= ilimit);
+        }
+#endif
+        /* Compute olimit */
+        {
+            /* Each loop does 5 table lookups for each of the 4 streams.
+             * Each table lookup consumes up to 11 bits of input, and produces
+             * up to 2 bytes of output.
+             */
+            /* We can consume up to 7 bytes of input per iteration per stream.
+             * We also know that each input pointer is >= ip[0]. So we can run
+             * iters loops before running out of input.
+             */
+            size_t iters = (size_t)(ip[0] - ilimit) / 7;
+            /* Each iteration can produce up to 10 bytes of output per stream.
+             * Each output stream my advance at different rates. So take the
+             * minimum number of safe iterations among all the output streams.
+             */
+            for (stream = 0; stream < 4; ++stream) {
+                size_t const oiters = (size_t)(oend[stream] - op[stream]) / 10;
+                iters = MIN(iters, oiters);
+            }
+
+            /* Each iteration produces at least 5 output symbols. So until
+             * op[3] crosses olimit, we know we haven't executed iters
+             * iterations yet. This saves us maintaining an iters counter,
+             * at the expense of computing the remaining # of iterations
+             * more frequently.
+             */
+            olimit = op[3] + (iters * 5);
+
+            /* Exit the fast decoding loop if we are too close to the end. */
+            if (op[3] + 10 > olimit)
+                break;
+
+            /* Exit the decoding loop if any input pointer has crossed the
+             * previous one. This indicates corruption, and a precondition
+             * to our loop is that ip[i] >= ip[0].
+             */
+            for (stream = 1; stream < 4; ++stream) {
+                if (ip[stream] < ip[stream - 1])
+                    goto _out;
+            }
+        }
+
+#ifndef NDEBUG
+        for (stream = 1; stream < 4; ++stream) {
+            assert(ip[stream] >= ip[stream - 1]);
+        }
+#endif
+
+        do {
+            /* Do 5 table lookups for each of the first 3 streams */
+            for (symbol = 0; symbol < 5; ++symbol) {
+                for (stream = 0; stream < 3; ++stream) {
+                    int const index = (int)(bits[stream] >> 53);
+                    HUF_DEltX2 const entry = dtable[index];
+                    MEM_write16(op[stream], entry.sequence);
+                    bits[stream] <<= (entry.nbBits);
+                    op[stream] += (entry.length);
+                }
+            }
+            /* Do 1 table lookup from the final stream */
+            {
+                int const index = (int)(bits[3] >> 53);
+                HUF_DEltX2 const entry = dtable[index];
+                MEM_write16(op[3], entry.sequence);
+                bits[3] <<= (entry.nbBits);
+                op[3] += (entry.length);
+            }
+            /* Do 4 table lookups from the final stream & reload bitstreams */
+            for (stream = 0; stream < 4; ++stream) {
+                /* Do a table lookup from the final stream.
+                 * This is interleaved with the reloading to reduce register
+                 * pressure. This shouldn't be necessary, but compilers can
+                 * struggle with codegen with high register pressure.
+                 */
+                {
+                    int const index = (int)(bits[3] >> 53);
+                    HUF_DEltX2 const entry = dtable[index];
+                    MEM_write16(op[3], entry.sequence);
+                    bits[3] <<= (entry.nbBits);
+                    op[3] += (entry.length);
+                }
+                /* Reload the bistreams. The final bitstream must be reloaded
+                 * after the 5th symbol was decoded.
+                 */
+                {
+                    int const ctz = ZSTD_countTrailingZeros64(bits[stream]);
+                    int const nbBits = ctz & 7;
+                    int const nbBytes = ctz >> 3;
+                    ip[stream] -= nbBytes;
+                    bits[stream] = MEM_read64(ip[stream]) | 1;
+                    bits[stream] <<= nbBits;
+                }
+            }
+        } while (op[3] < olimit);
+    }
+
+_out:
+
+    /* Save the final values of each of the state variables back to args. */
+    ZSTD_memcpy(&args->bits, &bits, sizeof(bits));
+    ZSTD_memcpy((void*)(&args->ip), &ip, sizeof(ip));
+    ZSTD_memcpy(&args->op, &op, sizeof(op));
+}
+
+
+static HUF_FAST_BMI2_ATTRS size_t
+HUF_decompress4X2_usingDTable_internal_fast(
           void* dst,  size_t dstSize,
     const void* cSrc, size_t cSrcSize,
-    const HUF_DTable* DTable)
-{
-    DTableDesc dtd = HUF_getDTableDesc(DTable);
-    if (dtd.tableType != 1) return ERROR(GENERIC);
-    return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
+    const HUF_DTable* DTable,
+    HUF_DecompressFastLoopFn loopFn) {
+    void const* dt = DTable + 1;
+    const BYTE* const iend = (const BYTE*)cSrc + 6;
+    BYTE* const oend = (BYTE*)dst + dstSize;
+    HUF_DecompressFastArgs args;
+    {
+        size_t const ret = HUF_DecompressFastArgs_init(&args, dst, dstSize, cSrc, cSrcSize, DTable);
+        FORWARD_IF_ERROR(ret, "Failed to init asm args");
+        if (ret == 0)
+            return 0;
+    }
+
+    assert(args.ip[0] >= args.ilimit);
+    loopFn(&args);
+
+    /* note : op4 already verified within main loop */
+    assert(args.ip[0] >= iend);
+    assert(args.ip[1] >= iend);
+    assert(args.ip[2] >= iend);
+    assert(args.ip[3] >= iend);
+    assert(args.op[3] <= oend);
+    (void)iend;
+
+    /* finish bitStreams one by one */
+    {
+        size_t const segmentSize = (dstSize+3) / 4;
+        BYTE* segmentEnd = (BYTE*)dst;
+        int i;
+        for (i = 0; i < 4; ++i) {
+            BIT_DStream_t bit;
+            if (segmentSize <= (size_t)(oend - segmentEnd))
+                segmentEnd += segmentSize;
+            else
+                segmentEnd = oend;
+            FORWARD_IF_ERROR(HUF_initRemainingDStream(&bit, &args, i, segmentEnd), "corruption");
+            args.op[i] += HUF_decodeStreamX2(args.op[i], &bit, segmentEnd, (HUF_DEltX2 const*)dt, HUF_DECODER_FAST_TABLELOG);
+            if (args.op[i] != segmentEnd)
+                return ERROR(corruption_detected);
+        }
+    }
+
+    /* decoded size */
+    return dstSize;
 }
 
+static size_t HUF_decompress4X2_usingDTable_internal(void* dst, size_t dstSize, void const* cSrc,
+                    size_t cSrcSize, HUF_DTable const* DTable, int flags)
+{
+    HUF_DecompressUsingDTableFn fallbackFn = HUF_decompress4X2_usingDTable_internal_default;
+    HUF_DecompressFastLoopFn loopFn = HUF_decompress4X2_usingDTable_internal_fast_c_loop;
+
+#if DYNAMIC_BMI2
+    if (flags & HUF_flags_bmi2) {
+        fallbackFn = HUF_decompress4X2_usingDTable_internal_bmi2;
+# if ZSTD_ENABLE_ASM_X86_64_BMI2
+        if (!(flags & HUF_flags_disableAsm)) {
+            loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop;
+        }
+# endif
+    } else {
+        return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable);
+    }
+#endif
+
+#if ZSTD_ENABLE_ASM_X86_64_BMI2 && defined(__BMI2__)
+    if (!(flags & HUF_flags_disableAsm)) {
+        loopFn = HUF_decompress4X2_usingDTable_internal_fast_asm_loop;
+    }
+#endif
+
+    if (!(flags & HUF_flags_disableFast)) {
+        size_t const ret = HUF_decompress4X2_usingDTable_internal_fast(dst, dstSize, cSrc, cSrcSize, DTable, loopFn);
+        if (ret != 0)
+            return ret;
+    }
+    return fallbackFn(dst, dstSize, cSrc, cSrcSize, DTable);
+}
+
+HUF_DGEN(HUF_decompress1X2_usingDTable_internal)
+
 size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* DCtx, void* dst, size_t dstSize,
                                    const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize)
+                                   void* workSpace, size_t wkspSize, int flags)
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
     size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize,
-                                               workSpace, wkspSize);
+                                               workSpace, wkspSize, flags);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
 
-    return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, /* bmi2 */ 0);
+    return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx, flags);
 }
 
-
-size_t HUF_decompress4X2_usingDTable(
-          void* dst,  size_t dstSize,
-    const void* cSrc, size_t cSrcSize,
-    const HUF_DTable* DTable)
-{
-    DTableDesc dtd = HUF_getDTableDesc(DTable);
-    if (dtd.tableType != 1) return ERROR(GENERIC);
-    return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-}
-
-static size_t HUF_decompress4X2_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize,
+static size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
                                    const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize, int bmi2)
+                                   void* workSpace, size_t wkspSize, int flags)
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
     size_t hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize,
-                                         workSpace, wkspSize);
+                                         workSpace, wkspSize, flags);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
 
-    return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+    return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags);
 }
 
-size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
-                                   const void* cSrc, size_t cSrcSize,
-                                   void* workSpace, size_t wkspSize)
-{
-    return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, /* bmi2 */ 0);
-}
-
-
 #endif /* HUF_FORCE_DECOMPRESS_X1 */
 
 
@@ -984,66 +1726,28 @@
 /* Universal decompression selectors */
 /* ***********************************/
 
-size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize,
-                                    const void* cSrc, size_t cSrcSize,
-                                    const HUF_DTable* DTable)
-{
-    DTableDesc const dtd = HUF_getDTableDesc(DTable);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-    (void)dtd;
-    assert(dtd.tableType == 0);
-    return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-    (void)dtd;
-    assert(dtd.tableType == 1);
-    return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#else
-    return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
-                           HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#endif
-}
-
-size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize,
-                                    const void* cSrc, size_t cSrcSize,
-                                    const HUF_DTable* DTable)
-{
-    DTableDesc const dtd = HUF_getDTableDesc(DTable);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-    (void)dtd;
-    assert(dtd.tableType == 0);
-    return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-    (void)dtd;
-    assert(dtd.tableType == 1);
-    return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#else
-    return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0) :
-                           HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, /* bmi2 */ 0);
-#endif
-}
-
 
 #if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
 typedef struct { U32 tableTime; U32 decode256Time; } algo_time_t;
-static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] =
+static const algo_time_t algoTime[16 /* Quantization */][2 /* single, double */] =
 {
     /* single, double, quad */
-    {{0,0}, {1,1}, {2,2}},  /* Q==0 : impossible */
-    {{0,0}, {1,1}, {2,2}},  /* Q==1 : impossible */
-    {{  38,130}, {1313, 74}, {2151, 38}},   /* Q == 2 : 12-18% */
-    {{ 448,128}, {1353, 74}, {2238, 41}},   /* Q == 3 : 18-25% */
-    {{ 556,128}, {1353, 74}, {2238, 47}},   /* Q == 4 : 25-32% */
-    {{ 714,128}, {1418, 74}, {2436, 53}},   /* Q == 5 : 32-38% */
-    {{ 883,128}, {1437, 74}, {2464, 61}},   /* Q == 6 : 38-44% */
-    {{ 897,128}, {1515, 75}, {2622, 68}},   /* Q == 7 : 44-50% */
-    {{ 926,128}, {1613, 75}, {2730, 75}},   /* Q == 8 : 50-56% */
-    {{ 947,128}, {1729, 77}, {3359, 77}},   /* Q == 9 : 56-62% */
-    {{1107,128}, {2083, 81}, {4006, 84}},   /* Q ==10 : 62-69% */
-    {{1177,128}, {2379, 87}, {4785, 88}},   /* Q ==11 : 69-75% */
-    {{1242,128}, {2415, 93}, {5155, 84}},   /* Q ==12 : 75-81% */
-    {{1349,128}, {2644,106}, {5260,106}},   /* Q ==13 : 81-87% */
-    {{1455,128}, {2422,124}, {4174,124}},   /* Q ==14 : 87-93% */
-    {{ 722,128}, {1891,145}, {1936,146}},   /* Q ==15 : 93-99% */
+    {{0,0}, {1,1}},  /* Q==0 : impossible */
+    {{0,0}, {1,1}},  /* Q==1 : impossible */
+    {{ 150,216}, { 381,119}},   /* Q == 2 : 12-18% */
+    {{ 170,205}, { 514,112}},   /* Q == 3 : 18-25% */
+    {{ 177,199}, { 539,110}},   /* Q == 4 : 25-32% */
+    {{ 197,194}, { 644,107}},   /* Q == 5 : 32-38% */
+    {{ 221,192}, { 735,107}},   /* Q == 6 : 38-44% */
+    {{ 256,189}, { 881,106}},   /* Q == 7 : 44-50% */
+    {{ 359,188}, {1167,109}},   /* Q == 8 : 50-56% */
+    {{ 582,187}, {1570,114}},   /* Q == 9 : 56-62% */
+    {{ 688,187}, {1712,122}},   /* Q ==10 : 62-69% */
+    {{ 825,186}, {1965,136}},   /* Q ==11 : 69-75% */
+    {{ 976,185}, {2131,150}},   /* Q ==12 : 75-81% */
+    {{1180,186}, {2070,175}},   /* Q ==13 : 81-87% */
+    {{1377,185}, {1731,202}},   /* Q ==14 : 87-93% */
+    {{1412,185}, {1695,202}},   /* Q ==15 : 93-99% */
 };
 #endif
 
@@ -1070,42 +1774,15 @@
         U32 const D256 = (U32)(dstSize >> 8);
         U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256);
         U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256);
-        DTime1 += DTime1 >> 3;  /* advantage to algorithm using less memory, to reduce cache eviction */
+        DTime1 += DTime1 >> 5;  /* small advantage to algorithm using less memory, to reduce cache eviction */
         return DTime1 < DTime0;
     }
 #endif
 }
 
-
-size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst,
-                                     size_t dstSize, const void* cSrc,
-                                     size_t cSrcSize, void* workSpace,
-                                     size_t wkspSize)
-{
-    /* validation checks */
-    if (dstSize == 0) return ERROR(dstSize_tooSmall);
-    if (cSrcSize == 0) return ERROR(corruption_detected);
-
-    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-        (void)algoNb;
-        assert(algoNb == 0);
-        return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-        (void)algoNb;
-        assert(algoNb == 1);
-        return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
-#else
-        return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
-                            cSrcSize, workSpace, wkspSize):
-                        HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize);
-#endif
-    }
-}
-
 size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize,
                                   const void* cSrc, size_t cSrcSize,
-                                  void* workSpace, size_t wkspSize)
+                                  void* workSpace, size_t wkspSize, int flags)
 {
     /* validation checks */
     if (dstSize == 0) return ERROR(dstSize_tooSmall);
@@ -1118,71 +1795,71 @@
         (void)algoNb;
         assert(algoNb == 0);
         return HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc,
-                                cSrcSize, workSpace, wkspSize);
+                                cSrcSize, workSpace, wkspSize, flags);
 #elif defined(HUF_FORCE_DECOMPRESS_X2)
         (void)algoNb;
         assert(algoNb == 1);
         return HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
-                                cSrcSize, workSpace, wkspSize);
+                                cSrcSize, workSpace, wkspSize, flags);
 #else
         return algoNb ? HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc,
-                                cSrcSize, workSpace, wkspSize):
+                                cSrcSize, workSpace, wkspSize, flags):
                         HUF_decompress1X1_DCtx_wksp(dctx, dst, dstSize, cSrc,
-                                cSrcSize, workSpace, wkspSize);
+                                cSrcSize, workSpace, wkspSize, flags);
 #endif
     }
 }
 
 
-size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags)
 {
     DTableDesc const dtd = HUF_getDTableDesc(DTable);
 #if defined(HUF_FORCE_DECOMPRESS_X1)
     (void)dtd;
     assert(dtd.tableType == 0);
-    return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #elif defined(HUF_FORCE_DECOMPRESS_X2)
     (void)dtd;
     assert(dtd.tableType == 1);
-    return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #else
-    return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
-                           HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return dtd.tableType ? HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) :
+                           HUF_decompress1X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #endif
 }
 
 #ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress1X1_DCtx_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags)
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
-    size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+    size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize, flags);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
 
-    return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, bmi2);
+    return HUF_decompress1X1_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx, flags);
 }
 #endif
 
-size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
+size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags)
 {
     DTableDesc const dtd = HUF_getDTableDesc(DTable);
 #if defined(HUF_FORCE_DECOMPRESS_X1)
     (void)dtd;
     assert(dtd.tableType == 0);
-    return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #elif defined(HUF_FORCE_DECOMPRESS_X2)
     (void)dtd;
     assert(dtd.tableType == 1);
-    return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #else
-    return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2) :
-                           HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, bmi2);
+    return dtd.tableType ? HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags) :
+                           HUF_decompress4X1_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable, flags);
 #endif
 }
 
-size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2)
+size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags)
 {
     /* validation checks */
     if (dstSize == 0) return ERROR(dstSize_tooSmall);
@@ -1192,160 +1869,14 @@
 #if defined(HUF_FORCE_DECOMPRESS_X1)
         (void)algoNb;
         assert(algoNb == 0);
-        return HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+        return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags);
 #elif defined(HUF_FORCE_DECOMPRESS_X2)
         (void)algoNb;
         assert(algoNb == 1);
-        return HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+        return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags);
 #else
-        return algoNb ? HUF_decompress4X2_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2) :
-                        HUF_decompress4X1_DCtx_wksp_bmi2(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
+        return algoNb ? HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags) :
+                        HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workSpace, wkspSize, flags);
 #endif
     }
 }
-
-#ifndef ZSTD_NO_UNUSED_FUNCTIONS
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_readDTableX1_wksp(DTable, src, srcSize,
-                                 workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
-}
-#endif
-
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize)
-{
-  U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-  return HUF_readDTableX2_wksp(DTable, src, srcSize,
-                               workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-#endif
-
-#ifndef HUF_FORCE_DECOMPRESS_X2
-size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-#endif
-
-#ifndef HUF_FORCE_DECOMPRESS_X1
-size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-#endif
-
-typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
-
-size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
-    static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 };
-#endif
-
-    /* validation checks */
-    if (dstSize == 0) return ERROR(dstSize_tooSmall);
-    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
-    if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
-    if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
-
-    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-        (void)algoNb;
-        assert(algoNb == 0);
-        return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-        (void)algoNb;
-        assert(algoNb == 1);
-        return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize);
-#else
-        return decompress[algoNb](dst, dstSize, cSrc, cSrcSize);
-#endif
-    }
-}
-
-size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    /* validation checks */
-    if (dstSize == 0) return ERROR(dstSize_tooSmall);
-    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
-    if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
-    if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
-
-    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-        (void)algoNb;
-        assert(algoNb == 0);
-        return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-        (void)algoNb;
-        assert(algoNb == 1);
-        return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
-#else
-        return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) :
-                        HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ;
-#endif
-    }
-}
-
-size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                         workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
-                             const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                      workSpace, sizeof(workSpace));
-}
-#endif
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.c b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
index ce33547..309ec0d 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_ddict.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,12 +14,12 @@
 /*-*******************************************************
 *  Dependencies
 *********************************************************/
+#include "../common/allocations.h"  /* ZSTD_customMalloc, ZSTD_customFree */
 #include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */
 #include "../common/cpu.h"         /* bmi2 */
 #include "../common/mem.h"         /* low level memory routines */
 #define FSE_STATIC_LINKING_ONLY
 #include "../common/fse.h"
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "zstd_decompress_internal.h"
 #include "zstd_ddict.h"
@@ -134,7 +134,7 @@
         ZSTD_memcpy(internalBuffer, dict, dictSize);
     }
     ddict->dictSize = dictSize;
-    ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001);  /* cover both little and big endian */
+    ddict->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001);  /* cover both little and big endian */
 
     /* parse dictionary content */
     FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , "");
@@ -240,5 +240,5 @@
 unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict)
 {
     if (ddict==NULL) return 0;
-    return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize);
+    return ddict->dictID;
 }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.h b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
index bd03268..c4ca887 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_ddict.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress.c b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
index 910bc03..7bc2713 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -55,18 +55,18 @@
 /*-*******************************************************
 *  Dependencies
 *********************************************************/
+#include "../common/allocations.h"  /* ZSTD_customMalloc, ZSTD_customCalloc, ZSTD_customFree */
 #include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */
-#include "../common/cpu.h"         /* bmi2 */
 #include "../common/mem.h"         /* low level memory routines */
 #define FSE_STATIC_LINKING_ONLY
 #include "../common/fse.h"
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */
 #include "../common/zstd_internal.h"  /* blockProperties_t */
 #include "zstd_decompress_internal.h"   /* ZSTD_DCtx */
 #include "zstd_ddict.h"  /* ZSTD_DDictDictContent */
 #include "zstd_decompress_block.h"   /* ZSTD_decompressBlock_internal */
+#include "../common/bits.h"  /* ZSTD_highbit32 */
 
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
 #  include "../legacy/zstd_legacy.h"
@@ -79,11 +79,11 @@
  *************************************/
 
 #define DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT 4
-#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3   /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float.
-                                                     * Currently, that means a 0.75 load factor.
-                                                     * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded
-                                                     * the load factor of the ddict hash set.
-                                                     */
+#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3  /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float.
+                                                    * Currently, that means a 0.75 load factor.
+                                                    * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded
+                                                    * the load factor of the ddict hash set.
+                                                    */
 
 #define DDICT_HASHSET_TABLE_BASE_SIZE 64
 #define DDICT_HASHSET_RESIZE_FACTOR 2
@@ -177,12 +177,15 @@
 static ZSTD_DDictHashSet* ZSTD_createDDictHashSet(ZSTD_customMem customMem) {
     ZSTD_DDictHashSet* ret = (ZSTD_DDictHashSet*)ZSTD_customMalloc(sizeof(ZSTD_DDictHashSet), customMem);
     DEBUGLOG(4, "Allocating new hash set");
+    if (!ret)
+        return NULL;
     ret->ddictPtrTable = (const ZSTD_DDict**)ZSTD_customCalloc(DDICT_HASHSET_TABLE_BASE_SIZE * sizeof(ZSTD_DDict*), customMem);
-    ret->ddictPtrTableSize = DDICT_HASHSET_TABLE_BASE_SIZE;
-    ret->ddictPtrCount = 0;
-    if (!ret || !ret->ddictPtrTable) {
+    if (!ret->ddictPtrTable) {
+        ZSTD_customFree(ret, customMem);
         return NULL;
     }
+    ret->ddictPtrTableSize = DDICT_HASHSET_TABLE_BASE_SIZE;
+    ret->ddictPtrCount = 0;
     return ret;
 }
 
@@ -241,6 +244,7 @@
     dctx->outBufferMode = ZSTD_bm_buffered;
     dctx->forceIgnoreChecksum = ZSTD_d_validateChecksum;
     dctx->refMultipleDDicts = ZSTD_rmd_refSingleDDict;
+    dctx->disableHufAsm = 0;
 }
 
 static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx)
@@ -255,11 +259,15 @@
     dctx->inBuffSize  = 0;
     dctx->outBuffSize = 0;
     dctx->streamStage = zdss_init;
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
     dctx->legacyContext = NULL;
     dctx->previousLegacyVersion = 0;
+#endif
     dctx->noForwardProgress = 0;
     dctx->oversizedDuration = 0;
-    dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
+#if DYNAMIC_BMI2
+    dctx->bmi2 = ZSTD_cpuSupportsBmi2();
+#endif
     dctx->ddictSet = NULL;
     ZSTD_DCtx_resetParameters(dctx);
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
@@ -280,8 +288,7 @@
     return dctx;
 }
 
-ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
-{
+static ZSTD_DCtx* ZSTD_createDCtx_internal(ZSTD_customMem customMem) {
     if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
 
     {   ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_customMalloc(sizeof(*dctx), customMem);
@@ -292,10 +299,15 @@
     }
 }
 
+ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
+{
+    return ZSTD_createDCtx_internal(customMem);
+}
+
 ZSTD_DCtx* ZSTD_createDCtx(void)
 {
     DEBUGLOG(3, "ZSTD_createDCtx");
-    return ZSTD_createDCtx_advanced(ZSTD_defaultCMem);
+    return ZSTD_createDCtx_internal(ZSTD_defaultCMem);
 }
 
 static void ZSTD_clearDict(ZSTD_DCtx* dctx)
@@ -380,6 +392,19 @@
     return 0;
 }
 
+/*! ZSTD_isSkippableFrame() :
+ *  Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame.
+ *  Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
+ */
+unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size)
+{
+    if (size < ZSTD_FRAMEIDSIZE) return 0;
+    {   U32 const magic = MEM_readLE32(buffer);
+        if ((magic & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) return 1;
+    }
+    return 0;
+}
+
 /** ZSTD_frameHeaderSize_internal() :
  *  srcSize must be large enough to reach header size fields.
  *  note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless.
@@ -415,16 +440,40 @@
  *  note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless
  * @return : 0, `zfhPtr` is correctly filled,
  *          >0, `srcSize` is too small, value is wanted `srcSize` amount,
- *           or an error code, which can be tested using ZSTD_isError() */
+**           or an error code, which can be tested using ZSTD_isError() */
 size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format)
 {
     const BYTE* ip = (const BYTE*)src;
     size_t const minInputSize = ZSTD_startingInputLength(format);
 
-    ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr));   /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */
-    if (srcSize < minInputSize) return minInputSize;
-    RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter");
+    DEBUGLOG(5, "ZSTD_getFrameHeader_advanced: minInputSize = %zu, srcSize = %zu", minInputSize, srcSize);
 
+    if (srcSize > 0) {
+        /* note : technically could be considered an assert(), since it's an invalid entry */
+        RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter : src==NULL, but srcSize>0");
+    }
+    if (srcSize < minInputSize) {
+        if (srcSize > 0 && format != ZSTD_f_zstd1_magicless) {
+            /* when receiving less than @minInputSize bytes,
+             * control these bytes at least correspond to a supported magic number
+             * in order to error out early if they don't.
+            **/
+            size_t const toCopy = MIN(4, srcSize);
+            unsigned char hbuf[4]; MEM_writeLE32(hbuf, ZSTD_MAGICNUMBER);
+            assert(src != NULL);
+            ZSTD_memcpy(hbuf, src, toCopy);
+            if ( MEM_readLE32(hbuf) != ZSTD_MAGICNUMBER ) {
+                /* not a zstd frame : let's check if it's a skippable frame */
+                MEM_writeLE32(hbuf, ZSTD_MAGIC_SKIPPABLE_START);
+                ZSTD_memcpy(hbuf, src, toCopy);
+                if ((MEM_readLE32(hbuf) & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START) {
+                    RETURN_ERROR(prefix_unknown,
+                                "first bytes don't correspond to any supported magic number");
+        }   }   }
+        return minInputSize;
+    }
+
+    ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr));   /* not strictly necessary, but static analyzers may not understand that zfhPtr will be read only if return value is zero, since they are 2 different signals */
     if ( (format != ZSTD_f_zstd1_magicless)
       && (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) {
         if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
@@ -466,7 +515,9 @@
         }
         switch(dictIDSizeCode)
         {
-            default: assert(0);  /* impossible */
+            default:
+                assert(0);  /* impossible */
+                ZSTD_FALLTHROUGH;
             case 0 : break;
             case 1 : dictID = ip[pos]; pos++; break;
             case 2 : dictID = MEM_readLE16(ip+pos); pos+=2; break;
@@ -474,7 +525,9 @@
         }
         switch(fcsID)
         {
-            default: assert(0);  /* impossible */
+            default:
+                assert(0);  /* impossible */
+                ZSTD_FALLTHROUGH;
             case 0 : if (singleSegment) frameContentSize = ip[pos]; break;
             case 1 : frameContentSize = MEM_readLE16(ip+pos)+256; break;
             case 2 : frameContentSize = MEM_readLE32(ip+pos); break;
@@ -503,7 +556,6 @@
     return ZSTD_getFrameHeader_advanced(zfhPtr, src, srcSize, ZSTD_f_zstd1);
 }
 
-
 /** ZSTD_getFrameContentSize() :
  *  compatible with legacy mode
  * @return : decompressed size of the single frame pointed to be `src` if known, otherwise
@@ -537,18 +589,52 @@
     sizeU32 = MEM_readLE32((BYTE const*)src + ZSTD_FRAMEIDSIZE);
     RETURN_ERROR_IF((U32)(sizeU32 + ZSTD_SKIPPABLEHEADERSIZE) < sizeU32,
                     frameParameter_unsupported, "");
-    {
-        size_t const skippableSize = skippableHeaderSize + sizeU32;
+    {   size_t const skippableSize = skippableHeaderSize + sizeU32;
         RETURN_ERROR_IF(skippableSize > srcSize, srcSize_wrong, "");
         return skippableSize;
     }
 }
 
+/*! ZSTD_readSkippableFrame() :
+ * Retrieves content of a skippable frame, and writes it to dst buffer.
+ *
+ * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
+ * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START.  This can be NULL if the caller is not interested
+ * in the magicVariant.
+ *
+ * Returns an error if destination buffer is not large enough, or if this is not a valid skippable frame.
+ *
+ * @return : number of bytes written or a ZSTD error.
+ */
+size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity,
+                               unsigned* magicVariant,  /* optional, can be NULL */
+                         const void* src, size_t srcSize)
+{
+    RETURN_ERROR_IF(srcSize < ZSTD_SKIPPABLEHEADERSIZE, srcSize_wrong, "");
+
+    {   U32 const magicNumber = MEM_readLE32(src);
+        size_t skippableFrameSize = readSkippableFrameSize(src, srcSize);
+        size_t skippableContentSize = skippableFrameSize - ZSTD_SKIPPABLEHEADERSIZE;
+
+        /* check input validity */
+        RETURN_ERROR_IF(!ZSTD_isSkippableFrame(src, srcSize), frameParameter_unsupported, "");
+        RETURN_ERROR_IF(skippableFrameSize < ZSTD_SKIPPABLEHEADERSIZE || skippableFrameSize > srcSize, srcSize_wrong, "");
+        RETURN_ERROR_IF(skippableContentSize > dstCapacity, dstSize_tooSmall, "");
+
+        /* deliver payload */
+        if (skippableContentSize > 0  && dst != NULL)
+            ZSTD_memcpy(dst, (const BYTE *)src + ZSTD_SKIPPABLEHEADERSIZE, skippableContentSize);
+        if (magicVariant != NULL)
+            *magicVariant = magicNumber - ZSTD_MAGIC_SKIPPABLE_START;
+        return skippableContentSize;
+    }
+}
+
 /** ZSTD_findDecompressedSize() :
- *  compatible with legacy mode
  *  `srcSize` must be the exact length of some number of ZSTD compressed and/or
  *      skippable frames
- *  @return : decompressed size of the frames contained */
+ *  note: compatible with legacy mode
+ * @return : decompressed size of the frames contained */
 unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
 {
     unsigned long long totalDstSize = 0;
@@ -558,9 +644,7 @@
 
         if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
             size_t const skippableSize = readSkippableFrameSize(src, srcSize);
-            if (ZSTD_isError(skippableSize)) {
-                return ZSTD_CONTENTSIZE_ERROR;
-            }
+            if (ZSTD_isError(skippableSize)) return ZSTD_CONTENTSIZE_ERROR;
             assert(skippableSize <= srcSize);
 
             src = (const BYTE *)src + skippableSize;
@@ -568,17 +652,17 @@
             continue;
         }
 
-        {   unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize);
-            if (ret >= ZSTD_CONTENTSIZE_ERROR) return ret;
+        {   unsigned long long const fcs = ZSTD_getFrameContentSize(src, srcSize);
+            if (fcs >= ZSTD_CONTENTSIZE_ERROR) return fcs;
 
-            /* check for overflow */
-            if (totalDstSize + ret < totalDstSize) return ZSTD_CONTENTSIZE_ERROR;
-            totalDstSize += ret;
+            if (totalDstSize + fcs < totalDstSize)
+                return ZSTD_CONTENTSIZE_ERROR; /* check for overflow */
+            totalDstSize += fcs;
         }
+        /* skip to next frame */
         {   size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize);
-            if (ZSTD_isError(frameSrcSize)) {
-                return ZSTD_CONTENTSIZE_ERROR;
-            }
+            if (ZSTD_isError(frameSrcSize)) return ZSTD_CONTENTSIZE_ERROR;
+            assert(frameSrcSize <= srcSize);
 
             src = (const BYTE *)src + frameSrcSize;
             srcSize -= frameSrcSize;
@@ -700,10 +784,11 @@
             ip += 4;
         }
 
+        frameSizeInfo.nbBlocks = nbBlocks;
         frameSizeInfo.compressedSize = (size_t)(ip - ipstart);
         frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN)
                                         ? zfh.frameContentSize
-                                        : nbBlocks * zfh.blockSizeMax;
+                                        : (unsigned long long)nbBlocks * zfh.blockSizeMax;
         return frameSizeInfo;
     }
 }
@@ -743,6 +828,48 @@
     return bound;
 }
 
+size_t ZSTD_decompressionMargin(void const* src, size_t srcSize)
+{
+    size_t margin = 0;
+    unsigned maxBlockSize = 0;
+
+    /* Iterate over each frame */
+    while (srcSize > 0) {
+        ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize);
+        size_t const compressedSize = frameSizeInfo.compressedSize;
+        unsigned long long const decompressedBound = frameSizeInfo.decompressedBound;
+        ZSTD_frameHeader zfh;
+
+        FORWARD_IF_ERROR(ZSTD_getFrameHeader(&zfh, src, srcSize), "");
+        if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR)
+            return ERROR(corruption_detected);
+
+        if (zfh.frameType == ZSTD_frame) {
+            /* Add the frame header to our margin */
+            margin += zfh.headerSize;
+            /* Add the checksum to our margin */
+            margin += zfh.checksumFlag ? 4 : 0;
+            /* Add 3 bytes per block */
+            margin += 3 * frameSizeInfo.nbBlocks;
+
+            /* Compute the max block size */
+            maxBlockSize = MAX(maxBlockSize, zfh.blockSizeMax);
+        } else {
+            assert(zfh.frameType == ZSTD_skippableFrame);
+            /* Add the entire skippable frame size to our margin. */
+            margin += compressedSize;
+        }
+
+        assert(srcSize >= compressedSize);
+        src = (const BYTE*)src + compressedSize;
+        srcSize -= compressedSize;
+    }
+
+    /* Add the max block size back to the margin. */
+    margin += maxBlockSize;
+
+    return margin;
+}
 
 /*-*************************************************************
  *   Frame decoding
@@ -768,7 +895,7 @@
         if (srcSize == 0) return 0;
         RETURN_ERROR(dstBuffer_null, "");
     }
-    ZSTD_memcpy(dst, src, srcSize);
+    ZSTD_memmove(dst, src, srcSize);
     return srcSize;
 }
 
@@ -846,6 +973,7 @@
 
     /* Loop on each block */
     while (1) {
+        BYTE* oBlockEnd = oend;
         size_t decodedSize;
         blockProperties_t blockProperties;
         size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties);
@@ -855,16 +983,34 @@
         remainingSrcSize -= ZSTD_blockHeaderSize;
         RETURN_ERROR_IF(cBlockSize > remainingSrcSize, srcSize_wrong, "");
 
+        if (ip >= op && ip < oBlockEnd) {
+            /* We are decompressing in-place. Limit the output pointer so that we
+             * don't overwrite the block that we are currently reading. This will
+             * fail decompression if the input & output pointers aren't spaced
+             * far enough apart.
+             *
+             * This is important to set, even when the pointers are far enough
+             * apart, because ZSTD_decompressBlock_internal() can decide to store
+             * literals in the output buffer, after the block it is decompressing.
+             * Since we don't want anything to overwrite our input, we have to tell
+             * ZSTD_decompressBlock_internal to never write past ip.
+             *
+             * See ZSTD_allocateLiteralsBuffer() for reference.
+             */
+            oBlockEnd = op + (ip - op);
+        }
+
         switch(blockProperties.blockType)
         {
         case bt_compressed:
-            decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oend-op), ip, cBlockSize, /* frame */ 1);
+            decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oBlockEnd-op), ip, cBlockSize, /* frame */ 1, not_streaming);
             break;
         case bt_raw :
+            /* Use oend instead of oBlockEnd because this function is safe to overlap. It uses memmove. */
             decodedSize = ZSTD_copyRawBlock(op, (size_t)(oend-op), ip, cBlockSize);
             break;
         case bt_rle :
-            decodedSize = ZSTD_setRleBlock(op, (size_t)(oend-op), *ip, blockProperties.origSize);
+            decodedSize = ZSTD_setRleBlock(op, (size_t)(oBlockEnd-op), *ip, blockProperties.origSize);
             break;
         case bt_reserved :
         default:
@@ -899,6 +1045,7 @@
     }
     ZSTD_DCtx_trace_end(dctx, (U64)(op-ostart), (U64)(ip-istart), /* streaming */ 0);
     /* Allow caller to get size read */
+    DEBUGLOG(4, "ZSTD_decompressFrame: decompressed frame of size %zi, consuming %zi bytes of input", op-ostart, ip - (const BYTE*)*srcPtr);
     *srcPtr = ip;
     *srcSizePtr = remainingSrcSize;
     return (size_t)(op-ostart);
@@ -945,17 +1092,18 @@
         }
 #endif
 
-        {   U32 const magicNumber = MEM_readLE32(src);
-            DEBUGLOG(4, "reading magic number %08X (expecting %08X)",
-                        (unsigned)magicNumber, ZSTD_MAGICNUMBER);
+        if (srcSize >= 4) {
+            U32 const magicNumber = MEM_readLE32(src);
+            DEBUGLOG(5, "reading magic number %08X", (unsigned)magicNumber);
             if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
+                /* skippable frame detected : skip it */
                 size_t const skippableSize = readSkippableFrameSize(src, srcSize);
-                FORWARD_IF_ERROR(skippableSize, "readSkippableFrameSize failed");
+                FORWARD_IF_ERROR(skippableSize, "invalid skippable frame");
                 assert(skippableSize <= srcSize);
 
                 src = (const BYTE *)src + skippableSize;
                 srcSize -= skippableSize;
-                continue;
+                continue; /* check next frame */
         }   }
 
         if (ddict) {
@@ -1009,7 +1157,7 @@
     switch (dctx->dictUses) {
     default:
         assert(0 /* Impossible */);
-        /* fall-through */
+        ZSTD_FALLTHROUGH;
     case ZSTD_dont_use:
         ZSTD_clearDict(dctx);
         return NULL;
@@ -1031,7 +1179,7 @@
 {
 #if defined(ZSTD_HEAPMODE) && (ZSTD_HEAPMODE>=1)
     size_t regenSize;
-    ZSTD_DCtx* const dctx = ZSTD_createDCtx();
+    ZSTD_DCtx* const dctx =  ZSTD_createDCtx_internal(ZSTD_defaultCMem);
     RETURN_ERROR_IF(dctx==NULL, memory_allocation, "NULL pointer!");
     regenSize = ZSTD_decompressDCtx(dctx, dst, dstCapacity, src, srcSize);
     ZSTD_freeDCtx(dctx);
@@ -1051,8 +1199,8 @@
 size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; }
 
 /**
- * Similar to ZSTD_nextSrcSizeToDecompress(), but when when a block input can be streamed,
- * we allow taking a partial block as the input. Currently only raw uncompressed blocks can
+ * Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we
+ * allow taking a partial block as the input. Currently only raw uncompressed blocks can
  * be streamed.
  *
  * For blocks that can be streamed, this allows us to reduce the latency until we produce
@@ -1065,7 +1213,7 @@
         return dctx->expected;
     if (dctx->bType != bt_raw)
         return dctx->expected;
-    return MIN(MAX(inputSize, 1), dctx->expected);
+    return BOUNDED(1, inputSize, dctx->expected);
 }
 
 ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx) {
@@ -1073,7 +1221,9 @@
     {
     default:   /* should not happen */
         assert(0);
+        ZSTD_FALLTHROUGH;
     case ZSTDds_getFrameHeaderSize:
+        ZSTD_FALLTHROUGH;
     case ZSTDds_decodeFrameHeader:
         return ZSTDnit_frameHeader;
     case ZSTDds_decodeBlockHeader:
@@ -1085,6 +1235,7 @@
     case ZSTDds_checkChecksum:
         return ZSTDnit_checksum;
     case ZSTDds_decodeSkippableHeader:
+        ZSTD_FALLTHROUGH;
     case ZSTDds_skipFrame:
         return ZSTDnit_skippableFrame;
     }
@@ -1168,7 +1319,7 @@
             {
             case bt_compressed:
                 DEBUGLOG(5, "ZSTD_decompressContinue: case bt_compressed");
-                rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1);
+                rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 1, is_streaming);
                 dctx->expected = 0;  /* Streaming not supported */
                 break;
             case bt_raw :
@@ -1249,7 +1400,7 @@
 
     default:
         assert(0);   /* impossible */
-        RETURN_ERROR(GENERIC, "impossible to reach");   /* some compiler require default to do something */
+        RETURN_ERROR(GENERIC, "impossible to reach");   /* some compilers require default to do something */
     }
 }
 
@@ -1290,11 +1441,11 @@
         /* in minimal huffman, we always use X1 variants */
         size_t const hSize = HUF_readDTableX1_wksp(entropy->hufTable,
                                                 dictPtr, dictEnd - dictPtr,
-                                                workspace, workspaceSize);
+                                                workspace, workspaceSize, /* flags */ 0);
 #else
         size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable,
                                                 dictPtr, (size_t)(dictEnd - dictPtr),
-                                                workspace, workspaceSize);
+                                                workspace, workspaceSize, /* flags */ 0);
 #endif
         RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, "");
         dictPtr += hSize;
@@ -1393,7 +1544,7 @@
     dctx->prefixStart = NULL;
     dctx->virtualStart = NULL;
     dctx->dictEnd = NULL;
-    dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001);  /* cover both little and big endian */
+    dctx->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001);  /* cover both little and big endian */
     dctx->litEntropy = dctx->fseEntropy = 0;
     dctx->dictID = 0;
     dctx->bType = bt_reserved;
@@ -1455,7 +1606,7 @@
  *  This could for one of the following reasons :
  *  - The frame does not require a dictionary (most common case).
  *  - The frame was built with dictID intentionally removed.
- *    Needed dictionary is a hidden information.
+ *    Needed dictionary is a hidden piece of information.
  *    Note : this use case also happens when using a non-conformant dictionary.
  *  - `srcSize` is too small, and as a result, frame header could not be decoded.
  *    Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`.
@@ -1464,7 +1615,7 @@
  *  ZSTD_getFrameHeader(), which will provide a more precise error code. */
 unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize)
 {
-    ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0 };
+    ZSTD_frameHeader zfp = { 0, 0, 0, ZSTD_frame, 0, 0, 0, 0, 0 };
     size_t const hError = ZSTD_getFrameHeader(&zfp, src, srcSize);
     if (ZSTD_isError(hError)) return 0;
     return zfp.dictID;
@@ -1493,7 +1644,7 @@
 ZSTD_DStream* ZSTD_createDStream(void)
 {
     DEBUGLOG(3, "ZSTD_createDStream");
-    return ZSTD_createDStream_advanced(ZSTD_defaultCMem);
+    return ZSTD_createDCtx_internal(ZSTD_defaultCMem);
 }
 
 ZSTD_DStream* ZSTD_initStaticDStream(void *workspace, size_t workspaceSize)
@@ -1503,7 +1654,7 @@
 
 ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem)
 {
-    return ZSTD_createDCtx_advanced(customMem);
+    return ZSTD_createDCtx_internal(customMem);
 }
 
 size_t ZSTD_freeDStream(ZSTD_DStream* zds)
@@ -1571,7 +1722,9 @@
 size_t ZSTD_initDStream(ZSTD_DStream* zds)
 {
     DEBUGLOG(4, "ZSTD_initDStream");
-    return ZSTD_initDStream_usingDDict(zds, NULL);
+    FORWARD_IF_ERROR(ZSTD_DCtx_reset(zds, ZSTD_reset_session_only), "");
+    FORWARD_IF_ERROR(ZSTD_DCtx_refDDict(zds, NULL), "");
+    return ZSTD_startingInputLength(zds->format);
 }
 
 /* ZSTD_initDStream_usingDDict() :
@@ -1579,6 +1732,7 @@
  * this function cannot fail */
 size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* dctx, const ZSTD_DDict* ddict)
 {
+    DEBUGLOG(4, "ZSTD_initDStream_usingDDict");
     FORWARD_IF_ERROR( ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only) , "");
     FORWARD_IF_ERROR( ZSTD_DCtx_refDDict(dctx, ddict) , "");
     return ZSTD_startingInputLength(dctx->format);
@@ -1589,6 +1743,7 @@
  * this function cannot fail */
 size_t ZSTD_resetDStream(ZSTD_DStream* dctx)
 {
+    DEBUGLOG(4, "ZSTD_resetDStream");
     FORWARD_IF_ERROR(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only), "");
     return ZSTD_startingInputLength(dctx->format);
 }
@@ -1660,6 +1815,11 @@
             bounds.lowerBound = (int)ZSTD_rmd_refSingleDDict;
             bounds.upperBound = (int)ZSTD_rmd_refMultipleDDicts;
             return bounds;
+        case ZSTD_d_disableHuffmanAssembly:
+            bounds.lowerBound = 0;
+            bounds.upperBound = 1;
+            return bounds;
+
         default:;
     }
     bounds.error = ERROR(parameter_unsupported);
@@ -1700,6 +1860,9 @@
         case ZSTD_d_refMultipleDDicts:
             *value = (int)dctx->refMultipleDDicts;
             return 0;
+        case ZSTD_d_disableHuffmanAssembly:
+            *value = (int)dctx->disableHufAsm;
+            return 0;
         default:;
     }
     RETURN_ERROR(parameter_unsupported, "");
@@ -1733,6 +1896,10 @@
             }
             dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value;
             return 0;
+        case ZSTD_d_disableHuffmanAssembly:
+            CHECK_DBOUNDS(ZSTD_d_disableHuffmanAssembly, value);
+            dctx->disableHufAsm = value != 0;
+            return 0;
         default:;
     }
     RETURN_ERROR(parameter_unsupported, "");
@@ -1763,7 +1930,8 @@
 size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize)
 {
     size_t const blockSize = (size_t) MIN(windowSize, ZSTD_BLOCKSIZE_MAX);
-    unsigned long long const neededRBSize = windowSize + blockSize + (WILDCOPY_OVERLENGTH * 2);
+    /* space is needed to store the litbuffer after the output of a given block without stomping the extDict of a previous run, as well as to cover both windows against wildcopy*/
+    unsigned long long const neededRBSize = windowSize + blockSize + ZSTD_BLOCKSIZE_MAX + (WILDCOPY_OVERLENGTH * 2);
     unsigned long long const neededSize = MIN(frameContentSize, neededRBSize);
     size_t const minRBSize = (size_t) neededSize;
     RETURN_ERROR_IF((unsigned long long)minRBSize != neededSize,
@@ -1897,10 +2065,12 @@
             DEBUGLOG(5, "stage zdss_init => transparent reset ");
             zds->streamStage = zdss_loadHeader;
             zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0;
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
             zds->legacyVersion = 0;
+#endif
             zds->hostageByte = 0;
             zds->expectedOutBuffer = *output;
-            /* fall-through */
+            ZSTD_FALLTHROUGH;
 
         case zdss_loadHeader :
             DEBUGLOG(5, "stage zdss_loadHeader (srcSize : %u)", (U32)(iend - ip));
@@ -1917,7 +2087,6 @@
                 if (zds->refMultipleDDicts && zds->ddictSet) {
                     ZSTD_DCtx_selectFrameDDict(zds);
                 }
-                DEBUGLOG(5, "header size : %u", (U32)hSize);
                 if (ZSTD_isError(hSize)) {
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
                     U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart);
@@ -1949,6 +2118,11 @@
                             zds->lhSize += remainingInput;
                         }
                         input->pos = input->size;
+                        /* check first few bytes */
+                        FORWARD_IF_ERROR(
+                            ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format),
+                            "First few bytes detected incorrect" );
+                        /* return hint input size */
                         return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize;   /* remaining header bytes + next block header */
                     }
                     assert(ip != NULL);
@@ -1966,8 +2140,9 @@
                     size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, (size_t)(oend-op), istart, cSize, ZSTD_getDDict(zds));
                     if (ZSTD_isError(decompressedSize)) return decompressedSize;
                     DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()")
+                    assert(istart != NULL);
                     ip = istart + cSize;
-                    op += decompressedSize;
+                    op = op ? op + decompressedSize : op; /* can occur if frameContentSize = 0 (empty frame) */
                     zds->expected = 0;
                     zds->streamStage = zdss_init;
                     someMoreWork = 0;
@@ -2038,7 +2213,7 @@
                         zds->outBuffSize = neededOutBuffSize;
             }   }   }
             zds->streamStage = zdss_read;
-            /* fall-through */
+            ZSTD_FALLTHROUGH;
 
         case zdss_read:
             DEBUGLOG(5, "stage zdss_read");
@@ -2051,13 +2226,14 @@
                 }
                 if ((size_t)(iend-ip) >= neededInSize) {  /* decode directly from src */
                     FORWARD_IF_ERROR(ZSTD_decompressContinueStream(zds, &op, oend, ip, neededInSize), "");
+                    assert(ip != NULL);
                     ip += neededInSize;
                     /* Function modifies the stage so we must break */
                     break;
             }   }
             if (ip==iend) { someMoreWork = 0; break; }   /* no more input */
             zds->streamStage = zdss_load;
-            /* fall-through */
+            ZSTD_FALLTHROUGH;
 
         case zdss_load:
             {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds);
@@ -2065,7 +2241,7 @@
                 int const isSkipFrame = ZSTD_isSkipFrame(zds);
                 size_t loadedSize;
                 /* At this point we shouldn't be decompressing a block that we can stream. */
-                assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip));
+                assert(neededInSize == ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (size_t)(iend - ip)));
                 if (isSkipFrame) {
                     loadedSize = MIN(toLoad, (size_t)(iend-ip));
                 } else {
@@ -2074,8 +2250,11 @@
                                     "should never happen");
                     loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (size_t)(iend-ip));
                 }
-                ip += loadedSize;
-                zds->inPos += loadedSize;
+                if (loadedSize != 0) {
+                    /* ip may be NULL */
+                    ip += loadedSize;
+                    zds->inPos += loadedSize;
+                }
                 if (loadedSize < toLoad) { someMoreWork = 0; break; }   /* not enough input, wait for more */
 
                 /* decode loaded input */
@@ -2085,14 +2264,17 @@
                 break;
             }
         case zdss_flush:
-            {   size_t const toFlushSize = zds->outEnd - zds->outStart;
+            {
+                size_t const toFlushSize = zds->outEnd - zds->outStart;
                 size_t const flushedSize = ZSTD_limitCopy(op, (size_t)(oend-op), zds->outBuff + zds->outStart, toFlushSize);
-                op += flushedSize;
+
+                op = op ? op + flushedSize : op;
+
                 zds->outStart += flushedSize;
                 if (flushedSize == toFlushSize) {  /* flush completed */
                     zds->streamStage = zdss_read;
                     if ( (zds->outBuffSize < zds->fParams.frameContentSize)
-                      && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) {
+                        && (zds->outStart + zds->fParams.blockSizeMax > zds->outBuffSize) ) {
                         DEBUGLOG(5, "restart filling outBuff from beginning (left:%i, needed:%u)",
                                 (int)(zds->outBuffSize - zds->outStart),
                                 (U32)zds->fParams.blockSizeMax);
@@ -2106,7 +2288,7 @@
 
         default:
             assert(0);    /* impossible */
-            RETURN_ERROR(GENERIC, "impossible to reach");   /* some compiler require default to do something */
+            RETURN_ERROR(GENERIC, "impossible to reach");   /* some compilers require default to do something */
     }   }
 
     /* result */
@@ -2119,8 +2301,8 @@
     if ((ip==istart) && (op==ostart)) {  /* no forward progress */
         zds->noForwardProgress ++;
         if (zds->noForwardProgress >= ZSTD_NO_FORWARD_PROGRESS_MAX) {
-            RETURN_ERROR_IF(op==oend, dstSize_tooSmall, "");
-            RETURN_ERROR_IF(ip==iend, srcSize_wrong, "");
+            RETURN_ERROR_IF(op==oend, noForwardProgress_destFull, "");
+            RETURN_ERROR_IF(ip==iend, noForwardProgress_inputEmpty, "");
             assert(0);
         }
     } else {
@@ -2157,11 +2339,17 @@
                             void* dst, size_t dstCapacity, size_t* dstPos,
                       const void* src, size_t srcSize, size_t* srcPos)
 {
-    ZSTD_outBuffer output = { dst, dstCapacity, *dstPos };
-    ZSTD_inBuffer  input  = { src, srcSize, *srcPos };
-    /* ZSTD_compress_generic() will check validity of dstPos and srcPos */
-    size_t const cErr = ZSTD_decompressStream(dctx, &output, &input);
-    *dstPos = output.pos;
-    *srcPos = input.pos;
-    return cErr;
+    ZSTD_outBuffer output;
+    ZSTD_inBuffer  input;
+    output.dst = dst;
+    output.size = dstCapacity;
+    output.pos = *dstPos;
+    input.src = src;
+    input.size = srcSize;
+    input.pos = *srcPos;
+    {   size_t const cErr = ZSTD_decompressStream(dctx, &output, &input);
+        *dstPos = output.pos;
+        *srcPos = input.pos;
+        return cErr;
+    }
 }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
index 349dcdc..09896a9 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -20,12 +20,12 @@
 #include "../common/mem.h"         /* low level memory routines */
 #define FSE_STATIC_LINKING_ONLY
 #include "../common/fse.h"
-#define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
 #include "../common/zstd_internal.h"
 #include "zstd_decompress_internal.h"   /* ZSTD_DCtx */
 #include "zstd_ddict.h"  /* ZSTD_DDictDictContent */
 #include "zstd_decompress_block.h"
+#include "../common/bits.h"  /* ZSTD_highbit32 */
 
 /*_*******************************************************
 *  Macros
@@ -69,15 +69,56 @@
     }
 }
 
+/* Allocate buffer for literals, either overlapping current dst, or split between dst and litExtraBuffer, or stored entirely within litExtraBuffer */
+static void ZSTD_allocateLiteralsBuffer(ZSTD_DCtx* dctx, void* const dst, const size_t dstCapacity, const size_t litSize,
+    const streaming_operation streaming, const size_t expectedWriteSize, const unsigned splitImmediately)
+{
+    if (streaming == not_streaming && dstCapacity > ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH + litSize + WILDCOPY_OVERLENGTH)
+    {
+        /* room for litbuffer to fit without read faulting */
+        dctx->litBuffer = (BYTE*)dst + ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH;
+        dctx->litBufferEnd = dctx->litBuffer + litSize;
+        dctx->litBufferLocation = ZSTD_in_dst;
+    }
+    else if (litSize > ZSTD_LITBUFFEREXTRASIZE)
+    {
+        /* won't fit in litExtraBuffer, so it will be split between end of dst and extra buffer */
+        if (splitImmediately) {
+            /* won't fit in litExtraBuffer, so it will be split between end of dst and extra buffer */
+            dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize + ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH;
+            dctx->litBufferEnd = dctx->litBuffer + litSize - ZSTD_LITBUFFEREXTRASIZE;
+        }
+        else {
+            /* initially this will be stored entirely in dst during huffman decoding, it will partially be shifted to litExtraBuffer after */
+            dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize;
+            dctx->litBufferEnd = (BYTE*)dst + expectedWriteSize;
+        }
+        dctx->litBufferLocation = ZSTD_split;
+    }
+    else
+    {
+        /* fits entirely within litExtraBuffer, so no split is necessary */
+        dctx->litBuffer = dctx->litExtraBuffer;
+        dctx->litBufferEnd = dctx->litBuffer + litSize;
+        dctx->litBufferLocation = ZSTD_not_in_dst;
+    }
+}
 
 /* Hidden declaration for fullbench */
 size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
-                          const void* src, size_t srcSize);
+                          const void* src, size_t srcSize,
+                          void* dst, size_t dstCapacity, const streaming_operation streaming);
 /*! ZSTD_decodeLiteralsBlock() :
+ * Where it is possible to do so without being stomped by the output during decompression, the literals block will be stored
+ * in the dstBuffer.  If there is room to do so, it will be stored in full in the excess dst space after where the current
+ * block will be output.  Otherwise it will be stored at the end of the current dst blockspace, with a small portion being
+ * stored in dctx->litExtraBuffer to help keep it "ahead" of the current output write.
+ *
  * @return : nb of bytes read from src (< srcSize )
  *  note : symbol not declared but exposed for fullbench */
 size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
-                          const void* src, size_t srcSize)   /* note : srcSize < BLOCKSIZE */
+                          const void* src, size_t srcSize,   /* note : srcSize < BLOCKSIZE */
+                          void* dst, size_t dstCapacity, const streaming_operation streaming)
 {
     DEBUGLOG(5, "ZSTD_decodeLiteralsBlock");
     RETURN_ERROR_IF(srcSize < MIN_CBLOCK_SIZE, corruption_detected, "");
@@ -90,15 +131,19 @@
         case set_repeat:
             DEBUGLOG(5, "set_repeat flag : re-using stats from previous compressed literals block");
             RETURN_ERROR_IF(dctx->litEntropy==0, dictionary_corrupted, "");
-            /* fall-through */
+            ZSTD_FALLTHROUGH;
 
         case set_compressed:
-            RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3");
+            RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need up to 5 for case 3");
             {   size_t lhSize, litSize, litCSize;
                 U32 singleStream=0;
                 U32 const lhlCode = (istart[0] >> 2) & 3;
                 U32 const lhc = MEM_readLE32(istart);
                 size_t hufSuccess;
+                size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity);
+                int const flags = 0
+                    | (ZSTD_DCtx_get_bmi2(dctx) ? HUF_flags_bmi2 : 0)
+                    | (dctx->disableHufAsm ? HUF_flags_disableAsm : 0);
                 switch(lhlCode)
                 {
                 case 0: case 1: default:   /* note : default is impossible, since lhlCode into [0..3] */
@@ -121,8 +166,15 @@
                     litCSize = (lhc >> 22) + ((size_t)istart[4] << 10);
                     break;
                 }
+                RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled");
                 RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, "");
+                if (!singleStream)
+                    RETURN_ERROR_IF(litSize < MIN_LITERALS_FOR_4_STREAMS, literals_headerWrong,
+                        "Not enough literals (%zu) for the 4-streams mode (min %u)",
+                        litSize, MIN_LITERALS_FOR_4_STREAMS);
                 RETURN_ERROR_IF(litCSize + lhSize > srcSize, corruption_detected, "");
+                RETURN_ERROR_IF(expectedWriteSize < litSize , dstSize_tooSmall, "");
+                ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 0);
 
                 /* prefetch huffman table if cold */
                 if (dctx->ddictIsCold && (litSize > 768 /* heuristic */)) {
@@ -131,13 +183,14 @@
 
                 if (litEncType==set_repeat) {
                     if (singleStream) {
-                        hufSuccess = HUF_decompress1X_usingDTable_bmi2(
+                        hufSuccess = HUF_decompress1X_usingDTable(
                             dctx->litBuffer, litSize, istart+lhSize, litCSize,
-                            dctx->HUFptr, dctx->bmi2);
+                            dctx->HUFptr, flags);
                     } else {
-                        hufSuccess = HUF_decompress4X_usingDTable_bmi2(
+                        assert(litSize >= MIN_LITERALS_FOR_4_STREAMS);
+                        hufSuccess = HUF_decompress4X_usingDTable(
                             dctx->litBuffer, litSize, istart+lhSize, litCSize,
-                            dctx->HUFptr, dctx->bmi2);
+                            dctx->HUFptr, flags);
                     }
                 } else {
                     if (singleStream) {
@@ -145,20 +198,27 @@
                         hufSuccess = HUF_decompress1X_DCtx_wksp(
                             dctx->entropy.hufTable, dctx->litBuffer, litSize,
                             istart+lhSize, litCSize, dctx->workspace,
-                            sizeof(dctx->workspace));
+                            sizeof(dctx->workspace), flags);
 #else
-                        hufSuccess = HUF_decompress1X1_DCtx_wksp_bmi2(
+                        hufSuccess = HUF_decompress1X1_DCtx_wksp(
                             dctx->entropy.hufTable, dctx->litBuffer, litSize,
                             istart+lhSize, litCSize, dctx->workspace,
-                            sizeof(dctx->workspace), dctx->bmi2);
+                            sizeof(dctx->workspace), flags);
 #endif
                     } else {
-                        hufSuccess = HUF_decompress4X_hufOnly_wksp_bmi2(
+                        hufSuccess = HUF_decompress4X_hufOnly_wksp(
                             dctx->entropy.hufTable, dctx->litBuffer, litSize,
                             istart+lhSize, litCSize, dctx->workspace,
-                            sizeof(dctx->workspace), dctx->bmi2);
+                            sizeof(dctx->workspace), flags);
                     }
                 }
+                if (dctx->litBufferLocation == ZSTD_split)
+                {
+                    ZSTD_memcpy(dctx->litExtraBuffer, dctx->litBufferEnd - ZSTD_LITBUFFEREXTRASIZE, ZSTD_LITBUFFEREXTRASIZE);
+                    ZSTD_memmove(dctx->litBuffer + ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH, dctx->litBuffer, litSize - ZSTD_LITBUFFEREXTRASIZE);
+                    dctx->litBuffer += ZSTD_LITBUFFEREXTRASIZE - WILDCOPY_OVERLENGTH;
+                    dctx->litBufferEnd -= WILDCOPY_OVERLENGTH;
+                }
 
                 RETURN_ERROR_IF(HUF_isError(hufSuccess), corruption_detected, "");
 
@@ -166,13 +226,13 @@
                 dctx->litSize = litSize;
                 dctx->litEntropy = 1;
                 if (litEncType==set_compressed) dctx->HUFptr = dctx->entropy.hufTable;
-                ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
                 return litCSize + lhSize;
             }
 
         case set_basic:
             {   size_t litSize, lhSize;
                 U32 const lhlCode = ((istart[0]) >> 2) & 3;
+                size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity);
                 switch(lhlCode)
                 {
                 case 0: case 2: default:   /* note : default is impossible, since lhlCode into [0..3] */
@@ -185,27 +245,41 @@
                     break;
                 case 3:
                     lhSize = 3;
+                    RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize = 3");
                     litSize = MEM_readLE24(istart) >> 4;
                     break;
                 }
 
+                RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled");
+                RETURN_ERROR_IF(expectedWriteSize < litSize, dstSize_tooSmall, "");
+                ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1);
                 if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) {  /* risk reading beyond src buffer with wildcopy */
                     RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected, "");
-                    ZSTD_memcpy(dctx->litBuffer, istart+lhSize, litSize);
+                    if (dctx->litBufferLocation == ZSTD_split)
+                    {
+                        ZSTD_memcpy(dctx->litBuffer, istart + lhSize, litSize - ZSTD_LITBUFFEREXTRASIZE);
+                        ZSTD_memcpy(dctx->litExtraBuffer, istart + lhSize + litSize - ZSTD_LITBUFFEREXTRASIZE, ZSTD_LITBUFFEREXTRASIZE);
+                    }
+                    else
+                    {
+                        ZSTD_memcpy(dctx->litBuffer, istart + lhSize, litSize);
+                    }
                     dctx->litPtr = dctx->litBuffer;
                     dctx->litSize = litSize;
-                    ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
                     return lhSize+litSize;
                 }
                 /* direct reference into compressed stream */
                 dctx->litPtr = istart+lhSize;
                 dctx->litSize = litSize;
+                dctx->litBufferEnd = dctx->litPtr + litSize;
+                dctx->litBufferLocation = ZSTD_not_in_dst;
                 return lhSize+litSize;
             }
 
         case set_rle:
             {   U32 const lhlCode = ((istart[0]) >> 2) & 3;
                 size_t litSize, lhSize;
+                size_t expectedWriteSize = MIN(ZSTD_BLOCKSIZE_MAX, dstCapacity);
                 switch(lhlCode)
                 {
                 case 0: case 2: default:   /* note : default is impossible, since lhlCode into [0..3] */
@@ -214,16 +288,28 @@
                     break;
                 case 1:
                     lhSize = 2;
+                    RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 3");
                     litSize = MEM_readLE16(istart) >> 4;
                     break;
                 case 3:
                     lhSize = 3;
+                    RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 4");
                     litSize = MEM_readLE24(istart) >> 4;
-                    RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4");
                     break;
                 }
+                RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled");
                 RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, "");
-                ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH);
+                RETURN_ERROR_IF(expectedWriteSize < litSize, dstSize_tooSmall, "");
+                ZSTD_allocateLiteralsBuffer(dctx, dst, dstCapacity, litSize, streaming, expectedWriteSize, 1);
+                if (dctx->litBufferLocation == ZSTD_split)
+                {
+                    ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize - ZSTD_LITBUFFEREXTRASIZE);
+                    ZSTD_memset(dctx->litExtraBuffer, istart[lhSize], ZSTD_LITBUFFEREXTRASIZE);
+                }
+                else
+                {
+                    ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize);
+                }
                 dctx->litPtr = dctx->litBuffer;
                 dctx->litSize = litSize;
                 return lhSize+1;
@@ -343,7 +429,7 @@
 };   /* ML_defaultDTable */
 
 
-static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U32 nbAddBits)
+static void ZSTD_buildSeqTable_rle(ZSTD_seqSymbol* dt, U32 baseValue, U8 nbAddBits)
 {
     void* ptr = dt;
     ZSTD_seqSymbol_header* const DTableH = (ZSTD_seqSymbol_header*)ptr;
@@ -355,7 +441,7 @@
     cell->nbBits = 0;
     cell->nextState = 0;
     assert(nbAddBits < 255);
-    cell->nbAdditionalBits = (BYTE)nbAddBits;
+    cell->nbAdditionalBits = nbAddBits;
     cell->baseValue = baseValue;
 }
 
@@ -367,7 +453,7 @@
 FORCE_INLINE_TEMPLATE
 void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt,
             const short* normalizedCounter, unsigned maxSymbolValue,
-            const U32* baseValue, const U32* nbAdditionalBits,
+            const U32* baseValue, const U8* nbAdditionalBits,
             unsigned tableLog, void* wksp, size_t wkspSize)
 {
     ZSTD_seqSymbol* const tableDecode = dt+1;
@@ -430,14 +516,15 @@
                 for (i = 8; i < n; i += 8) {
                     MEM_write64(spread + pos + i, sv);
                 }
-                pos += n;
+                assert(n>=0);
+                pos += (size_t)n;
             }
         }
         /* Now we spread those positions across the table.
-         * The benefit of doing it in two stages is that we avoid the the
+         * The benefit of doing it in two stages is that we avoid the
          * variable size inner loop, which caused lots of branch misses.
          * Now we can run through all the positions without any branch misses.
-         * We unroll the loop twice, since that is what emperically worked best.
+         * We unroll the loop twice, since that is what empirically worked best.
          */
         {
             size_t position = 0;
@@ -464,7 +551,7 @@
             for (i=0; i<n; i++) {
                 tableDecode[position].baseValue = s;
                 position = (position + step) & tableMask;
-                while (position > highThreshold) position = (position + step) & tableMask;   /* lowprob area */
+                while (UNLIKELY(position > highThreshold)) position = (position + step) & tableMask;   /* lowprob area */
         }   }
         assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
     }
@@ -475,10 +562,10 @@
         for (u=0; u<tableSize; u++) {
             U32 const symbol = tableDecode[u].baseValue;
             U32 const nextState = symbolNext[symbol]++;
-            tableDecode[u].nbBits = (BYTE) (tableLog - BIT_highbit32(nextState) );
+            tableDecode[u].nbBits = (BYTE) (tableLog - ZSTD_highbit32(nextState) );
             tableDecode[u].nextState = (U16) ( (nextState << tableDecode[u].nbBits) - tableSize);
             assert(nbAdditionalBits[symbol] < 255);
-            tableDecode[u].nbAdditionalBits = (BYTE)nbAdditionalBits[symbol];
+            tableDecode[u].nbAdditionalBits = nbAdditionalBits[symbol];
             tableDecode[u].baseValue = baseValue[symbol];
         }
     }
@@ -487,7 +574,7 @@
 /* Avoids the FORCE_INLINE of the _body() function. */
 static void ZSTD_buildFSETable_body_default(ZSTD_seqSymbol* dt,
             const short* normalizedCounter, unsigned maxSymbolValue,
-            const U32* baseValue, const U32* nbAdditionalBits,
+            const U32* baseValue, const U8* nbAdditionalBits,
             unsigned tableLog, void* wksp, size_t wkspSize)
 {
     ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue,
@@ -495,9 +582,9 @@
 }
 
 #if DYNAMIC_BMI2
-TARGET_ATTRIBUTE("bmi2") static void ZSTD_buildFSETable_body_bmi2(ZSTD_seqSymbol* dt,
+BMI2_TARGET_ATTRIBUTE static void ZSTD_buildFSETable_body_bmi2(ZSTD_seqSymbol* dt,
             const short* normalizedCounter, unsigned maxSymbolValue,
-            const U32* baseValue, const U32* nbAdditionalBits,
+            const U32* baseValue, const U8* nbAdditionalBits,
             unsigned tableLog, void* wksp, size_t wkspSize)
 {
     ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue,
@@ -507,7 +594,7 @@
 
 void ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
             const short* normalizedCounter, unsigned maxSymbolValue,
-            const U32* baseValue, const U32* nbAdditionalBits,
+            const U32* baseValue, const U8* nbAdditionalBits,
             unsigned tableLog, void* wksp, size_t wkspSize, int bmi2)
 {
 #if DYNAMIC_BMI2
@@ -529,7 +616,7 @@
 static size_t ZSTD_buildSeqTable(ZSTD_seqSymbol* DTableSpace, const ZSTD_seqSymbol** DTablePtr,
                                  symbolEncodingType_e type, unsigned max, U32 maxLog,
                                  const void* src, size_t srcSize,
-                                 const U32* baseValue, const U32* nbAdditionalBits,
+                                 const U32* baseValue, const U8* nbAdditionalBits,
                                  const ZSTD_seqSymbol* defaultTable, U32 flagRepeatTable,
                                  int ddictIsCold, int nbSeq, U32* wksp, size_t wkspSize,
                                  int bmi2)
@@ -541,7 +628,7 @@
         RETURN_ERROR_IF((*(const BYTE*)src) > max, corruption_detected, "");
         {   U32 const symbol = *(const BYTE*)src;
             U32 const baseline = baseValue[symbol];
-            U32 const nbBits = nbAdditionalBits[symbol];
+            U8 const nbBits = nbAdditionalBits[symbol];
             ZSTD_buildSeqTable_rle(DTableSpace, baseline, nbBits);
         }
         *DTablePtr = DTableSpace;
@@ -620,7 +707,7 @@
                                                       LL_defaultDTable, dctx->fseEntropy,
                                                       dctx->ddictIsCold, nbSeq,
                                                       dctx->workspace, sizeof(dctx->workspace),
-                                                      dctx->bmi2);
+                                                      ZSTD_DCtx_get_bmi2(dctx));
             RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += llhSize;
         }
@@ -632,7 +719,7 @@
                                                       OF_defaultDTable, dctx->fseEntropy,
                                                       dctx->ddictIsCold, nbSeq,
                                                       dctx->workspace, sizeof(dctx->workspace),
-                                                      dctx->bmi2);
+                                                      ZSTD_DCtx_get_bmi2(dctx));
             RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += ofhSize;
         }
@@ -644,7 +731,7 @@
                                                       ML_defaultDTable, dctx->fseEntropy,
                                                       dctx->ddictIsCold, nbSeq,
                                                       dctx->workspace, sizeof(dctx->workspace),
-                                                      dctx->bmi2);
+                                                      ZSTD_DCtx_get_bmi2(dctx));
             RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += mlhSize;
         }
@@ -713,7 +800,7 @@
  *         - ZSTD_overlap_src_before_dst: The src and dst may overlap and may be any distance apart.
  *           The src buffer must be before the dst buffer.
  */
-static void ZSTD_safecopy(BYTE* op, BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) {
+static void ZSTD_safecopy(BYTE* op, const BYTE* const oend_w, BYTE const* ip, ptrdiff_t length, ZSTD_overlap_e ovtype) {
     ptrdiff_t const diff = op - ip;
     BYTE* const oend = op + length;
 
@@ -729,6 +816,7 @@
         /* Copy 8 bytes and ensure the offset >= 8 when there can be overlap. */
         assert(length >= 8);
         ZSTD_overlapCopy8(&op, &ip, diff);
+        length -= 8;
         assert(op - ip >= 8);
         assert(op <= oend);
     }
@@ -743,12 +831,35 @@
         assert(oend > oend_w);
         ZSTD_wildcopy(op, ip, oend_w - op, ovtype);
         ip += oend_w - op;
-        op = oend_w;
+        op += oend_w - op;
     }
     /* Handle the leftovers. */
     while (op < oend) *op++ = *ip++;
 }
 
+/* ZSTD_safecopyDstBeforeSrc():
+ * This version allows overlap with dst before src, or handles the non-overlap case with dst after src
+ * Kept separate from more common ZSTD_safecopy case to avoid performance impact to the safecopy common case */
+static void ZSTD_safecopyDstBeforeSrc(BYTE* op, BYTE const* ip, ptrdiff_t length) {
+    ptrdiff_t const diff = op - ip;
+    BYTE* const oend = op + length;
+
+    if (length < 8 || diff > -8) {
+        /* Handle short lengths, close overlaps, and dst not before src. */
+        while (op < oend) *op++ = *ip++;
+        return;
+    }
+
+    if (op <= oend - WILDCOPY_OVERLENGTH && diff < -WILDCOPY_VECLEN) {
+        ZSTD_wildcopy(op, ip, oend - WILDCOPY_OVERLENGTH - op, ZSTD_no_overlap);
+        ip += oend - WILDCOPY_OVERLENGTH - op;
+        op += oend - WILDCOPY_OVERLENGTH - op;
+    }
+
+    /* Handle the leftovers. */
+    while (op < oend) *op++ = *ip++;
+}
+
 /* ZSTD_execSequenceEnd():
  * This version handles cases that are near the end of the output buffer. It requires
  * more careful checks to make sure there is no overflow. By separating out these hard
@@ -759,9 +870,9 @@
  */
 FORCE_NOINLINE
 size_t ZSTD_execSequenceEnd(BYTE* op,
-                            BYTE* const oend, seq_t sequence,
-                            const BYTE** litPtr, const BYTE* const litLimit,
-                            const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
+    BYTE* const oend, seq_t sequence,
+    const BYTE** litPtr, const BYTE* const litLimit,
+    const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
 {
     BYTE* const oLitEnd = op + sequence.litLength;
     size_t const sequenceLength = sequence.litLength + sequence.matchLength;
@@ -784,27 +895,76 @@
     if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
         /* offset beyond prefix */
         RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, "");
-        match = dictEnd - (prefixStart-match);
+        match = dictEnd - (prefixStart - match);
         if (match + sequence.matchLength <= dictEnd) {
             ZSTD_memmove(oLitEnd, match, sequence.matchLength);
             return sequenceLength;
         }
         /* span extDict & currentPrefixSegment */
         {   size_t const length1 = dictEnd - match;
-            ZSTD_memmove(oLitEnd, match, length1);
-            op = oLitEnd + length1;
-            sequence.matchLength -= length1;
-            match = prefixStart;
-    }   }
+        ZSTD_memmove(oLitEnd, match, length1);
+        op = oLitEnd + length1;
+        sequence.matchLength -= length1;
+        match = prefixStart;
+        }
+    }
+    ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst);
+    return sequenceLength;
+}
+
+/* ZSTD_execSequenceEndSplitLitBuffer():
+ * This version is intended to be used during instances where the litBuffer is still split.  It is kept separate to avoid performance impact for the good case.
+ */
+FORCE_NOINLINE
+size_t ZSTD_execSequenceEndSplitLitBuffer(BYTE* op,
+    BYTE* const oend, const BYTE* const oend_w, seq_t sequence,
+    const BYTE** litPtr, const BYTE* const litLimit,
+    const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
+{
+    BYTE* const oLitEnd = op + sequence.litLength;
+    size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+    const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+    const BYTE* match = oLitEnd - sequence.offset;
+
+
+    /* bounds checks : careful of address space overflow in 32-bit mode */
+    RETURN_ERROR_IF(sequenceLength > (size_t)(oend - op), dstSize_tooSmall, "last match must fit within dstBuffer");
+    RETURN_ERROR_IF(sequence.litLength > (size_t)(litLimit - *litPtr), corruption_detected, "try to read beyond literal buffer");
+    assert(op < op + sequenceLength);
+    assert(oLitEnd < op + sequenceLength);
+
+    /* copy literals */
+    RETURN_ERROR_IF(op > *litPtr && op < *litPtr + sequence.litLength, dstSize_tooSmall, "output should not catch up to and overwrite literal buffer");
+    ZSTD_safecopyDstBeforeSrc(op, *litPtr, sequence.litLength);
+    op = oLitEnd;
+    *litPtr = iLitEnd;
+
+    /* copy Match */
+    if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
+        /* offset beyond prefix */
+        RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, "");
+        match = dictEnd - (prefixStart - match);
+        if (match + sequence.matchLength <= dictEnd) {
+            ZSTD_memmove(oLitEnd, match, sequence.matchLength);
+            return sequenceLength;
+        }
+        /* span extDict & currentPrefixSegment */
+        {   size_t const length1 = dictEnd - match;
+        ZSTD_memmove(oLitEnd, match, length1);
+        op = oLitEnd + length1;
+        sequence.matchLength -= length1;
+        match = prefixStart;
+        }
+    }
     ZSTD_safecopy(op, oend_w, match, sequence.matchLength, ZSTD_overlap_src_before_dst);
     return sequenceLength;
 }
 
 HINT_INLINE
 size_t ZSTD_execSequence(BYTE* op,
-                         BYTE* const oend, seq_t sequence,
-                         const BYTE** litPtr, const BYTE* const litLimit,
-                         const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
+    BYTE* const oend, seq_t sequence,
+    const BYTE** litPtr, const BYTE* const litLimit,
+    const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
 {
     BYTE* const oLitEnd = op + sequence.litLength;
     size_t const sequenceLength = sequence.litLength + sequence.matchLength;
@@ -815,6 +975,103 @@
 
     assert(op != NULL /* Precondition */);
     assert(oend_w < oend /* No underflow */);
+
+#if defined(__aarch64__)
+    /* prefetch sequence starting from match that will be used for copy later */
+    PREFETCH_L1(match);
+#endif
+    /* Handle edge cases in a slow path:
+     *   - Read beyond end of literals
+     *   - Match end is within WILDCOPY_OVERLIMIT of oend
+     *   - 32-bit mode and the match length overflows
+     */
+    if (UNLIKELY(
+        iLitEnd > litLimit ||
+        oMatchEnd > oend_w ||
+        (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH)))
+        return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
+
+    /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */
+    assert(op <= oLitEnd /* No overflow */);
+    assert(oLitEnd < oMatchEnd /* Non-zero match & no overflow */);
+    assert(oMatchEnd <= oend /* No underflow */);
+    assert(iLitEnd <= litLimit /* Literal length is in bounds */);
+    assert(oLitEnd <= oend_w /* Can wildcopy literals */);
+    assert(oMatchEnd <= oend_w /* Can wildcopy matches */);
+
+    /* Copy Literals:
+     * Split out litLength <= 16 since it is nearly always true. +1.6% on gcc-9.
+     * We likely don't need the full 32-byte wildcopy.
+     */
+    assert(WILDCOPY_OVERLENGTH >= 16);
+    ZSTD_copy16(op, (*litPtr));
+    if (UNLIKELY(sequence.litLength > 16)) {
+        ZSTD_wildcopy(op + 16, (*litPtr) + 16, sequence.litLength - 16, ZSTD_no_overlap);
+    }
+    op = oLitEnd;
+    *litPtr = iLitEnd;   /* update for next sequence */
+
+    /* Copy Match */
+    if (sequence.offset > (size_t)(oLitEnd - prefixStart)) {
+        /* offset beyond prefix -> go into extDict */
+        RETURN_ERROR_IF(UNLIKELY(sequence.offset > (size_t)(oLitEnd - virtualStart)), corruption_detected, "");
+        match = dictEnd + (match - prefixStart);
+        if (match + sequence.matchLength <= dictEnd) {
+            ZSTD_memmove(oLitEnd, match, sequence.matchLength);
+            return sequenceLength;
+        }
+        /* span extDict & currentPrefixSegment */
+        {   size_t const length1 = dictEnd - match;
+        ZSTD_memmove(oLitEnd, match, length1);
+        op = oLitEnd + length1;
+        sequence.matchLength -= length1;
+        match = prefixStart;
+        }
+    }
+    /* Match within prefix of 1 or more bytes */
+    assert(op <= oMatchEnd);
+    assert(oMatchEnd <= oend_w);
+    assert(match >= prefixStart);
+    assert(sequence.matchLength >= 1);
+
+    /* Nearly all offsets are >= WILDCOPY_VECLEN bytes, which means we can use wildcopy
+     * without overlap checking.
+     */
+    if (LIKELY(sequence.offset >= WILDCOPY_VECLEN)) {
+        /* We bet on a full wildcopy for matches, since we expect matches to be
+         * longer than literals (in general). In silesia, ~10% of matches are longer
+         * than 16 bytes.
+         */
+        ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength, ZSTD_no_overlap);
+        return sequenceLength;
+    }
+    assert(sequence.offset < WILDCOPY_VECLEN);
+
+    /* Copy 8 bytes and spread the offset to be >= 8. */
+    ZSTD_overlapCopy8(&op, &match, sequence.offset);
+
+    /* If the match length is > 8 bytes, then continue with the wildcopy. */
+    if (sequence.matchLength > 8) {
+        assert(op < oMatchEnd);
+        ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8, ZSTD_overlap_src_before_dst);
+    }
+    return sequenceLength;
+}
+
+HINT_INLINE
+size_t ZSTD_execSequenceSplitLitBuffer(BYTE* op,
+    BYTE* const oend, const BYTE* const oend_w, seq_t sequence,
+    const BYTE** litPtr, const BYTE* const litLimit,
+    const BYTE* const prefixStart, const BYTE* const virtualStart, const BYTE* const dictEnd)
+{
+    BYTE* const oLitEnd = op + sequence.litLength;
+    size_t const sequenceLength = sequence.litLength + sequence.matchLength;
+    BYTE* const oMatchEnd = op + sequenceLength;   /* risk : address space overflow (32-bits) */
+    const BYTE* const iLitEnd = *litPtr + sequence.litLength;
+    const BYTE* match = oLitEnd - sequence.offset;
+
+    assert(op != NULL /* Precondition */);
+    assert(oend_w < oend /* No underflow */);
     /* Handle edge cases in a slow path:
      *   - Read beyond end of literals
      *   - Match end is within WILDCOPY_OVERLIMIT of oend
@@ -824,7 +1081,7 @@
             iLitEnd > litLimit ||
             oMatchEnd > oend_w ||
             (MEM_32bits() && (size_t)(oend - op) < sequenceLength + WILDCOPY_OVERLENGTH)))
-        return ZSTD_execSequenceEnd(op, oend, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
+        return ZSTD_execSequenceEndSplitLitBuffer(op, oend, oend_w, sequence, litPtr, litLimit, prefixStart, virtualStart, dictEnd);
 
     /* Assumptions (everything else goes into ZSTD_execSequenceEnd()) */
     assert(op <= oLitEnd /* No overflow */);
@@ -892,6 +1149,7 @@
     return sequenceLength;
 }
 
+
 static void
 ZSTD_initFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, const ZSTD_seqSymbol* dt)
 {
@@ -905,24 +1163,14 @@
 }
 
 FORCE_INLINE_TEMPLATE void
-ZSTD_updateFseState(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD)
+ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, U16 nextState, U32 nbBits)
 {
-    ZSTD_seqSymbol const DInfo = DStatePtr->table[DStatePtr->state];
-    U32 const nbBits = DInfo.nbBits;
     size_t const lowBits = BIT_readBits(bitD, nbBits);
-    DStatePtr->state = DInfo.nextState + lowBits;
-}
-
-FORCE_INLINE_TEMPLATE void
-ZSTD_updateFseStateWithDInfo(ZSTD_fseState* DStatePtr, BIT_DStream_t* bitD, ZSTD_seqSymbol const DInfo)
-{
-    U32 const nbBits = DInfo.nbBits;
-    size_t const lowBits = BIT_readBits(bitD, nbBits);
-    DStatePtr->state = DInfo.nextState + lowBits;
+    DStatePtr->state = nextState + lowBits;
 }
 
 /* We need to add at most (ZSTD_WINDOWLOG_MAX_32 - 1) bits to read the maximum
- * offset bits. But we can only read at most (STREAM_ACCUMULATOR_MIN_32 - 1)
+ * offset bits. But we can only read at most STREAM_ACCUMULATOR_MIN_32
  * bits before reloading. This value is the maximum number of bytes we read
  * after reloading when we are decoding long offsets.
  */
@@ -937,102 +1185,113 @@
 ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
 {
     seq_t seq;
-    ZSTD_seqSymbol const llDInfo = seqState->stateLL.table[seqState->stateLL.state];
-    ZSTD_seqSymbol const mlDInfo = seqState->stateML.table[seqState->stateML.state];
-    ZSTD_seqSymbol const ofDInfo = seqState->stateOffb.table[seqState->stateOffb.state];
-    U32 const llBase = llDInfo.baseValue;
-    U32 const mlBase = mlDInfo.baseValue;
-    U32 const ofBase = ofDInfo.baseValue;
-    BYTE const llBits = llDInfo.nbAdditionalBits;
-    BYTE const mlBits = mlDInfo.nbAdditionalBits;
-    BYTE const ofBits = ofDInfo.nbAdditionalBits;
-    BYTE const totalBits = llBits+mlBits+ofBits;
-
-    /* sequence */
-    {   size_t offset;
-        if (ofBits > 1) {
-            ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
-            ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
-            assert(ofBits <= MaxOff);
-            if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) {
-                U32 const extraBits = ofBits - MIN(ofBits, 32 - seqState->DStream.bitsConsumed);
-                offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
-                BIT_reloadDStream(&seqState->DStream);
-                if (extraBits) offset += BIT_readBitsFast(&seqState->DStream, extraBits);
-                assert(extraBits <= LONG_OFFSETS_MAX_EXTRA_BITS_32);   /* to avoid another reload */
-            } else {
-                offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/);   /* <=  (ZSTD_WINDOWLOG_MAX-1) bits */
-                if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
-            }
-            seqState->prevOffset[2] = seqState->prevOffset[1];
-            seqState->prevOffset[1] = seqState->prevOffset[0];
-            seqState->prevOffset[0] = offset;
-        } else {
-            U32 const ll0 = (llBase == 0);
-            if (LIKELY((ofBits == 0))) {
-                if (LIKELY(!ll0))
-                    offset = seqState->prevOffset[0];
-                else {
-                    offset = seqState->prevOffset[1];
-                    seqState->prevOffset[1] = seqState->prevOffset[0];
-                    seqState->prevOffset[0] = offset;
-                }
-            } else {
-                offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1);
-                {   size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
-                    temp += !temp;   /* 0 is not valid; input is corrupted; force offset to 1 */
-                    if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
-                    seqState->prevOffset[1] = seqState->prevOffset[0];
-                    seqState->prevOffset[0] = offset = temp;
-        }   }   }
-        seq.offset = offset;
-    }
-
-    seq.matchLength = mlBase;
-    if (mlBits > 0)
-        seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/);
-
-    if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
-        BIT_reloadDStream(&seqState->DStream);
-    if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
-        BIT_reloadDStream(&seqState->DStream);
-    /* Ensure there are enough bits to read the rest of data in 64-bit mode. */
-    ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
-
-    seq.litLength = llBase;
-    if (llBits > 0)
-        seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/);
-
-    if (MEM_32bits())
-        BIT_reloadDStream(&seqState->DStream);
-
-    DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
-                (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
-
-    /* ANS state update
-     * gcc-9.0.0 does 2.5% worse with ZSTD_updateFseStateWithDInfo().
-     * clang-9.2.0 does 7% worse with ZSTD_updateFseState().
-     * Naturally it seems like ZSTD_updateFseStateWithDInfo() should be the
-     * better option, so it is the default for other compilers. But, if you
-     * measure that it is worse, please put up a pull request.
+    /*
+     * ZSTD_seqSymbol is a structure with a total of 64 bits wide. So it can be
+     * loaded in one operation and extracted its fields by simply shifting or
+     * bit-extracting on aarch64.
+     * GCC doesn't recognize this and generates more unnecessary ldr/ldrb/ldrh
+     * operations that cause performance drop. This can be avoided by using this
+     * ZSTD_memcpy hack.
      */
-    {
-#if defined(__GNUC__) && !defined(__clang__)
-        const int kUseUpdateFseState = 1;
+#if defined(__aarch64__) && (defined(__GNUC__) && !defined(__clang__))
+    ZSTD_seqSymbol llDInfoS, mlDInfoS, ofDInfoS;
+    ZSTD_seqSymbol* const llDInfo = &llDInfoS;
+    ZSTD_seqSymbol* const mlDInfo = &mlDInfoS;
+    ZSTD_seqSymbol* const ofDInfo = &ofDInfoS;
+    ZSTD_memcpy(llDInfo, seqState->stateLL.table + seqState->stateLL.state, sizeof(ZSTD_seqSymbol));
+    ZSTD_memcpy(mlDInfo, seqState->stateML.table + seqState->stateML.state, sizeof(ZSTD_seqSymbol));
+    ZSTD_memcpy(ofDInfo, seqState->stateOffb.table + seqState->stateOffb.state, sizeof(ZSTD_seqSymbol));
 #else
-        const int kUseUpdateFseState = 0;
+    const ZSTD_seqSymbol* const llDInfo = seqState->stateLL.table + seqState->stateLL.state;
+    const ZSTD_seqSymbol* const mlDInfo = seqState->stateML.table + seqState->stateML.state;
+    const ZSTD_seqSymbol* const ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state;
 #endif
-        if (kUseUpdateFseState) {
-            ZSTD_updateFseState(&seqState->stateLL, &seqState->DStream);    /* <=  9 bits */
-            ZSTD_updateFseState(&seqState->stateML, &seqState->DStream);    /* <=  9 bits */
-            if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);    /* <= 18 bits */
-            ZSTD_updateFseState(&seqState->stateOffb, &seqState->DStream);  /* <=  8 bits */
-        } else {
-            ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llDInfo);    /* <=  9 bits */
-            ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlDInfo);    /* <=  9 bits */
-            if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);    /* <= 18 bits */
-            ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofDInfo);  /* <=  8 bits */
+    seq.matchLength = mlDInfo->baseValue;
+    seq.litLength = llDInfo->baseValue;
+    {   U32 const ofBase = ofDInfo->baseValue;
+        BYTE const llBits = llDInfo->nbAdditionalBits;
+        BYTE const mlBits = mlDInfo->nbAdditionalBits;
+        BYTE const ofBits = ofDInfo->nbAdditionalBits;
+        BYTE const totalBits = llBits+mlBits+ofBits;
+
+        U16 const llNext = llDInfo->nextState;
+        U16 const mlNext = mlDInfo->nextState;
+        U16 const ofNext = ofDInfo->nextState;
+        U32 const llnbBits = llDInfo->nbBits;
+        U32 const mlnbBits = mlDInfo->nbBits;
+        U32 const ofnbBits = ofDInfo->nbBits;
+
+        assert(llBits <= MaxLLBits);
+        assert(mlBits <= MaxMLBits);
+        assert(ofBits <= MaxOff);
+        /*
+         * As gcc has better branch and block analyzers, sometimes it is only
+         * valuable to mark likeliness for clang, it gives around 3-4% of
+         * performance.
+         */
+
+        /* sequence */
+        {   size_t offset;
+            if (ofBits > 1) {
+                ZSTD_STATIC_ASSERT(ZSTD_lo_isLongOffset == 1);
+                ZSTD_STATIC_ASSERT(LONG_OFFSETS_MAX_EXTRA_BITS_32 == 5);
+                ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 > LONG_OFFSETS_MAX_EXTRA_BITS_32);
+                ZSTD_STATIC_ASSERT(STREAM_ACCUMULATOR_MIN_32 - LONG_OFFSETS_MAX_EXTRA_BITS_32 >= MaxMLBits);
+                if (MEM_32bits() && longOffsets && (ofBits >= STREAM_ACCUMULATOR_MIN_32)) {
+                    /* Always read extra bits, this keeps the logic simple,
+                     * avoids branches, and avoids accidentally reading 0 bits.
+                     */
+                    U32 const extraBits = LONG_OFFSETS_MAX_EXTRA_BITS_32;
+                    offset = ofBase + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits);
+                    BIT_reloadDStream(&seqState->DStream);
+                    offset += BIT_readBitsFast(&seqState->DStream, extraBits);
+                } else {
+                    offset = ofBase + BIT_readBitsFast(&seqState->DStream, ofBits/*>0*/);   /* <=  (ZSTD_WINDOWLOG_MAX-1) bits */
+                    if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);
+                }
+                seqState->prevOffset[2] = seqState->prevOffset[1];
+                seqState->prevOffset[1] = seqState->prevOffset[0];
+                seqState->prevOffset[0] = offset;
+            } else {
+                U32 const ll0 = (llDInfo->baseValue == 0);
+                if (LIKELY((ofBits == 0))) {
+                    offset = seqState->prevOffset[ll0];
+                    seqState->prevOffset[1] = seqState->prevOffset[!ll0];
+                    seqState->prevOffset[0] = offset;
+                } else {
+                    offset = ofBase + ll0 + BIT_readBitsFast(&seqState->DStream, 1);
+                    {   size_t temp = (offset==3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset];
+                        temp += !temp;   /* 0 is not valid; input is corrupted; force offset to 1 */
+                        if (offset != 1) seqState->prevOffset[2] = seqState->prevOffset[1];
+                        seqState->prevOffset[1] = seqState->prevOffset[0];
+                        seqState->prevOffset[0] = offset = temp;
+            }   }   }
+            seq.offset = offset;
         }
+
+        if (mlBits > 0)
+            seq.matchLength += BIT_readBitsFast(&seqState->DStream, mlBits/*>0*/);
+
+        if (MEM_32bits() && (mlBits+llBits >= STREAM_ACCUMULATOR_MIN_32-LONG_OFFSETS_MAX_EXTRA_BITS_32))
+            BIT_reloadDStream(&seqState->DStream);
+        if (MEM_64bits() && UNLIKELY(totalBits >= STREAM_ACCUMULATOR_MIN_64-(LLFSELog+MLFSELog+OffFSELog)))
+            BIT_reloadDStream(&seqState->DStream);
+        /* Ensure there are enough bits to read the rest of data in 64-bit mode. */
+        ZSTD_STATIC_ASSERT(16+LLFSELog+MLFSELog+OffFSELog < STREAM_ACCUMULATOR_MIN_64);
+
+        if (llBits > 0)
+            seq.litLength += BIT_readBitsFast(&seqState->DStream, llBits/*>0*/);
+
+        if (MEM_32bits())
+            BIT_reloadDStream(&seqState->DStream);
+
+        DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
+                    (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
+
+        ZSTD_updateFseStateWithDInfo(&seqState->stateLL, &seqState->DStream, llNext, llnbBits);    /* <=  9 bits */
+        ZSTD_updateFseStateWithDInfo(&seqState->stateML, &seqState->DStream, mlNext, mlnbBits);    /* <=  9 bits */
+        if (MEM_32bits()) BIT_reloadDStream(&seqState->DStream);    /* <= 18 bits */
+        ZSTD_updateFseStateWithDInfo(&seqState->stateOffb, &seqState->DStream, ofNext, ofnbBits);  /* <=  8 bits */
     }
 
     return seq;
@@ -1085,9 +1344,11 @@
 #endif
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
+
+
 FORCE_INLINE_TEMPLATE size_t
 DONT_VECTORIZE
-ZSTD_decompressSequences_body( ZSTD_DCtx* dctx,
+ZSTD_decompressSequences_bodySplitLitBuffer( ZSTD_DCtx* dctx,
                                void* dst, size_t maxDstSize,
                          const void* seqStart, size_t seqSize, int nbSeq,
                          const ZSTD_longOffset_e isLongOffset,
@@ -1099,11 +1360,11 @@
     BYTE* const oend = ostart + maxDstSize;
     BYTE* op = ostart;
     const BYTE* litPtr = dctx->litPtr;
-    const BYTE* const litEnd = litPtr + dctx->litSize;
+    const BYTE* litBufferEnd = dctx->litBufferEnd;
     const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart);
     const BYTE* const vBase = (const BYTE*) (dctx->virtualStart);
     const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
-    DEBUGLOG(5, "ZSTD_decompressSequences_body");
+    DEBUGLOG(5, "ZSTD_decompressSequences_bodySplitLitBuffer");
     (void)frame;
 
     /* Regen sequences */
@@ -1124,55 +1385,237 @@
                 BIT_DStream_endOfBuffer < BIT_DStream_completed &&
                 BIT_DStream_completed < BIT_DStream_overflow);
 
+        /* decompress without overrunning litPtr begins */
+        {
+            seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+            /* Align the decompression loop to 32 + 16 bytes.
+                *
+                * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression
+                * speed swings based on the alignment of the decompression loop. This
+                * performance swing is caused by parts of the decompression loop falling
+                * out of the DSB. The entire decompression loop should fit in the DSB,
+                * when it can't we get much worse performance. You can measure if you've
+                * hit the good case or the bad case with this perf command for some
+                * compressed file test.zst:
+                *
+                *   perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \
+                *             -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst
+                *
+                * If you see most cycles served out of the MITE you've hit the bad case.
+                * If you see most cycles served out of the DSB you've hit the good case.
+                * If it is pretty even then you may be in an okay case.
+                *
+                * This issue has been reproduced on the following CPUs:
+                *   - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9
+                *               Use Instruments->Counters to get DSB/MITE cycles.
+                *               I never got performance swings, but I was able to
+                *               go from the good case of mostly DSB to half of the
+                *               cycles served from MITE.
+                *   - Coffeelake: Intel i9-9900k
+                *   - Coffeelake: Intel i7-9700k
+                *
+                * I haven't been able to reproduce the instability or DSB misses on any
+                * of the following CPUS:
+                *   - Haswell
+                *   - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH
+                *   - Skylake
+                *
+                * Alignment is done for each of the three major decompression loops:
+                *   - ZSTD_decompressSequences_bodySplitLitBuffer - presplit section of the literal buffer
+                *   - ZSTD_decompressSequences_bodySplitLitBuffer - postsplit section of the literal buffer
+                *   - ZSTD_decompressSequences_body
+                * Alignment choices are made to minimize large swings on bad cases and influence on performance
+                * from changes external to this code, rather than to overoptimize on the current commit.
+                *
+                * If you are seeing performance stability this script can help test.
+                * It tests on 4 commits in zstd where I saw performance change.
+                *
+                *   https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4
+                */
 #if defined(__GNUC__) && defined(__x86_64__)
-        /* Align the decompression loop to 32 + 16 bytes.
-         *
-         * zstd compiled with gcc-9 on an Intel i9-9900k shows 10% decompression
-         * speed swings based on the alignment of the decompression loop. This
-         * performance swing is caused by parts of the decompression loop falling
-         * out of the DSB. The entire decompression loop should fit in the DSB,
-         * when it can't we get much worse performance. You can measure if you've
-         * hit the good case or the bad case with this perf command for some
-         * compressed file test.zst:
-         *
-         *   perf stat -e cycles -e instructions -e idq.all_dsb_cycles_any_uops \
-         *             -e idq.all_mite_cycles_any_uops -- ./zstd -tq test.zst
-         *
-         * If you see most cycles served out of the MITE you've hit the bad case.
-         * If you see most cycles served out of the DSB you've hit the good case.
-         * If it is pretty even then you may be in an okay case.
-         *
-         * This issue has been reproduced on the following CPUs:
-         *   - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9
-         *               Use Instruments->Counters to get DSB/MITE cycles.
-         *               I never got performance swings, but I was able to
-         *               go from the good case of mostly DSB to half of the
-         *               cycles served from MITE.
-         *   - Coffeelake: Intel i9-9900k
-         *   - Coffeelake: Intel i7-9700k
-         *
-         * I haven't been able to reproduce the instability or DSB misses on any
-         * of the following CPUS:
-         *   - Haswell
-         *   - Broadwell: Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GH
-         *   - Skylake
-         *
-         * If you are seeing performance stability this script can help test.
-         * It tests on 4 commits in zstd where I saw performance change.
-         *
-         *   https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4
-         */
-        __asm__(".p2align 6");
-        __asm__("nop");
-        __asm__(".p2align 5");
-        __asm__("nop");
-#  if __GNUC__ >= 9
-        /* better for gcc-9 and gcc-10, worse for clang and gcc-8 */
-        __asm__(".p2align 3");
-#  else
-        __asm__(".p2align 4");
+            __asm__(".p2align 6");
+#  if __GNUC__ >= 7
+	    /* good for gcc-7, gcc-9, and gcc-11 */
+            __asm__("nop");
+            __asm__(".p2align 5");
+            __asm__("nop");
+            __asm__(".p2align 4");
+#    if __GNUC__ == 8 || __GNUC__ == 10
+	    /* good for gcc-8 and gcc-10 */
+            __asm__("nop");
+            __asm__(".p2align 3");
+#    endif
 #  endif
 #endif
+
+            /* Handle the initial state where litBuffer is currently split between dst and litExtraBuffer */
+            for (; litPtr + sequence.litLength <= dctx->litBufferEnd; ) {
+                size_t const oneSeqSize = ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence.litLength - WILDCOPY_OVERLENGTH, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                assert(!ZSTD_isError(oneSeqSize));
+                if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase);
+#endif
+                if (UNLIKELY(ZSTD_isError(oneSeqSize)))
+                    return oneSeqSize;
+                DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);
+                op += oneSeqSize;
+                if (UNLIKELY(!--nbSeq))
+                    break;
+                BIT_reloadDStream(&(seqState.DStream));
+                sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+            }
+
+            /* If there are more sequences, they will need to read literals from litExtraBuffer; copy over the remainder from dst and update litPtr and litEnd */
+            if (nbSeq > 0) {
+                const size_t leftoverLit = dctx->litBufferEnd - litPtr;
+                if (leftoverLit)
+                {
+                    RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer");
+                    ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit);
+                    sequence.litLength -= leftoverLit;
+                    op += leftoverLit;
+                }
+                litPtr = dctx->litExtraBuffer;
+                litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE;
+                dctx->litBufferLocation = ZSTD_not_in_dst;
+                {
+                    size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                    assert(!ZSTD_isError(oneSeqSize));
+                    if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase);
+#endif
+                    if (UNLIKELY(ZSTD_isError(oneSeqSize)))
+                        return oneSeqSize;
+                    DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);
+                    op += oneSeqSize;
+                    if (--nbSeq)
+                        BIT_reloadDStream(&(seqState.DStream));
+                }
+            }
+        }
+
+        if (nbSeq > 0) /* there is remaining lit from extra buffer */
+        {
+
+#if defined(__GNUC__) && defined(__x86_64__)
+            __asm__(".p2align 6");
+            __asm__("nop");
+#  if __GNUC__ != 7
+            /* worse for gcc-7 better for gcc-8, gcc-9, and gcc-10 and clang */
+            __asm__(".p2align 4");
+            __asm__("nop");
+            __asm__(".p2align 3");
+#  elif __GNUC__ >= 11
+            __asm__(".p2align 3");
+#  else
+            __asm__(".p2align 5");
+            __asm__("nop");
+            __asm__(".p2align 3");
+#  endif
+#endif
+
+            for (; ; ) {
+                seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+                size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litBufferEnd, prefixStart, vBase, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                assert(!ZSTD_isError(oneSeqSize));
+                if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase);
+#endif
+                if (UNLIKELY(ZSTD_isError(oneSeqSize)))
+                    return oneSeqSize;
+                DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);
+                op += oneSeqSize;
+                if (UNLIKELY(!--nbSeq))
+                    break;
+                BIT_reloadDStream(&(seqState.DStream));
+            }
+        }
+
+        /* check if reached exact end */
+        DEBUGLOG(5, "ZSTD_decompressSequences_bodySplitLitBuffer: after decode loop, remaining nbSeq : %i", nbSeq);
+        RETURN_ERROR_IF(nbSeq, corruption_detected, "");
+        RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected, "");
+        /* save reps for next block */
+        { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); }
+    }
+
+    /* last literal segment */
+    if (dctx->litBufferLocation == ZSTD_split)  /* split hasn't been reached yet, first get dst then copy litExtraBuffer */
+    {
+        size_t const lastLLSize = litBufferEnd - litPtr;
+        RETURN_ERROR_IF(lastLLSize > (size_t)(oend - op), dstSize_tooSmall, "");
+        if (op != NULL) {
+            ZSTD_memmove(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
+        litPtr = dctx->litExtraBuffer;
+        litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE;
+        dctx->litBufferLocation = ZSTD_not_in_dst;
+    }
+    {   size_t const lastLLSize = litBufferEnd - litPtr;
+        RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
+        if (op != NULL) {
+            ZSTD_memcpy(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
+    }
+
+    return op-ostart;
+}
+
+FORCE_INLINE_TEMPLATE size_t
+DONT_VECTORIZE
+ZSTD_decompressSequences_body(ZSTD_DCtx* dctx,
+    void* dst, size_t maxDstSize,
+    const void* seqStart, size_t seqSize, int nbSeq,
+    const ZSTD_longOffset_e isLongOffset,
+    const int frame)
+{
+    const BYTE* ip = (const BYTE*)seqStart;
+    const BYTE* const iend = ip + seqSize;
+    BYTE* const ostart = (BYTE*)dst;
+    BYTE* const oend = dctx->litBufferLocation == ZSTD_not_in_dst ? ostart + maxDstSize : dctx->litBuffer;
+    BYTE* op = ostart;
+    const BYTE* litPtr = dctx->litPtr;
+    const BYTE* const litEnd = litPtr + dctx->litSize;
+    const BYTE* const prefixStart = (const BYTE*)(dctx->prefixStart);
+    const BYTE* const vBase = (const BYTE*)(dctx->virtualStart);
+    const BYTE* const dictEnd = (const BYTE*)(dctx->dictEnd);
+    DEBUGLOG(5, "ZSTD_decompressSequences_body: nbSeq = %d", nbSeq);
+    (void)frame;
+
+    /* Regen sequences */
+    if (nbSeq) {
+        seqState_t seqState;
+        dctx->fseEntropy = 1;
+        { U32 i; for (i = 0; i < ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
+        RETURN_ERROR_IF(
+            ERR_isError(BIT_initDStream(&seqState.DStream, ip, iend - ip)),
+            corruption_detected, "");
+        ZSTD_initFseState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr);
+        ZSTD_initFseState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr);
+        ZSTD_initFseState(&seqState.stateML, &seqState.DStream, dctx->MLTptr);
+        assert(dst != NULL);
+
+        ZSTD_STATIC_ASSERT(
+            BIT_DStream_unfinished < BIT_DStream_completed &&
+            BIT_DStream_endOfBuffer < BIT_DStream_completed &&
+            BIT_DStream_completed < BIT_DStream_overflow);
+
+#if defined(__GNUC__) && defined(__x86_64__)
+            __asm__(".p2align 6");
+            __asm__("nop");
+#  if __GNUC__ >= 7
+            __asm__(".p2align 5");
+            __asm__("nop");
+            __asm__(".p2align 3");
+#  else
+            __asm__(".p2align 4");
+            __asm__("nop");
+            __asm__(".p2align 3");
+#  endif
+#endif
+
         for ( ; ; ) {
             seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
             size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd);
@@ -1218,6 +1661,16 @@
 {
     return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
 }
+
+static size_t
+ZSTD_decompressSequencesSplitLitBuffer_default(ZSTD_DCtx* dctx,
+                                               void* dst, size_t maxDstSize,
+                                         const void* seqStart, size_t seqSize, int nbSeq,
+                                         const ZSTD_longOffset_e isLongOffset,
+                                         const int frame)
+{
+    return ZSTD_decompressSequences_bodySplitLitBuffer(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
+}
 #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
@@ -1250,10 +1703,10 @@
     const BYTE* ip = (const BYTE*)seqStart;
     const BYTE* const iend = ip + seqSize;
     BYTE* const ostart = (BYTE*)dst;
-    BYTE* const oend = ostart + maxDstSize;
+    BYTE* const oend = dctx->litBufferLocation == ZSTD_in_dst ? dctx->litBuffer : ostart + maxDstSize;
     BYTE* op = ostart;
     const BYTE* litPtr = dctx->litPtr;
-    const BYTE* const litEnd = litPtr + dctx->litSize;
+    const BYTE* litBufferEnd = dctx->litBufferEnd;
     const BYTE* const prefixStart = (const BYTE*) (dctx->prefixStart);
     const BYTE* const dictStart = (const BYTE*) (dctx->virtualStart);
     const BYTE* const dictEnd = (const BYTE*) (dctx->dictEnd);
@@ -1289,32 +1742,94 @@
         }
         RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected, "");
 
-        /* decode and decompress */
-        for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) {
-            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
-            size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
-#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
-            assert(!ZSTD_isError(oneSeqSize));
-            if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart);
-#endif
-            if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+        /* decompress without stomping litBuffer */
+        for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb < nbSeq); seqNb++) {
+            seq_t sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+            size_t oneSeqSize;
 
-            prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
-            sequences[seqNb & STORED_SEQS_MASK] = sequence;
-            op += oneSeqSize;
+            if (dctx->litBufferLocation == ZSTD_split && litPtr + sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength > dctx->litBufferEnd)
+            {
+                /* lit buffer is reaching split point, empty out the first buffer and transition to litExtraBuffer */
+                const size_t leftoverLit = dctx->litBufferEnd - litPtr;
+                if (leftoverLit)
+                {
+                    RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer");
+                    ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit);
+                    sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength -= leftoverLit;
+                    op += leftoverLit;
+                }
+                litPtr = dctx->litExtraBuffer;
+                litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE;
+                dctx->litBufferLocation = ZSTD_not_in_dst;
+                oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                assert(!ZSTD_isError(oneSeqSize));
+                if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart);
+#endif
+                if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+
+                prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
+                sequences[seqNb & STORED_SEQS_MASK] = sequence;
+                op += oneSeqSize;
+            }
+            else
+            {
+                /* lit buffer is either wholly contained in first or second split, or not split at all*/
+                oneSeqSize = dctx->litBufferLocation == ZSTD_split ?
+                    ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK].litLength - WILDCOPY_OVERLENGTH, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) :
+                    ZSTD_execSequence(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                assert(!ZSTD_isError(oneSeqSize));
+                if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb - ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart);
+#endif
+                if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+
+                prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
+                sequences[seqNb & STORED_SEQS_MASK] = sequence;
+                op += oneSeqSize;
+            }
         }
         RETURN_ERROR_IF(seqNb<nbSeq, corruption_detected, "");
 
         /* finish queue */
         seqNb -= seqAdvance;
         for ( ; seqNb<nbSeq ; seqNb++) {
-            size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[seqNb&STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
+            seq_t *sequence = &(sequences[seqNb&STORED_SEQS_MASK]);
+            if (dctx->litBufferLocation == ZSTD_split && litPtr + sequence->litLength > dctx->litBufferEnd)
+            {
+                const size_t leftoverLit = dctx->litBufferEnd - litPtr;
+                if (leftoverLit)
+                {
+                    RETURN_ERROR_IF(leftoverLit > (size_t)(oend - op), dstSize_tooSmall, "remaining lit must fit within dstBuffer");
+                    ZSTD_safecopyDstBeforeSrc(op, litPtr, leftoverLit);
+                    sequence->litLength -= leftoverLit;
+                    op += leftoverLit;
+                }
+                litPtr = dctx->litExtraBuffer;
+                litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE;
+                dctx->litBufferLocation = ZSTD_not_in_dst;
+                {
+                    size_t const oneSeqSize = ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
 #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
-            assert(!ZSTD_isError(oneSeqSize));
-            if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart);
+                    assert(!ZSTD_isError(oneSeqSize));
+                    if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart);
 #endif
-            if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
-            op += oneSeqSize;
+                    if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+                    op += oneSeqSize;
+                }
+            }
+            else
+            {
+                size_t const oneSeqSize = dctx->litBufferLocation == ZSTD_split ?
+                    ZSTD_execSequenceSplitLitBuffer(op, oend, litPtr + sequence->litLength - WILDCOPY_OVERLENGTH, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd) :
+                    ZSTD_execSequence(op, oend, *sequence, &litPtr, litBufferEnd, prefixStart, dictStart, dictEnd);
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
+                assert(!ZSTD_isError(oneSeqSize));
+                if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[seqNb&STORED_SEQS_MASK], prefixStart, dictStart);
+#endif
+                if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
+                op += oneSeqSize;
+            }
         }
 
         /* save reps for next block */
@@ -1322,10 +1837,21 @@
     }
 
     /* last literal segment */
-    {   size_t const lastLLSize = litEnd - litPtr;
+    if (dctx->litBufferLocation == ZSTD_split)  /* first deplete literal buffer in dst, then copy litExtraBuffer */
+    {
+        size_t const lastLLSize = litBufferEnd - litPtr;
+        RETURN_ERROR_IF(lastLLSize > (size_t)(oend - op), dstSize_tooSmall, "");
+        if (op != NULL) {
+            ZSTD_memmove(op, litPtr, lastLLSize);
+            op += lastLLSize;
+        }
+        litPtr = dctx->litExtraBuffer;
+        litBufferEnd = dctx->litExtraBuffer + ZSTD_LITBUFFEREXTRASIZE;
+    }
+    {   size_t const lastLLSize = litBufferEnd - litPtr;
         RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
         if (op != NULL) {
-            ZSTD_memcpy(op, litPtr, lastLLSize);
+            ZSTD_memmove(op, litPtr, lastLLSize);
             op += lastLLSize;
         }
     }
@@ -1349,7 +1875,7 @@
 #if DYNAMIC_BMI2
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
-static TARGET_ATTRIBUTE("bmi2") size_t
+static BMI2_TARGET_ATTRIBUTE size_t
 DONT_VECTORIZE
 ZSTD_decompressSequences_bmi2(ZSTD_DCtx* dctx,
                                  void* dst, size_t maxDstSize,
@@ -1359,10 +1885,20 @@
 {
     return ZSTD_decompressSequences_body(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
 }
+static BMI2_TARGET_ATTRIBUTE size_t
+DONT_VECTORIZE
+ZSTD_decompressSequencesSplitLitBuffer_bmi2(ZSTD_DCtx* dctx,
+                                 void* dst, size_t maxDstSize,
+                           const void* seqStart, size_t seqSize, int nbSeq,
+                           const ZSTD_longOffset_e isLongOffset,
+                           const int frame)
+{
+    return ZSTD_decompressSequences_bodySplitLitBuffer(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
+}
 #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
-static TARGET_ATTRIBUTE("bmi2") size_t
+static BMI2_TARGET_ATTRIBUTE size_t
 ZSTD_decompressSequencesLong_bmi2(ZSTD_DCtx* dctx,
                                  void* dst, size_t maxDstSize,
                            const void* seqStart, size_t seqSize, int nbSeq,
@@ -1391,11 +1927,25 @@
 {
     DEBUGLOG(5, "ZSTD_decompressSequences");
 #if DYNAMIC_BMI2
-    if (dctx->bmi2) {
+    if (ZSTD_DCtx_get_bmi2(dctx)) {
         return ZSTD_decompressSequences_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
     }
 #endif
-  return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
+    return ZSTD_decompressSequences_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
+}
+static size_t
+ZSTD_decompressSequencesSplitLitBuffer(ZSTD_DCtx* dctx, void* dst, size_t maxDstSize,
+                                 const void* seqStart, size_t seqSize, int nbSeq,
+                                 const ZSTD_longOffset_e isLongOffset,
+                                 const int frame)
+{
+    DEBUGLOG(5, "ZSTD_decompressSequencesSplitLitBuffer");
+#if DYNAMIC_BMI2
+    if (ZSTD_DCtx_get_bmi2(dctx)) {
+        return ZSTD_decompressSequencesSplitLitBuffer_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
+    }
+#endif
+    return ZSTD_decompressSequencesSplitLitBuffer_default(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
 }
 #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
 
@@ -1415,7 +1965,7 @@
 {
     DEBUGLOG(5, "ZSTD_decompressSequencesLong");
 #if DYNAMIC_BMI2
-    if (dctx->bmi2) {
+    if (ZSTD_DCtx_get_bmi2(dctx)) {
         return ZSTD_decompressSequencesLong_bmi2(dctx, dst, maxDstSize, seqStart, seqSize, nbSeq, isLongOffset, frame);
     }
 #endif
@@ -1424,55 +1974,101 @@
 #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT */
 
 
+/**
+ * @returns The total size of the history referenceable by zstd, including
+ * both the prefix and the extDict. At @p op any offset larger than this
+ * is invalid.
+ */
+static size_t ZSTD_totalHistorySize(BYTE* op, BYTE const* virtualStart)
+{
+    return (size_t)(op - virtualStart);
+}
 
-#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
-    !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
-/* ZSTD_getLongOffsetsShare() :
+typedef struct {
+    unsigned longOffsetShare;
+    unsigned maxNbAdditionalBits;
+} ZSTD_OffsetInfo;
+
+/* ZSTD_getOffsetInfo() :
  * condition : offTable must be valid
  * @return : "share" of long offsets (arbitrarily defined as > (1<<23))
- *           compared to maximum possible of (1<<OffFSELog) */
-static unsigned
-ZSTD_getLongOffsetsShare(const ZSTD_seqSymbol* offTable)
+ *           compared to maximum possible of (1<<OffFSELog),
+ *           as well as the maximum number additional bits required.
+ */
+static ZSTD_OffsetInfo
+ZSTD_getOffsetInfo(const ZSTD_seqSymbol* offTable, int nbSeq)
 {
-    const void* ptr = offTable;
-    U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog;
-    const ZSTD_seqSymbol* table = offTable + 1;
-    U32 const max = 1 << tableLog;
-    U32 u, total = 0;
-    DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog);
+    ZSTD_OffsetInfo info = {0, 0};
+    /* If nbSeq == 0, then the offTable is uninitialized, but we have
+     * no sequences, so both values should be 0.
+     */
+    if (nbSeq != 0) {
+        const void* ptr = offTable;
+        U32 const tableLog = ((const ZSTD_seqSymbol_header*)ptr)[0].tableLog;
+        const ZSTD_seqSymbol* table = offTable + 1;
+        U32 const max = 1 << tableLog;
+        U32 u;
+        DEBUGLOG(5, "ZSTD_getLongOffsetsShare: (tableLog=%u)", tableLog);
 
-    assert(max <= (1 << OffFSELog));  /* max not too large */
-    for (u=0; u<max; u++) {
-        if (table[u].nbAdditionalBits > 22) total += 1;
+        assert(max <= (1 << OffFSELog));  /* max not too large */
+        for (u=0; u<max; u++) {
+            info.maxNbAdditionalBits = MAX(info.maxNbAdditionalBits, table[u].nbAdditionalBits);
+            if (table[u].nbAdditionalBits > 22) info.longOffsetShare += 1;
+        }
+
+        assert(tableLog <= OffFSELog);
+        info.longOffsetShare <<= (OffFSELog - tableLog);  /* scale to OffFSELog */
     }
 
-    assert(tableLog <= OffFSELog);
-    total <<= (OffFSELog - tableLog);  /* scale to OffFSELog */
-
-    return total;
+    return info;
 }
-#endif
+
+/**
+ * @returns The maximum offset we can decode in one read of our bitstream, without
+ * reloading more bits in the middle of the offset bits read. Any offsets larger
+ * than this must use the long offset decoder.
+ */
+static size_t ZSTD_maxShortOffset(void)
+{
+    if (MEM_64bits()) {
+        /* We can decode any offset without reloading bits.
+         * This might change if the max window size grows.
+         */
+        ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX <= 31);
+        return (size_t)-1;
+    } else {
+        /* The maximum offBase is (1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1.
+         * This offBase would require STREAM_ACCUMULATOR_MIN extra bits.
+         * Then we have to subtract ZSTD_REP_NUM to get the maximum possible offset.
+         */
+        size_t const maxOffbase = ((size_t)1 << (STREAM_ACCUMULATOR_MIN + 1)) - 1;
+        size_t const maxOffset = maxOffbase - ZSTD_REP_NUM;
+        assert(ZSTD_highbit32((U32)maxOffbase) == STREAM_ACCUMULATOR_MIN);
+        return maxOffset;
+    }
+}
 
 size_t
 ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
                               void* dst, size_t dstCapacity,
-                        const void* src, size_t srcSize, const int frame)
+                        const void* src, size_t srcSize, const int frame, const streaming_operation streaming)
 {   /* blockType == blockCompressed */
     const BYTE* ip = (const BYTE*)src;
-    /* isLongOffset must be true if there are long offsets.
-     * Offsets are long if they are larger than 2^STREAM_ACCUMULATOR_MIN.
-     * We don't expect that to be the case in 64-bit mode.
-     * In block mode, window size is not known, so we have to be conservative.
-     * (note: but it could be evaluated from current-lowLimit)
-     */
-    ZSTD_longOffset_e const isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (!frame || (dctx->fParams.windowSize > (1ULL << STREAM_ACCUMULATOR_MIN))));
     DEBUGLOG(5, "ZSTD_decompressBlock_internal (size : %u)", (U32)srcSize);
 
-    RETURN_ERROR_IF(srcSize >= ZSTD_BLOCKSIZE_MAX, srcSize_wrong, "");
+    /* Note : the wording of the specification
+     * allows compressed block to be sized exactly ZSTD_BLOCKSIZE_MAX.
+     * This generally does not happen, as it makes little sense,
+     * since an uncompressed block would feature same size and have no decompression cost.
+     * Also, note that decoder from reference libzstd before < v1.5.4
+     * would consider this edge case as an error.
+     * As a consequence, avoid generating compressed blocks of size ZSTD_BLOCKSIZE_MAX
+     * for broader compatibility with the deployed ecosystem of zstd decoders */
+    RETURN_ERROR_IF(srcSize > ZSTD_BLOCKSIZE_MAX, srcSize_wrong, "");
 
     /* Decode literals section */
-    {   size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize);
-        DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize);
+    {   size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize, dst, dstCapacity, streaming);
+        DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : cSize=%u, nbLiterals=%zu", (U32)litCSize, dctx->litSize);
         if (ZSTD_isError(litCSize)) return litCSize;
         ip += litCSize;
         srcSize -= litCSize;
@@ -1480,6 +2076,23 @@
 
     /* Build Decoding Tables */
     {
+        /* Compute the maximum block size, which must also work when !frame and fParams are unset.
+         * Additionally, take the min with dstCapacity to ensure that the totalHistorySize fits in a size_t.
+         */
+        size_t const blockSizeMax = MIN(dstCapacity, (frame ? dctx->fParams.blockSizeMax : ZSTD_BLOCKSIZE_MAX));
+        size_t const totalHistorySize = ZSTD_totalHistorySize((BYTE*)dst + blockSizeMax, (BYTE const*)dctx->virtualStart);
+        /* isLongOffset must be true if there are long offsets.
+         * Offsets are long if they are larger than ZSTD_maxShortOffset().
+         * We don't expect that to be the case in 64-bit mode.
+         *
+         * We check here to see if our history is large enough to allow long offsets.
+         * If it isn't, then we can't possible have (valid) long offsets. If the offset
+         * is invalid, then it is okay to read it incorrectly.
+         *
+         * If isLongOffsets is true, then we will later check our decoding table to see
+         * if it is even possible to generate long offsets.
+         */
+        ZSTD_longOffset_e isLongOffset = (ZSTD_longOffset_e)(MEM_32bits() && (totalHistorySize > ZSTD_maxShortOffset()));
         /* These macros control at build-time which decompressor implementation
          * we use. If neither is defined, we do some inspection and dispatch at
          * runtime.
@@ -1487,6 +2100,11 @@
 #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
     !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
         int usePrefetchDecoder = dctx->ddictIsCold;
+#else
+        /* Set to 1 to avoid computing offset info if we don't need to.
+         * Otherwise this value is ignored.
+         */
+        int usePrefetchDecoder = 1;
 #endif
         int nbSeq;
         size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, srcSize);
@@ -1494,32 +2112,49 @@
         ip += seqHSize;
         srcSize -= seqHSize;
 
-        RETURN_ERROR_IF(dst == NULL && nbSeq > 0, dstSize_tooSmall, "NULL not handled");
+        RETURN_ERROR_IF((dst == NULL || dstCapacity == 0) && nbSeq > 0, dstSize_tooSmall, "NULL not handled");
+        RETURN_ERROR_IF(MEM_64bits() && sizeof(size_t) == sizeof(void*) && (size_t)(-1) - (size_t)dst < (size_t)(1 << 20), dstSize_tooSmall,
+                "invalid dst");
 
-#if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
-    !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
-        if ( !usePrefetchDecoder
-          && (!frame || (dctx->fParams.windowSize > (1<<24)))
-          && (nbSeq>ADVANCED_SEQS) ) {  /* could probably use a larger nbSeq limit */
-            U32 const shareLongOffsets = ZSTD_getLongOffsetsShare(dctx->OFTptr);
-            U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */
-            usePrefetchDecoder = (shareLongOffsets >= minShare);
+        /* If we could potentially have long offsets, or we might want to use the prefetch decoder,
+         * compute information about the share of long offsets, and the maximum nbAdditionalBits.
+         * NOTE: could probably use a larger nbSeq limit
+         */
+        if (isLongOffset || (!usePrefetchDecoder && (totalHistorySize > (1u << 24)) && (nbSeq > 8))) {
+            ZSTD_OffsetInfo const info = ZSTD_getOffsetInfo(dctx->OFTptr, nbSeq);
+            if (isLongOffset && info.maxNbAdditionalBits <= STREAM_ACCUMULATOR_MIN) {
+                /* If isLongOffset, but the maximum number of additional bits that we see in our table is small
+                 * enough, then we know it is impossible to have too long an offset in this block, so we can
+                 * use the regular offset decoder.
+                 */
+                isLongOffset = ZSTD_lo_isRegularOffset;
+            }
+            if (!usePrefetchDecoder) {
+                U32 const minShare = MEM_64bits() ? 7 : 20; /* heuristic values, correspond to 2.73% and 7.81% */
+                usePrefetchDecoder = (info.longOffsetShare >= minShare);
+            }
         }
-#endif
 
         dctx->ddictIsCold = 0;
 
 #if !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT) && \
     !defined(ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG)
-        if (usePrefetchDecoder)
+        if (usePrefetchDecoder) {
+#else
+        (void)usePrefetchDecoder;
+        {
 #endif
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
             return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame);
 #endif
+        }
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG
         /* else */
-        return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame);
+        if (dctx->litBufferLocation == ZSTD_split)
+            return ZSTD_decompressSequencesSplitLitBuffer(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame);
+        else
+            return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize, nbSeq, isLongOffset, frame);
 #endif
     }
 }
@@ -1536,13 +2171,22 @@
 }
 
 
+size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx,
+                                       void* dst, size_t dstCapacity,
+                                 const void* src, size_t srcSize)
+{
+    size_t dSize;
+    ZSTD_checkContinuity(dctx, dst, dstCapacity);
+    dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0, not_streaming);
+    dctx->previousDstEnd = (char*)dst + dSize;
+    return dSize;
+}
+
+
+/* NOTE: Must just wrap ZSTD_decompressBlock_deprecated() */
 size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx,
                             void* dst, size_t dstCapacity,
                       const void* src, size_t srcSize)
 {
-    size_t dSize;
-    ZSTD_checkContinuity(dctx, dst, dstCapacity);
-    dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0);
-    dctx->previousDstEnd = (char*)dst + dSize;
-    return dSize;
+    return ZSTD_decompressBlock_deprecated(dctx, dst, dstCapacity, src, srcSize);
 }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
index 049a0cd..9d13188 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -33,6 +33,12 @@
  */
 
 
+ /* Streaming state is used to inform allocation of the literal buffer */
+typedef enum {
+    not_streaming = 0,
+    is_streaming = 1
+} streaming_operation;
+
 /* ZSTD_decompressBlock_internal() :
  * decompress block, starting at `src`,
  * into destination buffer `dst`.
@@ -41,7 +47,7 @@
  */
 size_t ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
                                void* dst, size_t dstCapacity,
-                         const void* src, size_t srcSize, const int frame);
+                         const void* src, size_t srcSize, const int frame, const streaming_operation streaming);
 
 /* ZSTD_buildFSETable() :
  * generate FSE decoding table for one symbol (ll, ml or off)
@@ -54,9 +60,14 @@
  */
 void ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
              const short* normalizedCounter, unsigned maxSymbolValue,
-             const U32* baseValue, const U32* nbAdditionalBits,
+             const U32* baseValue, const U8* nbAdditionalBits,
                    unsigned tableLog, void* wksp, size_t wkspSize,
                    int bmi2);
 
+/* Internal definition of ZSTD_decompressBlock() to avoid deprecation warnings. */
+size_t ZSTD_decompressBlock_deprecated(ZSTD_DCtx* dctx,
+                            void* dst, size_t dstCapacity,
+                      const void* src, size_t srcSize);
+
 
 #endif /* ZSTD_DEC_BLOCK_H */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
index ebda0c9..c2ec5d9 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -20,7 +20,7 @@
  *  Dependencies
  *********************************************************/
 #include "../common/mem.h"             /* BYTE, U16, U32 */
-#include "../common/zstd_internal.h"   /* ZSTD_seqSymbol */
+#include "../common/zstd_internal.h"   /* constants : MaxLL, MaxML, MaxOff, LLFSELog, etc. */
 
 
 
@@ -40,7 +40,7 @@
                  0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
                  0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
 
-static UNUSED_ATTR const U32 OF_bits[MaxOff+1] = {
+static UNUSED_ATTR const U8 OF_bits[MaxOff+1] = {
                      0,  1,  2,  3,  4,  5,  6,  7,
                      8,  9, 10, 11, 12, 13, 14, 15,
                     16, 17, 18, 19, 20, 21, 22, 23,
@@ -75,12 +75,13 @@
 
 #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64))
 #define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32))
+#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12
 
 typedef struct {
     ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)];    /* Note : Space reserved for FSE Tables */
     ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)];   /* is also used as temporary workspace while building hufTable during DDict creation */
     ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)];    /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */
-    HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)];  /* can accommodate HUF_decompress4X */
+    HUF_DTable hufTable[HUF_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)];  /* can accommodate HUF_decompress4X */
     U32 rep[ZSTD_REP_NUM];
     U32 workspace[ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32];
 } ZSTD_entropyDTables_t;
@@ -106,6 +107,22 @@
     size_t ddictPtrCount;
 } ZSTD_DDictHashSet;
 
+#ifndef ZSTD_DECODER_INTERNAL_BUFFER
+#  define ZSTD_DECODER_INTERNAL_BUFFER  (1 << 16)
+#endif
+
+#define ZSTD_LBMIN 64
+#define ZSTD_LBMAX (128 << 10)
+
+/* extra buffer, compensates when dst is not large enough to store litBuffer */
+#define ZSTD_LITBUFFEREXTRASIZE  BOUNDED(ZSTD_LBMIN, ZSTD_DECODER_INTERNAL_BUFFER, ZSTD_LBMAX)
+
+typedef enum {
+    ZSTD_not_in_dst = 0,  /* Stored entirely within litExtraBuffer */
+    ZSTD_in_dst = 1,           /* Stored entirely within dst (in memory after current output write) */
+    ZSTD_split = 2            /* Split between litExtraBuffer and dst */
+} ZSTD_litLocation_e;
+
 struct ZSTD_DCtx_s
 {
     const ZSTD_seqSymbol* LLTptr;
@@ -136,7 +153,9 @@
     size_t litSize;
     size_t rleSize;
     size_t staticSize;
+#if DYNAMIC_BMI2 != 0
     int bmi2;                     /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
+#endif
 
     /* dictionary */
     ZSTD_DDict* ddictLocal;
@@ -146,6 +165,7 @@
     ZSTD_dictUses_e dictUses;
     ZSTD_DDictHashSet* ddictSet;                    /* Hash set for multiple ddicts */
     ZSTD_refMultipleDDicts_e refMultipleDDicts;     /* User specified: if == 1, will allow references to multiple DDicts. Default == 0 (disabled) */
+    int disableHufAsm;
 
     /* streaming */
     ZSTD_dStreamStage streamStage;
@@ -158,16 +178,21 @@
     size_t outStart;
     size_t outEnd;
     size_t lhSize;
+#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
     void* legacyContext;
     U32 previousLegacyVersion;
     U32 legacyVersion;
+#endif
     U32 hostageByte;
     int noForwardProgress;
     ZSTD_bufferMode_e outBufferMode;
     ZSTD_outBuffer expectedOutBuffer;
 
     /* workspace */
-    BYTE litBuffer[ZSTD_BLOCKSIZE_MAX + WILDCOPY_OVERLENGTH];
+    BYTE* litBuffer;
+    const BYTE* litBufferEnd;
+    ZSTD_litLocation_e litBufferLocation;
+    BYTE litExtraBuffer[ZSTD_LITBUFFEREXTRASIZE + WILDCOPY_OVERLENGTH]; /* literal buffer can be split between storage within dst and within this scratch buffer */
     BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
 
     size_t oversizedDuration;
@@ -183,6 +208,14 @@
 #endif
 };  /* typedef'd to ZSTD_DCtx within "zstd.h" */
 
+MEM_STATIC int ZSTD_DCtx_get_bmi2(const struct ZSTD_DCtx_s *dctx) {
+#if DYNAMIC_BMI2 != 0
+	return dctx->bmi2;
+#else
+    (void)dctx;
+	return 0;
+#endif
+}
 
 /*-*******************************************************
  *  Shared internal functions
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff.h b/Utilities/cmzstd/lib/deprecated/zbuff.h
index b83ea0f..a968245 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff.h
+++ b/Utilities/cmzstd/lib/deprecated/zbuff.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_common.c b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
index e7d01a0..5a2f2db 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_common.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
index 2e72267..1d86821 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -15,6 +15,7 @@
 ***************************************/
 #define ZBUFF_STATIC_LINKING_ONLY
 #include "zbuff.h"
+#include "../common/error_private.h"
 
 
 /*-***********************************************************
@@ -73,13 +74,32 @@
                                    ZSTD_parameters params, unsigned long long pledgedSrcSize)
 {
     if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;  /* preserve "0 == unknown" behavior */
-    return ZSTD_initCStream_advanced(zbc, dict, dictSize, params, pledgedSrcSize);
-}
+    FORWARD_IF_ERROR(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setPledgedSrcSize(zbc, pledgedSrcSize), "");
 
+    FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_windowLog, params.cParams.windowLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_hashLog, params.cParams.hashLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_chainLog, params.cParams.chainLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_searchLog, params.cParams.searchLog), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_minMatch, params.cParams.minMatch), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_targetLength, params.cParams.targetLength), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_strategy, params.cParams.strategy), "");
+
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_contentSizeFlag, params.fParams.contentSizeFlag), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_checksumFlag, params.fParams.checksumFlag), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_dictIDFlag, params.fParams.noDictIDFlag), "");
+
+    FORWARD_IF_ERROR(ZSTD_CCtx_loadDictionary(zbc, dict, dictSize), "");
+    return 0;
+}
 
 size_t ZBUFF_compressInitDictionary(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, int compressionLevel)
 {
-    return ZSTD_initCStream_usingDict(zbc, dict, dictSize, compressionLevel);
+    FORWARD_IF_ERROR(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_setParameter(zbc, ZSTD_c_compressionLevel, compressionLevel), "");
+    FORWARD_IF_ERROR(ZSTD_CCtx_loadDictionary(zbc, dict, dictSize), "");
+    return 0;
 }
 
 size_t ZBUFF_compressInit(ZBUFF_CCtx* zbc, int compressionLevel)
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
index d73c0f3..12a66af 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -13,6 +13,8 @@
 /* *************************************
 *  Dependencies
 ***************************************/
+#define ZSTD_DISABLE_DEPRECATE_WARNINGS  /* suppress warning on ZSTD_initDStream_usingDict */
+#include "../zstd.h"        /* ZSTD_CStream, ZSTD_DStream, ZSTDLIB_API */
 #define ZBUFF_STATIC_LINKING_ONLY
 #include "zbuff.h"
 
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.c b/Utilities/cmzstd/lib/dictBuilder/cover.c
index 8364444..9e5e7d5 100644
--- a/Utilities/cmzstd/lib/dictBuilder/cover.c
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -34,12 +34,20 @@
 #include "../common/pool.h"
 #include "../common/threading.h"
 #include "../common/zstd_internal.h" /* includes zstd.h */
+#include "../common/bits.h" /* ZSTD_highbit32 */
 #include "../zdict.h"
 #include "cover.h"
 
 /*-*************************************
 *  Constants
 ***************************************/
+/**
+* There are 32bit indexes used to ref samples, so limit samples size to 4GB
+* on 64bit builds.
+* For 32bit builds we choose 1 GB.
+* Most 32bit platforms have 2GB user-mode addressable space and we allocate a large
+* contiguous buffer, so 1GB is already a high limit.
+*/
 #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
 #define COVER_DEFAULT_SPLITPOINT 1.0
 
@@ -47,7 +55,7 @@
 *  Console display
 ***************************************/
 #ifndef LOCALDISPLAYLEVEL
-static int g_displayLevel = 2;
+static int g_displayLevel = 0;
 #endif
 #undef  DISPLAY
 #define DISPLAY(...)                                                           \
@@ -534,7 +542,7 @@
 
 /**
  * Prepare a context for dictionary building.
- * The context is only dependent on the parameter `d` and can used multiple
+ * The context is only dependent on the parameter `d` and can be used multiple
  * times.
  * Returns 0 on success or error code on error.
  * The context must be destroyed with `COVER_ctx_destroy()`.
@@ -639,7 +647,7 @@
 
 void COVER_warnOnSmallCorpus(size_t maxDictSize, size_t nbDmers, int displayLevel)
 {
-  const double ratio = (double)nbDmers / maxDictSize;
+  const double ratio = (double)nbDmers / (double)maxDictSize;
   if (ratio >= 10) {
       return;
   }
@@ -735,7 +743,7 @@
   COVER_map_t activeDmers;
   parameters.splitPoint = 1.0;
   /* Initialize global data */
-  g_displayLevel = parameters.zParams.notificationLevel;
+  g_displayLevel = (int)parameters.zParams.notificationLevel;
   /* Checks */
   if (!COVER_checkParameters(parameters, dictBufferCapacity)) {
     DISPLAYLEVEL(1, "Cover parameters incorrect\n");
@@ -943,9 +951,17 @@
   }
 }
 
+static COVER_dictSelection_t setDictSelection(BYTE* buf, size_t s, size_t csz)
+{
+    COVER_dictSelection_t ds;
+    ds.dictContent = buf;
+    ds.dictSize = s;
+    ds.totalCompressedSize = csz;
+    return ds;
+}
+
 COVER_dictSelection_t COVER_dictSelectionError(size_t error) {
-    COVER_dictSelection_t selection = { NULL, 0, error };
-    return selection;
+    return setDictSelection(NULL, 0, error);
 }
 
 unsigned COVER_dictSelectionIsError(COVER_dictSelection_t selection) {
@@ -998,9 +1014,8 @@
   }
 
   if (params.shrinkDict == 0) {
-    COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize };
     free(candidateDictBuffer);
-    return selection;
+    return setDictSelection(largestDictbuffer, dictContentSize, totalCompressedSize);
   }
 
   largestDict = dictContentSize;
@@ -1032,20 +1047,16 @@
       return COVER_dictSelectionError(totalCompressedSize);
     }
 
-    if (totalCompressedSize <= largestCompressed * regressionTolerance) {
-      COVER_dictSelection_t selection = { candidateDictBuffer, dictContentSize, totalCompressedSize };
+    if ((double)totalCompressedSize <= (double)largestCompressed * regressionTolerance) {
       free(largestDictbuffer);
-      return selection;
+      return setDictSelection( candidateDictBuffer, dictContentSize, totalCompressedSize );
     }
     dictContentSize *= 2;
   }
   dictContentSize = largestDict;
   totalCompressedSize = largestCompressed;
-  {
-    COVER_dictSelection_t selection = { largestDictbuffer, dictContentSize, totalCompressedSize };
-    free(candidateDictBuffer);
-    return selection;
-  }
+  free(candidateDictBuffer);
+  return setDictSelection( largestDictbuffer, dictContentSize, totalCompressedSize );
 }
 
 /**
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.h b/Utilities/cmzstd/lib/dictBuilder/cover.h
index 1aacddd..252624b 100644
--- a/Utilities/cmzstd/lib/dictBuilder/cover.h
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/dictBuilder/fastcover.c b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
index ed789f9..46bba01 100644
--- a/Utilities/cmzstd/lib/dictBuilder/fastcover.c
+++ b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -32,6 +32,13 @@
 /*-*************************************
 *  Constants
 ***************************************/
+/**
+* There are 32bit indexes used to ref samples, so limit samples size to 4GB
+* on 64bit builds.
+* For 32bit builds we choose 1 GB.
+* Most 32bit platforms have 2GB user-mode addressable space and we allocate a large
+* contiguous buffer, so 1GB is already a high limit.
+*/
 #define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
 #define FASTCOVER_MAX_F 31
 #define FASTCOVER_MAX_ACCEL 10
@@ -44,7 +51,7 @@
 *  Console display
 ***************************************/
 #ifndef LOCALDISPLAYLEVEL
-static int g_displayLevel = 2;
+static int g_displayLevel = 0;
 #endif
 #undef  DISPLAY
 #define DISPLAY(...)                                                           \
@@ -297,7 +304,7 @@
 
 /**
  * Prepare a context for dictionary building.
- * The context is only dependent on the parameter `d` and can used multiple
+ * The context is only dependent on the parameter `d` and can be used multiple
  * times.
  * Returns 0 on success or error code on error.
  * The context must be destroyed with `FASTCOVER_ctx_destroy()`.
@@ -549,7 +556,7 @@
     ZDICT_cover_params_t coverParams;
     FASTCOVER_accel_t accelParams;
     /* Initialize global data */
-    g_displayLevel = parameters.zParams.notificationLevel;
+    g_displayLevel = (int)parameters.zParams.notificationLevel;
     /* Assign splitPoint and f if not provided */
     parameters.splitPoint = 1.0;
     parameters.f = parameters.f == 0 ? DEFAULT_F : parameters.f;
@@ -632,7 +639,7 @@
     const unsigned accel = parameters->accel == 0 ? DEFAULT_ACCEL : parameters->accel;
     const unsigned shrinkDict = 0;
     /* Local variables */
-    const int displayLevel = parameters->zParams.notificationLevel;
+    const int displayLevel = (int)parameters->zParams.notificationLevel;
     unsigned iteration = 1;
     unsigned d;
     unsigned k;
@@ -716,7 +723,7 @@
         data->parameters.splitPoint = splitPoint;
         data->parameters.steps = kSteps;
         data->parameters.shrinkDict = shrinkDict;
-        data->parameters.zParams.notificationLevel = g_displayLevel;
+        data->parameters.zParams.notificationLevel = (unsigned)g_displayLevel;
         /* Check the parameters */
         if (!FASTCOVER_checkParameters(data->parameters, dictBufferCapacity,
                                        data->ctx->f, accel)) {
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.c b/Utilities/cmzstd/lib/dictBuilder/zdict.c
index 459cbe4..58290f4 100644
--- a/Utilities/cmzstd/lib/dictBuilder/zdict.c
+++ b/Utilities/cmzstd/lib/dictBuilder/zdict.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -44,7 +44,6 @@
 #ifndef ZDICT_STATIC_LINKING_ONLY
 #  define ZDICT_STATIC_LINKING_ONLY
 #endif
-#define HUF_STATIC_LINKING_ONLY
 
 #include "../common/mem.h"           /* read */
 #include "../common/fse.h"           /* FSE_normalizeCount, FSE_writeNCount */
@@ -54,6 +53,7 @@
 #include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */
 #include "../zdict.h"
 #include "divsufsort.h"
+#include "../common/bits.h"          /* ZSTD_NbCommonBytes */
 
 
 /*-*************************************
@@ -130,65 +130,6 @@
 /*-********************************************************
 *  Dictionary training functions
 **********************************************************/
-static unsigned ZDICT_NbCommonBytes (size_t val)
-{
-    if (MEM_isLittleEndian()) {
-        if (MEM_64bits()) {
-#       if defined(_MSC_VER) && defined(_WIN64)
-            unsigned long r = 0;
-            _BitScanForward64( &r, (U64)val );
-            return (unsigned)(r>>3);
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_ctzll((U64)val) >> 3);
-#       else
-            static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
-            return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-#       endif
-        } else { /* 32 bits */
-#       if defined(_MSC_VER)
-            unsigned long r=0;
-            _BitScanForward( &r, (U32)val );
-            return (unsigned)(r>>3);
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_ctz((U32)val) >> 3);
-#       else
-            static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
-            return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-#       endif
-        }
-    } else {  /* Big Endian CPU */
-        if (MEM_64bits()) {
-#       if defined(_MSC_VER) && defined(_WIN64)
-            unsigned long r = 0;
-            _BitScanReverse64( &r, val );
-            return (unsigned)(r>>3);
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_clzll(val) >> 3);
-#       else
-            unsigned r;
-            const unsigned n32 = sizeof(size_t)*4;   /* calculate this way due to compiler complaining in 32-bits mode */
-            if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
-            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
-            r += (!val);
-            return r;
-#       endif
-        } else { /* 32 bits */
-#       if defined(_MSC_VER)
-            unsigned long r = 0;
-            _BitScanReverse( &r, (unsigned long)val );
-            return (unsigned)(r>>3);
-#       elif defined(__GNUC__) && (__GNUC__ >= 3)
-            return (__builtin_clz((U32)val) >> 3);
-#       else
-            unsigned r;
-            if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
-            r += (!val);
-            return r;
-#       endif
-    }   }
-}
-
-
 /*! ZDICT_count() :
     Count the nb of common bytes between 2 pointers.
     Note : this function presumes end of buffer followed by noisy guard band.
@@ -203,7 +144,7 @@
             pMatch = (const char*)pMatch+sizeof(size_t);
             continue;
         }
-        pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff);
+        pIn = (const char*)pIn+ZSTD_NbCommonBytes(diff);
         return (size_t)((const char*)pIn - pStart);
     }
 }
@@ -235,7 +176,7 @@
     U32 savings[LLIMIT] = {0};
     const BYTE* b = (const BYTE*)buffer;
     size_t maxLength = LLIMIT;
-    size_t pos = suffix[start];
+    size_t pos = (size_t)suffix[start];
     U32 end = start;
     dictItem solution;
 
@@ -369,7 +310,7 @@
             savings[i] = savings[i-1] + (lengthList[i] * (i-3));
 
         DISPLAYLEVEL(4, "Selected dict at position %u, of length %u : saves %u (ratio: %.2f)  \n",
-                     (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / maxLength);
+                     (unsigned)pos, (unsigned)maxLength, (unsigned)savings[maxLength], (double)savings[maxLength] / (double)maxLength);
 
         solution.pos = (U32)pos;
         solution.length = (U32)maxLength;
@@ -379,7 +320,7 @@
         {   U32 id;
             for (id=start; id<end; id++) {
                 U32 p, pEnd, length;
-                U32 const testedPos = suffix[id];
+                U32 const testedPos = (U32)suffix[id];
                 if (testedPos == pos)
                     length = solution.length;
                 else {
@@ -431,7 +372,7 @@
             elt = table[u];
             /* sort : improve rank */
             while ((u>1) && (table[u-1].savings < elt.savings))
-            table[u] = table[u-1], u--;
+                table[u] = table[u-1], u--;
             table[u] = elt;
             return u;
     }   }
@@ -442,7 +383,7 @@
 
         if ((table[u].pos + table[u].length >= elt.pos) && (table[u].pos < elt.pos)) {  /* overlap, existing < new */
             /* append */
-            int const addedLength = (int)eltEnd - (table[u].pos + table[u].length);
+            int const addedLength = (int)eltEnd - (int)(table[u].pos + table[u].length);
             table[u].savings += elt.length / 8;    /* rough approx bonus */
             if (addedLength > 0) {   /* otherwise, elt fully included into existing */
                 table[u].length += addedLength;
@@ -582,7 +523,7 @@
             if (solution.length==0) { cursor++; continue; }
             ZDICT_insertDictItem(dictList, dictListSize, solution, buffer);
             cursor += solution.length;
-            DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / bufferSize * 100);
+            DISPLAYUPDATE(2, "\r%4.2f %% \r", (double)cursor / (double)bufferSize * 100.0);
     }   }
 
 _cleanup:
@@ -625,11 +566,11 @@
     size_t cSize;
 
     if (srcSize > blockSizeMax) srcSize = blockSizeMax;   /* protection vs large samples */
-    {   size_t const errorCode = ZSTD_compressBegin_usingCDict(esr.zc, esr.dict);
+    {   size_t const errorCode = ZSTD_compressBegin_usingCDict_deprecated(esr.zc, esr.dict);
         if (ZSTD_isError(errorCode)) { DISPLAYLEVEL(1, "warning : ZSTD_compressBegin_usingCDict failed \n"); return; }
 
     }
-    cSize = ZSTD_compressBlock(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize);
+    cSize = ZSTD_compressBlock_deprecated(esr.zc, esr.workPlace, ZSTD_BLOCKSIZE_MAX, src, srcSize);
     if (ZSTD_isError(cSize)) { DISPLAYLEVEL(3, "warning : could not compress sample size %u \n", (unsigned)srcSize); return; }
 
     if (cSize) {  /* if == 0; block is not compressible */
@@ -662,8 +603,8 @@
 
             if (nbSeq >= 2) { /* rep offsets */
                 const seqDef* const seq = seqStorePtr->sequencesStart;
-                U32 offset1 = seq[0].offset - 3;
-                U32 offset2 = seq[1].offset - 3;
+                U32 offset1 = seq[0].offBase - ZSTD_REP_NUM;
+                U32 offset2 = seq[1].offBase - ZSTD_REP_NUM;
                 if (offset1 >= MAXREPOFFSET) offset1 = 0;
                 if (offset2 >= MAXREPOFFSET) offset2 = 0;
                 repOffsets[offset1] += 3;
@@ -734,6 +675,7 @@
     size_t const totalSrcSize = ZDICT_totalSampleSize(fileSizes, nbFiles);
     size_t const averageSampleSize = totalSrcSize / (nbFiles + !nbFiles);
     BYTE* dstPtr = (BYTE*)dstBuffer;
+    U32 wksp[HUF_CTABLE_WORKSPACE_SIZE_U32];
 
     /* init */
     DEBUGLOG(4, "ZDICT_analyzeEntropy");
@@ -766,8 +708,15 @@
         pos += fileSizes[u];
     }
 
+    if (notificationLevel >= 4) {
+        /* writeStats */
+        DISPLAYLEVEL(4, "Offset Code Frequencies : \n");
+        for (u=0; u<=offcodeMax; u++) {
+            DISPLAYLEVEL(4, "%2u :%7u \n", u, offcodeCount[u]);
+    }   }
+
     /* analyze, build stats, starting with literals */
-    {   size_t maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog);
+    {   size_t maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp));
         if (HUF_isError(maxNbBits)) {
             eSize = maxNbBits;
             DISPLAYLEVEL(1, " HUF_buildCTable error \n");
@@ -776,7 +725,7 @@
         if (maxNbBits==8) {  /* not compressible : will fail on HUF_writeCTable() */
             DISPLAYLEVEL(2, "warning : pathological dataset : literals are not compressible : samples are noisy or too regular \n");
             ZDICT_flatLit(countLit);  /* replace distribution by a fake "mostly flat but still compressible" distribution, that HUF_writeCTable() can encode */
-            maxNbBits = HUF_buildCTable (hufTable, countLit, 255, huffLog);
+            maxNbBits = HUF_buildCTable_wksp(hufTable, countLit, 255, huffLog, wksp, sizeof(wksp));
             assert(maxNbBits==9);
         }
         huffLog = (U32)maxNbBits;
@@ -817,7 +766,7 @@
     llLog = (U32)errorCode;
 
     /* write result to buffer */
-    {   size_t const hhSize = HUF_writeCTable(dstPtr, maxDstSize, hufTable, 255, huffLog);
+    {   size_t const hhSize = HUF_writeCTable_wksp(dstPtr, maxDstSize, hufTable, 255, huffLog, wksp, sizeof(wksp));
         if (HUF_isError(hhSize)) {
             eSize = hhSize;
             DISPLAYLEVEL(1, "HUF_writeCTable error \n");
@@ -872,7 +821,7 @@
     MEM_writeLE32(dstPtr+8, bestRepOffset[2].offset);
 #else
     /* at this stage, we don't use the result of "most common first offset",
-       as the impact of statistics is not properly evaluated */
+     * as the impact of statistics is not properly evaluated */
     MEM_writeLE32(dstPtr+0, repStartValue[0]);
     MEM_writeLE32(dstPtr+4, repStartValue[1]);
     MEM_writeLE32(dstPtr+8, repStartValue[2]);
@@ -888,6 +837,17 @@
 }
 
 
+/**
+ * @returns the maximum repcode value
+ */
+static U32 ZDICT_maxRep(U32 const reps[ZSTD_REP_NUM])
+{
+    U32 maxRep = reps[0];
+    int r;
+    for (r = 1; r < ZSTD_REP_NUM; ++r)
+        maxRep = MAX(maxRep, reps[r]);
+    return maxRep;
+}
 
 size_t ZDICT_finalizeDictionary(void* dictBuffer, size_t dictBufferCapacity,
                           const void* customDictContent, size_t dictContentSize,
@@ -899,11 +859,13 @@
     BYTE header[HBUFFSIZE];
     int const compressionLevel = (params.compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : params.compressionLevel;
     U32 const notificationLevel = params.notificationLevel;
+    /* The final dictionary content must be at least as large as the largest repcode */
+    size_t const minContentSize = (size_t)ZDICT_maxRep(repStartValue);
+    size_t paddingSize;
 
     /* check conditions */
     DEBUGLOG(4, "ZDICT_finalizeDictionary");
     if (dictBufferCapacity < dictContentSize) return ERROR(dstSize_tooSmall);
-    if (dictContentSize < ZDICT_CONTENTSIZE_MIN) return ERROR(srcSize_wrong);
     if (dictBufferCapacity < ZDICT_DICTSIZE_MIN) return ERROR(dstSize_tooSmall);
 
     /* dictionary header */
@@ -927,12 +889,43 @@
         hSize += eSize;
     }
 
-    /* copy elements in final buffer ; note : src and dst buffer can overlap */
-    if (hSize + dictContentSize > dictBufferCapacity) dictContentSize = dictBufferCapacity - hSize;
-    {   size_t const dictSize = hSize + dictContentSize;
-        char* dictEnd = (char*)dictBuffer + dictSize;
-        memmove(dictEnd - dictContentSize, customDictContent, dictContentSize);
-        memcpy(dictBuffer, header, hSize);
+    /* Shrink the content size if it doesn't fit in the buffer */
+    if (hSize + dictContentSize > dictBufferCapacity) {
+        dictContentSize = dictBufferCapacity - hSize;
+    }
+
+    /* Pad the dictionary content with zeros if it is too small */
+    if (dictContentSize < minContentSize) {
+        RETURN_ERROR_IF(hSize + minContentSize > dictBufferCapacity, dstSize_tooSmall,
+                        "dictBufferCapacity too small to fit max repcode");
+        paddingSize = minContentSize - dictContentSize;
+    } else {
+        paddingSize = 0;
+    }
+
+    {
+        size_t const dictSize = hSize + paddingSize + dictContentSize;
+
+        /* The dictionary consists of the header, optional padding, and the content.
+         * The padding comes before the content because the "best" position in the
+         * dictionary is the last byte.
+         */
+        BYTE* const outDictHeader = (BYTE*)dictBuffer;
+        BYTE* const outDictPadding = outDictHeader + hSize;
+        BYTE* const outDictContent = outDictPadding + paddingSize;
+
+        assert(dictSize <= dictBufferCapacity);
+        assert(outDictContent + dictContentSize == (BYTE*)dictBuffer + dictSize);
+
+        /* First copy the customDictContent into its final location.
+         * `customDictContent` and `dictBuffer` may overlap, so we must
+         * do this before any other writes into the output buffer.
+         * Then copy the header & padding into the output buffer.
+         */
+        memmove(outDictContent, customDictContent, dictContentSize);
+        memcpy(outDictHeader, header, hSize);
+        memset(outDictPadding, 0, paddingSize);
+
         return dictSize;
     }
 }
diff --git a/Utilities/cmzstd/lib/zdict.h b/Utilities/cmzstd/lib/zdict.h
index 75b05db..2268f94 100644
--- a/Utilities/cmzstd/lib/zdict.h
+++ b/Utilities/cmzstd/lib/zdict.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -8,32 +8,43 @@
  * You may select, at your option, one of the above-listed licenses.
  */
 
-#ifndef DICTBUILDER_H_001
-#define DICTBUILDER_H_001
-
 #if defined (__cplusplus)
 extern "C" {
 #endif
 
+#ifndef ZSTD_ZDICT_H
+#define ZSTD_ZDICT_H
 
 /*======  Dependencies  ======*/
 #include <stddef.h>  /* size_t */
 
 
 /* =====   ZDICTLIB_API : control library symbols visibility   ===== */
-#ifndef ZDICTLIB_VISIBILITY
-#  if defined(__GNUC__) && (__GNUC__ >= 4)
-#    define ZDICTLIB_VISIBILITY __attribute__ ((visibility ("default")))
+#ifndef ZDICTLIB_VISIBLE
+   /* Backwards compatibility with old macro name */
+#  ifdef ZDICTLIB_VISIBILITY
+#    define ZDICTLIB_VISIBLE ZDICTLIB_VISIBILITY
+#  elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZDICTLIB_VISIBLE __attribute__ ((visibility ("default")))
 #  else
-#    define ZDICTLIB_VISIBILITY
+#    define ZDICTLIB_VISIBLE
 #  endif
 #endif
+
+#ifndef ZDICTLIB_HIDDEN
+#  if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZDICTLIB_HIDDEN __attribute__ ((visibility ("hidden")))
+#  else
+#    define ZDICTLIB_HIDDEN
+#  endif
+#endif
+
 #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
-#  define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBILITY
+#  define ZDICTLIB_API __declspec(dllexport) ZDICTLIB_VISIBLE
 #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
-#  define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#  define ZDICTLIB_API __declspec(dllimport) ZDICTLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
 #else
-#  define ZDICTLIB_API ZDICTLIB_VISIBILITY
+#  define ZDICTLIB_API ZDICTLIB_VISIBLE
 #endif
 
 /*******************************************************************************
@@ -46,7 +57,7 @@
  *
  * Zstd can use dictionaries to improve compression ratio of small data.
  * Traditionally small files don't compress well because there is very little
- * repetion in a single sample, since it is small. But, if you are compressing
+ * repetition in a single sample, since it is small. But, if you are compressing
  * many similar files, like a bunch of JSON records that share the same
  * structure, you can train a dictionary on ahead of time on some samples of
  * these files. Then, zstd can use the dictionary to find repetitions that are
@@ -110,8 +121,8 @@
  * The zstd CLI defaults to a 110KB dictionary. You likely don't need a
  * dictionary larger than that. But, most use cases can get away with a
  * smaller dictionary. The advanced dictionary builders can automatically
- * shrink the dictionary for you, and select a the smallest size that
- * doesn't hurt compression ratio too much. See the `shrinkDict` parameter.
+ * shrink the dictionary for you, and select the smallest size that doesn't
+ * hurt compression ratio too much. See the `shrinkDict` parameter.
  * A smaller dictionary can save memory, and potentially speed up
  * compression.
  *
@@ -132,7 +143,7 @@
  *
  *   # Benchmark levels 1-3 without a dictionary
  *   zstd -b1e3 -r /path/to/my/files
- *   # Benchmark levels 1-3 with a dictioanry
+ *   # Benchmark levels 1-3 with a dictionary
  *   zstd -b1e3 -r /path/to/my/files -D /path/to/my/dictionary
  *
  * When should I retrain a dictionary?
@@ -201,9 +212,9 @@
                                     const size_t* samplesSizes, unsigned nbSamples);
 
 typedef struct {
-    int      compressionLevel;   /*< optimize for a specific zstd compression level; 0 means default */
-    unsigned notificationLevel;  /*< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */
-    unsigned dictID;             /*< force dictID value; 0 means auto mode (32-bits random value)
+    int      compressionLevel;   /**< optimize for a specific zstd compression level; 0 means default */
+    unsigned notificationLevel;  /**< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */
+    unsigned dictID;             /**< force dictID value; 0 means auto mode (32-bits random value)
                                   *   NOTE: The zstd format reserves some dictionary IDs for future use.
                                   *         You may use them in private settings, but be warned that they
                                   *         may be used by zstd in a public dictionary registry in the future.
@@ -237,7 +248,6 @@
  * is presumed that the most profitable content is at the end of the dictionary,
  * since that is the cheapest to reference.
  *
- * `dictContentSize` must be >= ZDICT_CONTENTSIZE_MIN bytes.
  * `maxDictSize` must be >= max(dictContentSize, ZSTD_DICTSIZE_MIN).
  *
  * @return: size of dictionary stored into `dstDictBuffer` (<= `maxDictSize`),
@@ -261,9 +271,21 @@
 ZDICTLIB_API unsigned ZDICT_isError(size_t errorCode);
 ZDICTLIB_API const char* ZDICT_getErrorName(size_t errorCode);
 
+#endif   /* ZSTD_ZDICT_H */
 
+#if defined(ZDICT_STATIC_LINKING_ONLY) && !defined(ZSTD_ZDICT_H_STATIC)
+#define ZSTD_ZDICT_H_STATIC
 
-#ifdef ZDICT_STATIC_LINKING_ONLY
+/* This can be overridden externally to hide static symbols. */
+#ifndef ZDICTLIB_STATIC_API
+#  if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+#    define ZDICTLIB_STATIC_API __declspec(dllexport) ZDICTLIB_VISIBLE
+#  elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+#    define ZDICTLIB_STATIC_API __declspec(dllimport) ZDICTLIB_VISIBLE
+#  else
+#    define ZDICTLIB_STATIC_API ZDICTLIB_VISIBLE
+#  endif
+#endif
 
 /* ====================================================================================
  * The definitions in this section are considered experimental.
@@ -272,8 +294,9 @@
  * Use them only in association with static linking.
  * ==================================================================================== */
 
-#define ZDICT_CONTENTSIZE_MIN 128
 #define ZDICT_DICTSIZE_MIN    256
+/* Deprecated: Remove in v1.6.0 */
+#define ZDICT_CONTENTSIZE_MIN 128
 
 /*! ZDICT_cover_params_t:
  *  k and d are the only required parameters.
@@ -318,7 +341,7 @@
  *        In general, it's recommended to provide a few thousands samples, though this can vary a lot.
  *        It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
  */
-ZDICTLIB_API size_t ZDICT_trainFromBuffer_cover(
+ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_cover(
           void *dictBuffer, size_t dictBufferCapacity,
     const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
           ZDICT_cover_params_t parameters);
@@ -340,7 +363,7 @@
  *          See ZDICT_trainFromBuffer() for details on failure modes.
  * Note: ZDICT_optimizeTrainFromBuffer_cover() requires about 8 bytes of memory for each input byte and additionally another 5 bytes of memory for each byte of memory for each thread.
  */
-ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover(
+ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_cover(
           void* dictBuffer, size_t dictBufferCapacity,
     const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
           ZDICT_cover_params_t* parameters);
@@ -361,7 +384,7 @@
  *        In general, it's recommended to provide a few thousands samples, though this can vary a lot.
  *        It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
  */
-ZDICTLIB_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer,
+ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_fastCover(void *dictBuffer,
                     size_t dictBufferCapacity, const void *samplesBuffer,
                     const size_t *samplesSizes, unsigned nbSamples,
                     ZDICT_fastCover_params_t parameters);
@@ -384,7 +407,7 @@
  *          See ZDICT_trainFromBuffer() for details on failure modes.
  * Note: ZDICT_optimizeTrainFromBuffer_fastCover() requires about 6 * 2^f bytes of memory for each thread.
  */
-ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer,
+ZDICTLIB_STATIC_API size_t ZDICT_optimizeTrainFromBuffer_fastCover(void* dictBuffer,
                     size_t dictBufferCapacity, const void* samplesBuffer,
                     const size_t* samplesSizes, unsigned nbSamples,
                     ZDICT_fastCover_params_t* parameters);
@@ -409,7 +432,7 @@
  *        It's recommended that total size of all samples be about ~x100 times the target size of dictionary.
  *  Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0.
  */
-ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy(
+ZDICTLIB_STATIC_API size_t ZDICT_trainFromBuffer_legacy(
     void* dictBuffer, size_t dictBufferCapacity,
     const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
     ZDICT_legacy_params_t parameters);
@@ -421,32 +444,31 @@
    or _CRT_SECURE_NO_WARNINGS in Visual.
    Otherwise, it's also possible to manually define ZDICT_DISABLE_DEPRECATE_WARNINGS */
 #ifdef ZDICT_DISABLE_DEPRECATE_WARNINGS
-#  define ZDICT_DEPRECATED(message) ZDICTLIB_API   /* disable deprecation warnings */
+#  define ZDICT_DEPRECATED(message) /* disable deprecation warnings */
 #else
 #  define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
 #  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
-#    define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API
+#    define ZDICT_DEPRECATED(message) [[deprecated(message)]]
 #  elif defined(__clang__) || (ZDICT_GCC_VERSION >= 405)
-#    define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message)))
+#    define ZDICT_DEPRECATED(message) __attribute__((deprecated(message)))
 #  elif (ZDICT_GCC_VERSION >= 301)
-#    define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated))
+#    define ZDICT_DEPRECATED(message) __attribute__((deprecated))
 #  elif defined(_MSC_VER)
-#    define ZDICT_DEPRECATED(message) ZDICTLIB_API __declspec(deprecated(message))
+#    define ZDICT_DEPRECATED(message) __declspec(deprecated(message))
 #  else
 #    pragma message("WARNING: You need to implement ZDICT_DEPRECATED for this compiler")
-#    define ZDICT_DEPRECATED(message) ZDICTLIB_API
+#    define ZDICT_DEPRECATED(message)
 #  endif
 #endif /* ZDICT_DISABLE_DEPRECATE_WARNINGS */
 
 ZDICT_DEPRECATED("use ZDICT_finalizeDictionary() instead")
+ZDICTLIB_STATIC_API
 size_t ZDICT_addEntropyTablesFromBuffer(void* dictBuffer, size_t dictContentSize, size_t dictBufferCapacity,
                                   const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples);
 
 
-#endif   /* ZDICT_STATIC_LINKING_ONLY */
+#endif   /* ZSTD_ZDICT_H_STATIC */
 
 #if defined (__cplusplus)
 }
 #endif
-
-#endif   /* DICTBUILDER_H_001 */
diff --git a/Utilities/cmzstd/lib/zstd.h b/Utilities/cmzstd/lib/zstd.h
index 4651e6c..e5c3f8b 100644
--- a/Utilities/cmzstd/lib/zstd.h
+++ b/Utilities/cmzstd/lib/zstd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,27 +14,61 @@
 #ifndef ZSTD_H_235446
 #define ZSTD_H_235446
 
-/* ======   Dependency   ======*/
+/* ======   Dependencies   ======*/
 #include <limits.h>   /* INT_MAX */
 #include <stddef.h>   /* size_t */
 
 
 /* =====   ZSTDLIB_API : control library symbols visibility   ===== */
-#ifndef ZSTDLIB_VISIBILITY
-#  if defined(__GNUC__) && (__GNUC__ >= 4)
-#    define ZSTDLIB_VISIBILITY __attribute__ ((visibility ("default")))
+#ifndef ZSTDLIB_VISIBLE
+   /* Backwards compatibility with old macro name */
+#  ifdef ZSTDLIB_VISIBILITY
+#    define ZSTDLIB_VISIBLE ZSTDLIB_VISIBILITY
+#  elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZSTDLIB_VISIBLE __attribute__ ((visibility ("default")))
 #  else
-#    define ZSTDLIB_VISIBILITY
+#    define ZSTDLIB_VISIBLE
 #  endif
 #endif
-#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
-#  define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBILITY
-#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
-#  define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
-#else
-#  define ZSTDLIB_API ZSTDLIB_VISIBILITY
+
+#ifndef ZSTDLIB_HIDDEN
+#  if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZSTDLIB_HIDDEN __attribute__ ((visibility ("hidden")))
+#  else
+#    define ZSTDLIB_HIDDEN
+#  endif
 #endif
 
+#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+#  define ZSTDLIB_API __declspec(dllexport) ZSTDLIB_VISIBLE
+#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+#  define ZSTDLIB_API __declspec(dllimport) ZSTDLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+#  define ZSTDLIB_API ZSTDLIB_VISIBLE
+#endif
+
+/* Deprecation warnings :
+ * Should these warnings be a problem, it is generally possible to disable them,
+ * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
+ * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
+ */
+#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
+#  define ZSTD_DEPRECATED(message) /* disable deprecation warnings */
+#else
+#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+#    define ZSTD_DEPRECATED(message) [[deprecated(message)]]
+#  elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
+#    define ZSTD_DEPRECATED(message) __attribute__((deprecated(message)))
+#  elif defined(__GNUC__) && (__GNUC__ >= 3)
+#    define ZSTD_DEPRECATED(message) __attribute__((deprecated))
+#  elif defined(_MSC_VER)
+#    define ZSTD_DEPRECATED(message) __declspec(deprecated(message))
+#  else
+#    pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
+#    define ZSTD_DEPRECATED(message)
+#  endif
+#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
+
 
 /*******************************************************************************
   Introduction
@@ -72,7 +106,7 @@
 /*------   Version   ------*/
 #define ZSTD_VERSION_MAJOR    1
 #define ZSTD_VERSION_MINOR    5
-#define ZSTD_VERSION_RELEASE  0
+#define ZSTD_VERSION_RELEASE  5
 #define ZSTD_VERSION_NUMBER  (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
 
 /*! ZSTD_versionNumber() :
@@ -114,7 +148,8 @@
 ***************************************/
 /*! ZSTD_compress() :
  *  Compresses `src` content as a single zstd compressed frame into already allocated `dst`.
- *  Hint : compression runs faster if `dstCapacity` >=  `ZSTD_compressBound(srcSize)`.
+ *  NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have
+ *        enough space to successfully compress the data.
  *  @return : compressed size written into `dst` (<= `dstCapacity),
  *            or an error code if it fails (which can be tested using ZSTD_isError()). */
 ZSTDLIB_API size_t ZSTD_compress( void* dst, size_t dstCapacity,
@@ -163,7 +198,9 @@
  *  "empty", "unknown" and "error" results to the same return value (0),
  *  while ZSTD_getFrameContentSize() gives them separate return values.
  * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */
-ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize")
+ZSTDLIB_API
+unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
 
 /*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+
  * `src` should point to the start of a ZSTD frame or skippable frame.
@@ -175,8 +212,30 @@
 
 
 /*======  Helper functions  ======*/
-#define ZSTD_COMPRESSBOUND(srcSize)   ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
-ZSTDLIB_API size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
+/* ZSTD_compressBound() :
+ * maximum compressed size in worst case single-pass scenario.
+ * When invoking `ZSTD_compress()` or any other one-pass compression function,
+ * it's recommended to provide @dstCapacity >= ZSTD_compressBound(srcSize)
+ * as it eliminates one potential failure scenario,
+ * aka not enough room in dst buffer to write the compressed frame.
+ * Note : ZSTD_compressBound() itself can fail, if @srcSize > ZSTD_MAX_INPUT_SIZE .
+ *        In which case, ZSTD_compressBound() will return an error code
+ *        which can be tested using ZSTD_isError().
+ *
+ * ZSTD_COMPRESSBOUND() :
+ * same as ZSTD_compressBound(), but as a macro.
+ * It can be used to produce constants, which can be useful for static allocation,
+ * for example to size a static array on stack.
+ * Will produce constant value 0 if srcSize too large.
+ */
+#define ZSTD_MAX_INPUT_SIZE ((sizeof(size_t)==8) ? 0xFF00FF00FF00FF00LLU : 0xFF00FF00U)
+#define ZSTD_COMPRESSBOUND(srcSize)   (((size_t)(srcSize) >= ZSTD_MAX_INPUT_SIZE) ? 0 : (srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
+ZSTDLIB_API size_t ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
+/* ZSTD_isError() :
+ * Most ZSTD_* functions returning a size_t value can be tested for error,
+ * using ZSTD_isError().
+ * @return 1 if error, 0 otherwise
+ */
 ZSTDLIB_API unsigned    ZSTD_isError(size_t code);          /*!< tells if a `size_t` function result is an error code */
 ZSTDLIB_API const char* ZSTD_getErrorName(size_t code);     /*!< provides readable string from an error code */
 ZSTDLIB_API int         ZSTD_minCLevel(void);               /*!< minimum negative compression level allowed, requires v1.4.0+ */
@@ -247,7 +306,7 @@
  *
  *   It's possible to reset all parameters to "default" using ZSTD_CCtx_reset().
  *
- *   This API supercedes all other "advanced" API entry points in the experimental section.
+ *   This API supersedes all other "advanced" API entry points in the experimental section.
  *   In the future, we expect to remove from experimental API entry points which are redundant with this API.
  */
 
@@ -417,8 +476,11 @@
      * ZSTD_c_stableOutBuffer
      * ZSTD_c_blockDelimiters
      * ZSTD_c_validateSequences
-     * ZSTD_c_splitBlocks
+     * ZSTD_c_useBlockSplitter
      * ZSTD_c_useRowMatchFinder
+     * ZSTD_c_prefetchCDictTables
+     * ZSTD_c_enableSeqProducerFallback
+     * ZSTD_c_maxBlockSize
      * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
      * note : never ever use experimentalParam? names directly;
      *        also, the enums values themselves are unstable and can still change.
@@ -437,7 +499,11 @@
      ZSTD_c_experimentalParam12=1009,
      ZSTD_c_experimentalParam13=1010,
      ZSTD_c_experimentalParam14=1011,
-     ZSTD_c_experimentalParam15=1012
+     ZSTD_c_experimentalParam15=1012,
+     ZSTD_c_experimentalParam16=1013,
+     ZSTD_c_experimentalParam17=1014,
+     ZSTD_c_experimentalParam18=1015,
+     ZSTD_c_experimentalParam19=1016
 } ZSTD_cParameter;
 
 typedef struct {
@@ -500,7 +566,7 @@
  *                  They will be used to compress next frame.
  *                  Resetting session never fails.
  *  - The parameters : changes all parameters back to "default".
- *                  This removes any reference to any dictionary too.
+ *                  This also removes any reference to any dictionary or external sequence producer.
  *                  Parameters can only be changed between 2 sessions (i.e. no compression is currently ongoing)
  *                  otherwise the reset fails, and function returns an error value (which can be tested using ZSTD_isError())
  *  - Both : similar to resetting the session, followed by resetting parameters.
@@ -513,7 +579,8 @@
  *  Should cctx hold data from a previously unfinished frame, everything about it is forgotten.
  *  - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
  *  - The function is always blocking, returns when compression is completed.
- *  Hint : compression runs faster if `dstCapacity` >=  `ZSTD_compressBound(srcSize)`.
+ *  NOTE: Providing `dstCapacity >= ZSTD_compressBound(srcSize)` guarantees that zstd will have
+ *        enough space to successfully compress the data, though it is possible it fails for other reasons.
  * @return : compressed size written into `dst` (<= `dstCapacity),
  *           or an error code if it fails (which can be tested using ZSTD_isError()).
  */
@@ -550,13 +617,15 @@
      * ZSTD_d_stableOutBuffer
      * ZSTD_d_forceIgnoreChecksum
      * ZSTD_d_refMultipleDDicts
+     * ZSTD_d_disableHuffmanAssembly
      * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
      * note : never ever use experimentalParam? names directly
      */
      ZSTD_d_experimentalParam1=1000,
      ZSTD_d_experimentalParam2=1001,
      ZSTD_d_experimentalParam3=1002,
-     ZSTD_d_experimentalParam4=1003
+     ZSTD_d_experimentalParam4=1003,
+     ZSTD_d_experimentalParam5=1004
 
 } ZSTD_dParameter;
 
@@ -735,8 +804,6 @@
  * This following is a legacy streaming API, available since v1.0+ .
  * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2().
  * It is redundant, but remains fully supported.
- * Streaming in combination with advanced parameters and dictionary compression
- * can only be used through the new API.
  ******************************************************************************/
 
 /*!
@@ -745,6 +812,9 @@
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any)
  *     ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
+ *
+ * Note that ZSTD_initCStream() clears any previously set dictionary. Use the new API
+ * to compress with a dictionary.
  */
 ZSTDLIB_API size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel);
 /*!
@@ -795,13 +865,31 @@
 
 /*===== Streaming decompression functions =====*/
 
-/* This function is redundant with the advanced API and equivalent to:
+/*! ZSTD_initDStream() :
+ * Initialize/reset DStream state for new decompression operation.
+ * Call before new decompression operation using same DStream.
  *
+ * Note : This function is redundant with the advanced API and equivalent to:
  *     ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
  *     ZSTD_DCtx_refDDict(zds, NULL);
  */
 ZSTDLIB_API size_t ZSTD_initDStream(ZSTD_DStream* zds);
 
+/*! ZSTD_decompressStream() :
+ * Streaming decompression function.
+ * Call repetitively to consume full input updating it as necessary.
+ * Function will update both input and output `pos` fields exposing current state via these fields:
+ * - `input.pos < input.size`, some input remaining and caller should provide remaining input
+ *   on the next call.
+ * - `output.pos < output.size`, decoder finished and flushed all remaining buffers.
+ * - `output.pos == output.size`, potentially uncflushed data present in the internal buffers,
+ *   call ZSTD_decompressStream() again to flush remaining data to output.
+ * Note : with no additional input, amount of data flushed <= ZSTD_BLOCKSIZE_MAX.
+ *
+ * @return : 0 when a frame is completely decoded and fully flushed,
+ *           or an error code, which can be tested using ZSTD_isError(),
+ *           or any other value > 0, which means there is some decoding or flushing to do to complete current frame.
+ */
 ZSTDLIB_API size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
 
 ZSTDLIB_API size_t ZSTD_DStreamInSize(void);    /*!< recommended size for input buffer */
@@ -920,7 +1008,7 @@
  *  If @return == 0, the dictID could not be decoded.
  *  This could for one of the following reasons :
  *  - The frame does not require a dictionary to be decoded (most common case).
- *  - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information.
+ *  - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information.
  *    Note : this use case also happens when using a non-conformant dictionary.
  *  - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
  *  - This is not a Zstandard frame.
@@ -932,9 +1020,11 @@
  * Advanced dictionary and prefix API (Requires v1.4.0+)
  *
  * This API allows dictionaries to be used with ZSTD_compress2(),
- * ZSTD_compressStream2(), and ZSTD_decompress(). Dictionaries are sticky, and
- * only reset with the context is reset with ZSTD_reset_parameters or
- * ZSTD_reset_session_and_parameters. Prefixes are single-use.
+ * ZSTD_compressStream2(), and ZSTD_decompressDCtx().
+ * Dictionaries are sticky, they remain valid when same context is re-used,
+ * they only reset when the context is reset
+ * with ZSTD_reset_parameters or ZSTD_reset_session_and_parameters.
+ * In contrast, Prefixes are single-use.
  ******************************************************************************/
 
 
@@ -944,8 +1034,9 @@
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  *  Special: Loading a NULL (or 0-size) dictionary invalidates previous dictionary,
  *           meaning "return to no-dictionary mode".
- *  Note 1 : Dictionary is sticky, it will be used for all future compressed frames.
- *           To return to "no-dictionary" situation, load a NULL dictionary (or reset parameters).
+ *  Note 1 : Dictionary is sticky, it will be used for all future compressed frames,
+ *           until parameters are reset, a new dictionary is loaded, or the dictionary
+ *           is explicitly invalidated by loading a NULL dictionary.
  *  Note 2 : Loading a dictionary involves building tables.
  *           It's also a CPU consuming operation, with non-negligible impact on latency.
  *           Tables are dependent on compression parameters, and for this reason,
@@ -954,11 +1045,15 @@
  *           Use experimental ZSTD_CCtx_loadDictionary_byReference() to reference content instead.
  *           In such a case, dictionary buffer must outlive its users.
  *  Note 4 : Use ZSTD_CCtx_loadDictionary_advanced()
- *           to precisely select how dictionary content must be interpreted. */
+ *           to precisely select how dictionary content must be interpreted.
+ *  Note 5 : This method does not benefit from LDM (long distance mode).
+ *           If you want to employ LDM on some large dictionary content,
+ *           prefer employing ZSTD_CCtx_refPrefix() described below.
+ */
 ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
 
 /*! ZSTD_CCtx_refCDict() : Requires v1.4.0+
- *  Reference a prepared dictionary, to be used for all next compressed frames.
+ *  Reference a prepared dictionary, to be used for all future compressed frames.
  *  Note that compression parameters are enforced from within CDict,
  *  and supersede any compression parameter previously set within CCtx.
  *  The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs.
@@ -977,6 +1072,7 @@
  *  Decompression will need same prefix to properly regenerate data.
  *  Compressing with a prefix is similar in outcome as performing a diff and compressing it,
  *  but performs much faster, especially during decompression (compression speed is tunable with compression level).
+ *  This method is compatible with LDM (long distance mode).
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  *  Special: Adding any prefix (including NULL) invalidates any previous prefix or dictionary
  *  Note 1 : Prefix buffer is referenced. It **must** outlive compression.
@@ -993,9 +1089,9 @@
                                  const void* prefix, size_t prefixSize);
 
 /*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+
- *  Create an internal DDict from dict buffer,
- *  to be used to decompress next frames.
- *  The dictionary remains valid for all future frames, until explicitly invalidated.
+ *  Create an internal DDict from dict buffer, to be used to decompress all future frames.
+ *  The dictionary remains valid for all future frames, until explicitly invalidated, or
+ *  a new dictionary is loaded.
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  *  Special : Adding a NULL (or 0-size) dictionary invalidates any previous dictionary,
  *            meaning "return to no-dictionary mode".
@@ -1019,9 +1115,10 @@
  *  The memory for the table is allocated on the first call to refDDict, and can be
  *  freed with ZSTD_freeDCtx().
  *
+ *  If called with ZSTD_d_refMultipleDDicts disabled (the default), only one dictionary
+ *  will be managed, and referencing a dictionary effectively "discards" any previous one.
+ *
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
- *  Note 1 : Currently, only one dictionary can be managed.
- *           Referencing a new dictionary effectively "discards" any previous one.
  *  Special: referencing a NULL DDict means "return to no-dictionary mode".
  *  Note 2 : DDict is just referenced, its lifetime must outlive its usage from DCtx.
  */
@@ -1073,27 +1170,16 @@
 #if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY)
 #define ZSTD_H_ZSTD_STATIC_LINKING_ONLY
 
-/* Deprecation warnings :
- * Should these warnings be a problem, it is generally possible to disable them,
- * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
- * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
- */
-#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
-#  define ZSTD_DEPRECATED(message) ZSTDLIB_API  /* disable deprecation warnings */
-#else
-#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
-#    define ZSTD_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_API
-#  elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
-#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated(message)))
-#  elif defined(__GNUC__) && (__GNUC__ >= 3)
-#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated))
-#  elif defined(_MSC_VER)
-#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __declspec(deprecated(message))
+/* This can be overridden externally to hide static symbols. */
+#ifndef ZSTDLIB_STATIC_API
+#  if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
+#    define ZSTDLIB_STATIC_API __declspec(dllexport) ZSTDLIB_VISIBLE
+#  elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
+#    define ZSTDLIB_STATIC_API __declspec(dllimport) ZSTDLIB_VISIBLE
 #  else
-#    pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
-#    define ZSTD_DEPRECATED(message) ZSTDLIB_API
+#    define ZSTDLIB_STATIC_API ZSTDLIB_VISIBLE
 #  endif
-#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
+#endif
 
 /****************************************************************************************
  *   experimental API (static linking only)
@@ -1129,6 +1215,7 @@
 #define ZSTD_TARGETLENGTH_MIN     0   /* note : comparing this constant to an unsigned results in a tautological test */
 #define ZSTD_STRATEGY_MIN        ZSTD_fast
 #define ZSTD_STRATEGY_MAX        ZSTD_btultra2
+#define ZSTD_BLOCKSIZE_MAX_MIN (1 << 10) /* The minimum valid max blocksize. Maximum blocksizes smaller than this make compressBound() inaccurate. */
 
 
 #define ZSTD_OVERLAPLOG_MIN       0
@@ -1157,9 +1244,6 @@
 #define ZSTD_SRCSIZEHINT_MIN        0
 #define ZSTD_SRCSIZEHINT_MAX        INT_MAX
 
-/* internal */
-#define ZSTD_HASHLOG3_MAX           17
-
 
 /* ---  Advanced types  --- */
 
@@ -1302,13 +1386,17 @@
 } ZSTD_literalCompressionMode_e;
 
 typedef enum {
-  ZSTD_urm_auto = 0,                   /* Automatically determine whether or not we use row matchfinder */
-  ZSTD_urm_disableRowMatchFinder = 1,  /* Never use row matchfinder */
-  ZSTD_urm_enableRowMatchFinder = 2    /* Always use row matchfinder when applicable */
-} ZSTD_useRowMatchFinderMode_e;
+  /* Note: This enum controls features which are conditionally beneficial. Zstd typically will make a final
+   * decision on whether or not to enable the feature (ZSTD_ps_auto), but setting the switch to ZSTD_ps_enable
+   * or ZSTD_ps_disable allow for a force enable/disable the feature.
+   */
+  ZSTD_ps_auto = 0,         /* Let the library automatically determine whether the feature shall be enabled */
+  ZSTD_ps_enable = 1,       /* Force-enable the feature */
+  ZSTD_ps_disable = 2       /* Do not use the feature */
+} ZSTD_paramSwitch_e;
 
 /***************************************
-*  Frame size functions
+*  Frame header and size functions
 ***************************************/
 
 /*! ZSTD_findDecompressedSize() :
@@ -1332,7 +1420,7 @@
  *   note 5 : ZSTD_findDecompressedSize handles multiple frames, and so it must traverse the input to
  *            read each contained frame header.  This is fast as most of the data is skipped,
  *            however it does mean that all frame data must be present and valid. */
-ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
 
 /*! ZSTD_decompressBound() :
  *  `src` should point to the start of a series of ZSTD encoded and/or skippable frames
@@ -1347,41 +1435,121 @@
  *  note 3  : when the decompressed size field isn't available, the upper-bound for that frame is calculated by:
  *              upper-bound = # blocks * min(128 KB, Window_Size)
  */
-ZSTDLIB_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize);
 
 /*! ZSTD_frameHeaderSize() :
  *  srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX.
  * @return : size of the Frame Header,
  *           or an error code (if srcSize is too small) */
-ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
+
+typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
+typedef struct {
+    unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
+    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
+    unsigned blockSizeMax;
+    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
+    unsigned headerSize;
+    unsigned dictID;
+    unsigned checksumFlag;
+    unsigned _reserved1;
+    unsigned _reserved2;
+} ZSTD_frameHeader;
+
+/*! ZSTD_getFrameHeader() :
+ *  decode Frame Header, or requires larger `srcSize`.
+ * @return : 0, `zfhPtr` is correctly filled,
+ *          >0, `srcSize` is too small, value is wanted `srcSize` amount,
+ *           or an error code, which can be tested using ZSTD_isError() */
+ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
+/*! ZSTD_getFrameHeader_advanced() :
+ *  same as ZSTD_getFrameHeader(),
+ *  with added capability to select a format (like ZSTD_f_zstd1_magicless) */
+ZSTDLIB_STATIC_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
+
+/*! ZSTD_decompressionMargin() :
+ * Zstd supports in-place decompression, where the input and output buffers overlap.
+ * In this case, the output buffer must be at least (Margin + Output_Size) bytes large,
+ * and the input buffer must be at the end of the output buffer.
+ *
+ *  _______________________ Output Buffer ________________________
+ * |                                                              |
+ * |                                        ____ Input Buffer ____|
+ * |                                       |                      |
+ * v                                       v                      v
+ * |---------------------------------------|-----------|----------|
+ * ^                                                   ^          ^
+ * |___________________ Output_Size ___________________|_ Margin _|
+ *
+ * NOTE: See also ZSTD_DECOMPRESSION_MARGIN().
+ * NOTE: This applies only to single-pass decompression through ZSTD_decompress() or
+ * ZSTD_decompressDCtx().
+ * NOTE: This function supports multi-frame input.
+ *
+ * @param src The compressed frame(s)
+ * @param srcSize The size of the compressed frame(s)
+ * @returns The decompression margin or an error that can be checked with ZSTD_isError().
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_decompressionMargin(const void* src, size_t srcSize);
+
+/*! ZSTD_DECOMPRESS_MARGIN() :
+ * Similar to ZSTD_decompressionMargin(), but instead of computing the margin from
+ * the compressed frame, compute it from the original size and the blockSizeLog.
+ * See ZSTD_decompressionMargin() for details.
+ *
+ * WARNING: This macro does not support multi-frame input, the input must be a single
+ * zstd frame. If you need that support use the function, or implement it yourself.
+ *
+ * @param originalSize The original uncompressed size of the data.
+ * @param blockSize    The block size == MIN(windowSize, ZSTD_BLOCKSIZE_MAX).
+ *                     Unless you explicitly set the windowLog smaller than
+ *                     ZSTD_BLOCKSIZELOG_MAX you can just use ZSTD_BLOCKSIZE_MAX.
+ */
+#define ZSTD_DECOMPRESSION_MARGIN(originalSize, blockSize) ((size_t)(                                              \
+        ZSTD_FRAMEHEADERSIZE_MAX                                                              /* Frame header */ + \
+        4                                                                                         /* checksum */ + \
+        ((originalSize) == 0 ? 0 : 3 * (((originalSize) + (blockSize) - 1) / blockSize)) /* 3 bytes per block */ + \
+        (blockSize)                                                                    /* One block of margin */   \
+    ))
 
 typedef enum {
   ZSTD_sf_noBlockDelimiters = 0,         /* Representation of ZSTD_Sequence has no block delimiters, sequences only */
   ZSTD_sf_explicitBlockDelimiters = 1    /* Representation of ZSTD_Sequence contains explicit block delimiters */
 } ZSTD_sequenceFormat_e;
 
+/*! ZSTD_sequenceBound() :
+ * `srcSize` : size of the input buffer
+ *  @return : upper-bound for the number of sequences that can be generated
+ *            from a buffer of srcSize bytes
+ *
+ *  note : returns number of sequences - to get bytes, multiply by sizeof(ZSTD_Sequence).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_sequenceBound(size_t srcSize);
+
 /*! ZSTD_generateSequences() :
- * Generate sequences using ZSTD_compress2, given a source buffer.
+ * Generate sequences using ZSTD_compress2(), given a source buffer.
  *
  * Each block will end with a dummy sequence
  * with offset == 0, matchLength == 0, and litLength == length of last literals.
  * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0)
  * simply acts as a block delimiter.
  *
- * zc can be used to insert custom compression params.
- * This function invokes ZSTD_compress2
+ * @zc can be used to insert custom compression params.
+ * This function invokes ZSTD_compress2().
  *
  * The output of this function can be fed into ZSTD_compressSequences() with CCtx
  * setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters
  * @return : number of sequences generated
  */
 
-ZSTDLIB_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
-                                          size_t outSeqsSize, const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t
+ZSTD_generateSequences( ZSTD_CCtx* zc,
+                        ZSTD_Sequence* outSeqs, size_t outSeqsSize,
+                        const void* src, size_t srcSize);
 
 /*! ZSTD_mergeBlockDelimiters() :
  * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
- * by merging them into into the literals of the next sequence.
+ * by merging them into the literals of the next sequence.
  *
  * As such, the final generated result has no explicit representation of block boundaries,
  * and the final last literals segment is not represented in the sequences.
@@ -1390,10 +1558,12 @@
  * setting of ZSTD_c_blockDelimiters as ZSTD_sf_noBlockDelimiters
  * @return : number of sequences left after merging
  */
-ZSTDLIB_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
+ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
 
 /*! ZSTD_compressSequences() :
- * Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst.
+ * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst.
+ * @src contains the entire input (not just the literals).
+ * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
  * If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
  * The entire source is compressed into a single frame.
  *
@@ -1418,17 +1588,18 @@
  * Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused.
  * Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly,
  *         and cannot emit an RLE block that disagrees with the repcode history
- * @return : final compressed size or a ZSTD error.
+ * @return : final compressed size, or a ZSTD error code.
  */
-ZSTDLIB_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
-                                  const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
-                                  const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t
+ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize,
+                        const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+                        const void* src, size_t srcSize);
 
 
 /*! ZSTD_writeSkippableFrame() :
  * Generates a zstd skippable frame containing data given by src, and writes it to dst buffer.
  *
- * Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number,
+ * Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number,
  * ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15.
  * As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so
  * the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant.
@@ -1438,9 +1609,29 @@
  *
  * @return : number of bytes written or a ZSTD error.
  */
-ZSTDLIB_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
+ZSTDLIB_STATIC_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
                                             const void* src, size_t srcSize, unsigned magicVariant);
 
+/*! ZSTD_readSkippableFrame() :
+ * Retrieves a zstd skippable frame containing data given by src, and writes it to dst buffer.
+ *
+ * The parameter magicVariant will receive the magicVariant that was supplied when the frame was written,
+ * i.e. magicNumber - ZSTD_MAGIC_SKIPPABLE_START.  This can be NULL if the caller is not interested
+ * in the magicVariant.
+ *
+ * Returns an error if destination buffer is not large enough, or if the frame is not skippable.
+ *
+ * @return : number of bytes written or a ZSTD error.
+ */
+ZSTDLIB_API size_t ZSTD_readSkippableFrame(void* dst, size_t dstCapacity, unsigned* magicVariant,
+                                            const void* src, size_t srcSize);
+
+/*! ZSTD_isSkippableFrame() :
+ *  Tells if the content of `buffer` starts with a valid Frame Identifier for a skippable frame.
+ */
+ZSTDLIB_API unsigned ZSTD_isSkippableFrame(const void* buffer, size_t size);
+
+
 
 /***************************************
 *  Memory management
@@ -1466,13 +1657,16 @@
  *  and ZSTD_estimateCCtxSize_usingCCtxParams(), which can be used in tandem with ZSTD_CCtxParams_setParameter().
  *  Both can be used to estimate memory using custom compression parameters and arbitrary srcSize limits.
  *
- *  Note 2 : only single-threaded compression is supported.
+ *  Note : only single-threaded compression is supported.
  *  ZSTD_estimateCCtxSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.
+ *
+ *  Note 2 : ZSTD_estimateCCtxSize* functions are not compatible with the Block-Level Sequence Producer API at this time.
+ *  Size estimates assume that no external sequence producer is registered.
  */
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
-ZSTDLIB_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
-ZSTDLIB_API size_t ZSTD_estimateDCtxSize(void);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize(int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDCtxSize(void);
 
 /*! ZSTD_estimateCStreamSize() :
  *  ZSTD_estimateCStreamSize() will provide a budget large enough for any compression level up to selected one.
@@ -1486,21 +1680,26 @@
  *  or deducted from a valid frame Header, using ZSTD_estimateDStreamSize_fromFrame();
  *  Note : if streaming is init with function ZSTD_init?Stream_usingDict(),
  *         an internal ?Dict will be created, which additional size is not estimated here.
- *         In this case, get total size by adding ZSTD_estimate?DictSize */
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize(int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams);
-ZSTDLIB_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params);
-ZSTDLIB_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
-ZSTDLIB_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
+ *         In this case, get total size by adding ZSTD_estimate?DictSize
+ *  Note 2 : only single-threaded compression is supported.
+ *  ZSTD_estimateCStreamSize_usingCCtxParams() will return an error code if ZSTD_c_nbWorkers is >= 1.
+ *  Note 3 : ZSTD_estimateCStreamSize* functions are not compatible with the Block-Level Sequence Producer API at this time.
+ *  Size estimates assume that no external sequence producer is registered.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize(int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCStreamSize_usingCCtxParams(const ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize(size_t windowSize);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDStreamSize_fromFrame(const void* src, size_t srcSize);
 
 /*! ZSTD_estimate?DictSize() :
  *  ZSTD_estimateCDictSize() will bet that src size is relatively "small", and content is copied, like ZSTD_createCDict().
  *  ZSTD_estimateCDictSize_advanced() makes it possible to control compression parameters precisely, like ZSTD_createCDict_advanced().
  *  Note : dictionaries created by reference (`ZSTD_dlm_byRef`) are logically smaller.
  */
-ZSTDLIB_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
-ZSTDLIB_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateCDictSize_advanced(size_t dictSize, ZSTD_compressionParameters cParams, ZSTD_dictLoadMethod_e dictLoadMethod);
+ZSTDLIB_STATIC_API size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod);
 
 /*! ZSTD_initStatic*() :
  *  Initialize an object using a pre-allocated fixed-size buffer.
@@ -1523,20 +1722,20 @@
  *  Limitation 2 : static cctx currently not compatible with multi-threading.
  *  Limitation 3 : static dctx is incompatible with legacy support.
  */
-ZSTDLIB_API ZSTD_CCtx*    ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
-ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
+ZSTDLIB_STATIC_API ZSTD_CCtx*    ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
 
-ZSTDLIB_API ZSTD_DCtx*    ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
-ZSTDLIB_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
+ZSTDLIB_STATIC_API ZSTD_DCtx*    ZSTD_initStaticDCtx(void* workspace, size_t workspaceSize);
+ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_initStaticDStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticDCtx() */
 
-ZSTDLIB_API const ZSTD_CDict* ZSTD_initStaticCDict(
+ZSTDLIB_STATIC_API const ZSTD_CDict* ZSTD_initStaticCDict(
                                         void* workspace, size_t workspaceSize,
                                         const void* dict, size_t dictSize,
                                         ZSTD_dictLoadMethod_e dictLoadMethod,
                                         ZSTD_dictContentType_e dictContentType,
                                         ZSTD_compressionParameters cParams);
 
-ZSTDLIB_API const ZSTD_DDict* ZSTD_initStaticDDict(
+ZSTDLIB_STATIC_API const ZSTD_DDict* ZSTD_initStaticDDict(
                                         void* workspace, size_t workspaceSize,
                                         const void* dict, size_t dictSize,
                                         ZSTD_dictLoadMethod_e dictLoadMethod,
@@ -1557,44 +1756,44 @@
 #endif
 ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
 
-ZSTDLIB_API ZSTD_CCtx*    ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DCtx*    ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
-ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_CCtx*    ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_DCtx*    ZSTD_createDCtx_advanced(ZSTD_customMem customMem);
+ZSTDLIB_STATIC_API ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
 
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictSize,
                                                   ZSTD_dictLoadMethod_e dictLoadMethod,
                                                   ZSTD_dictContentType_e dictContentType,
                                                   ZSTD_compressionParameters cParams,
                                                   ZSTD_customMem customMem);
 
-/* ! Thread pool :
- * These prototypes make it possible to share a thread pool among multiple compression contexts.
- * This can limit resources for applications with multiple threads where each one uses
- * a threaded compression mode (via ZSTD_c_nbWorkers parameter).
- * ZSTD_createThreadPool creates a new thread pool with a given number of threads.
- * Note that the lifetime of such pool must exist while being used.
- * ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value
- * to use an internal thread pool).
- * ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer.
+/*! Thread pool :
+ *  These prototypes make it possible to share a thread pool among multiple compression contexts.
+ *  This can limit resources for applications with multiple threads where each one uses
+ *  a threaded compression mode (via ZSTD_c_nbWorkers parameter).
+ *  ZSTD_createThreadPool creates a new thread pool with a given number of threads.
+ *  Note that the lifetime of such pool must exist while being used.
+ *  ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value
+ *  to use an internal thread pool).
+ *  ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer.
  */
 typedef struct POOL_ctx_s ZSTD_threadPool;
-ZSTDLIB_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads);
-ZSTDLIB_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool);  /* accept NULL pointer */
-ZSTDLIB_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool);
+ZSTDLIB_STATIC_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads);
+ZSTDLIB_STATIC_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool);  /* accept NULL pointer */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool);
 
 
 /*
  * This API is temporary and is expected to change or disappear in the future!
  */
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2(
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_advanced2(
     const void* dict, size_t dictSize,
     ZSTD_dictLoadMethod_e dictLoadMethod,
     ZSTD_dictContentType_e dictContentType,
     const ZSTD_CCtx_params* cctxParams,
     ZSTD_customMem customMem);
 
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(
+ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_advanced(
     const void* dict, size_t dictSize,
     ZSTD_dictLoadMethod_e dictLoadMethod,
     ZSTD_dictContentType_e dictContentType,
@@ -1611,22 +1810,22 @@
  *  As a consequence, `dictBuffer` **must** outlive CDict,
  *  and its content must remain unmodified throughout the lifetime of CDict.
  *  note: equivalent to ZSTD_createCDict_advanced(), with dictLoadMethod==ZSTD_dlm_byRef */
-ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
+ZSTDLIB_STATIC_API ZSTD_CDict* ZSTD_createCDict_byReference(const void* dictBuffer, size_t dictSize, int compressionLevel);
 
 /*! ZSTD_getCParams() :
  * @return ZSTD_compressionParameters structure for a selected compression level and estimated srcSize.
  * `estimatedSrcSize` value is optional, select 0 if not known */
-ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
 
 /*! ZSTD_getParams() :
  *  same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`.
  *  All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */
-ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
+ZSTDLIB_STATIC_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
 
 /*! ZSTD_checkCParams() :
  *  Ensure param values remain within authorized range.
  * @return 0 on success, or an error code (can be checked with ZSTD_isError()) */
-ZSTDLIB_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
+ZSTDLIB_STATIC_API size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
 
 /*! ZSTD_adjustCParams() :
  *  optimize params for a given `srcSize` and `dictSize`.
@@ -1634,24 +1833,47 @@
  * `dictSize` must be `0` when there is no dictionary.
  *  cPar can be invalid : all parameters will be clamped within valid range in the @return struct.
  *  This function never fails (wide contract) */
-ZSTDLIB_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize);
+ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize);
+
+/*! ZSTD_CCtx_setCParams() :
+ *  Set all parameters provided within @p cparams into the working @p cctx.
+ *  Note : if modifying parameters during compression (MT mode only),
+ *         note that changes to the .windowLog parameter will be ignored.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ *         On failure, no parameters are updated.
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setCParams(ZSTD_CCtx* cctx, ZSTD_compressionParameters cparams);
+
+/*! ZSTD_CCtx_setFParams() :
+ *  Set all parameters provided within @p fparams into the working @p cctx.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setFParams(ZSTD_CCtx* cctx, ZSTD_frameParameters fparams);
+
+/*! ZSTD_CCtx_setParams() :
+ *  Set all parameters provided within @p params into the working @p cctx.
+ * @return 0 on success, or an error code (can be checked with ZSTD_isError()).
+ */
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParams(ZSTD_CCtx* cctx, ZSTD_parameters params);
 
 /*! ZSTD_compress_advanced() :
  *  Note : this function is now DEPRECATED.
  *         It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters.
  *  This prototype will generate compilation warnings. */
 ZSTD_DEPRECATED("use ZSTD_compress2")
+ZSTDLIB_STATIC_API
 size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
-                                          void* dst, size_t dstCapacity,
-                                    const void* src, size_t srcSize,
-                                    const void* dict,size_t dictSize,
-                                          ZSTD_parameters params);
+                              void* dst, size_t dstCapacity,
+                        const void* src, size_t srcSize,
+                        const void* dict,size_t dictSize,
+                              ZSTD_parameters params);
 
 /*! ZSTD_compress_usingCDict_advanced() :
  *  Note : this function is now DEPRECATED.
  *         It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters.
  *  This prototype will generate compilation warnings. */
 ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
+ZSTDLIB_STATIC_API
 size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
                                               void* dst, size_t dstCapacity,
                                         const void* src, size_t srcSize,
@@ -1662,18 +1884,18 @@
 /*! ZSTD_CCtx_loadDictionary_byReference() :
  *  Same as ZSTD_CCtx_loadDictionary(), but dictionary content is referenced, instead of being copied into CCtx.
  *  It saves some memory, but also requires that `dict` outlives its usage within `cctx` */
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_byReference(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
 
 /*! ZSTD_CCtx_loadDictionary_advanced() :
  *  Same as ZSTD_CCtx_loadDictionary(), but gives finer control over
  *  how to load the dictionary (by copy ? by reference ?)
  *  and how to interpret it (automatic ? force raw mode ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_loadDictionary_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
 
 /*! ZSTD_CCtx_refPrefix_advanced() :
  *  Same as ZSTD_CCtx_refPrefix(), but gives finer control over
  *  how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
 
 /* ===   experimental parameters   === */
 /* these parameters can be used with ZSTD_setParameter()
@@ -1712,9 +1934,15 @@
  * See the comments on that enum for an explanation of the feature. */
 #define ZSTD_c_forceAttachDict ZSTD_c_experimentalParam4
 
-/* Controls how the literals are compressed (default is auto).
- * The value must be of type ZSTD_literalCompressionMode_e.
- * See ZSTD_literalCompressionMode_e enum definition for details.
+/* Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never compress literals.
+ * Set to ZSTD_ps_enable to always compress literals. (Note: uncompressed literals
+ * may still be emitted if huffman is not beneficial to use.)
+ *
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * literals compression based on the compression parameters - specifically,
+ * negative compression levels do not use literal compression.
  */
 #define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5
 
@@ -1777,7 +2005,7 @@
  *
  * Note that this means that the CDict tables can no longer be copied into the
  * CCtx, so the dict attachment mode ZSTD_dictForceCopy will no longer be
- * useable. The dictionary can only be attached or reloaded.
+ * usable. The dictionary can only be attached or reloaded.
  *
  * In general, you should expect compression to be faster--sometimes very much
  * so--and CDict creation to be slightly slower. Eventually, we will probably
@@ -1789,13 +2017,16 @@
  * Experimental parameter.
  * Default is 0 == disabled. Set to 1 to enable.
  *
- * Tells the compressor that the ZSTD_inBuffer will ALWAYS be the same
- * between calls, except for the modifications that zstd makes to pos (the
- * caller must not modify pos). This is checked by the compressor, and
- * compression will fail if it ever changes. This means the only flush
- * mode that makes sense is ZSTD_e_end, so zstd will error if ZSTD_e_end
- * is not used. The data in the ZSTD_inBuffer in the range [src, src + pos)
- * MUST not be modified during compression or you will get data corruption.
+ * Tells the compressor that input data presented with ZSTD_inBuffer
+ * will ALWAYS be the same between calls.
+ * Technically, the @src pointer must never be changed,
+ * and the @pos field can only be updated by zstd.
+ * However, it's possible to increase the @size field,
+ * allowing scenarios where more data can be appended after compressions starts.
+ * These conditions are checked by the compressor,
+ * and compression will fail if they are not respected.
+ * Also, data in the ZSTD_inBuffer within the range [src, src + pos)
+ * MUST not be modified during compression or it will result in data corruption.
  *
  * When this flag is enabled zstd won't allocate an input window buffer,
  * because the user guarantees it can reference the ZSTD_inBuffer until
@@ -1803,18 +2034,15 @@
  * large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also
  * avoid the memcpy() from the input buffer to the input window buffer.
  *
- * NOTE: ZSTD_compressStream2() will error if ZSTD_e_end is not used.
- * That means this flag cannot be used with ZSTD_compressStream().
- *
  * NOTE: So long as the ZSTD_inBuffer always points to valid memory, using
  * this flag is ALWAYS memory safe, and will never access out-of-bounds
- * memory. However, compression WILL fail if you violate the preconditions.
+ * memory. However, compression WILL fail if conditions are not respected.
  *
- * WARNING: The data in the ZSTD_inBuffer in the range [dst, dst + pos) MUST
- * not be modified during compression or you will get data corruption. This
- * is because zstd needs to reference data in the ZSTD_inBuffer to find
+ * WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST
+ * not be modified during compression or it will result in data corruption.
+ * This is because zstd needs to reference data in the ZSTD_inBuffer to find
  * matches. Normally zstd maintains its own window buffer for this purpose,
- * but passing this flag tells zstd to use the user provided buffer.
+ * but passing this flag tells zstd to rely on user provided buffer instead.
  */
 #define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9
 
@@ -1859,30 +2087,33 @@
  * Without validation, providing a sequence that does not conform to the zstd spec will cause
  * undefined behavior, and may produce a corrupted block.
  *
- * With validation enabled, a if sequence is invalid (see doc/zstd_compression_format.md for
+ * With validation enabled, if sequence is invalid (see doc/zstd_compression_format.md for
  * specifics regarding offset/matchlength requirements) then the function will bail out and
  * return an error.
  *
  */
 #define ZSTD_c_validateSequences ZSTD_c_experimentalParam12
 
-/* ZSTD_c_splitBlocks
- * Default is 0 == disabled. Set to 1 to enable block splitting.
+/* ZSTD_c_useBlockSplitter
+ * Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never use block splitter.
+ * Set to ZSTD_ps_enable to always use block splitter.
  *
- * Will attempt to split blocks in order to improve compression ratio at the cost of speed.
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * block splitting based on the compression parameters.
  */
-#define ZSTD_c_splitBlocks ZSTD_c_experimentalParam13
+#define ZSTD_c_useBlockSplitter ZSTD_c_experimentalParam13
 
 /* ZSTD_c_useRowMatchFinder
- * Default is ZSTD_urm_auto.
- * Controlled with ZSTD_useRowMatchFinderMode_e enum.
+ * Controlled with ZSTD_paramSwitch_e enum.
+ * Default is ZSTD_ps_auto.
+ * Set to ZSTD_ps_disable to never use row-based matchfinder.
+ * Set to ZSTD_ps_enable to force usage of row-based matchfinder.
  *
- * By default, in ZSTD_urm_auto, when finalizing the compression parameters, the library
- * will decide at runtime whether to use the row-based matchfinder based on support for SIMD
- * instructions as well as the windowLog.
- *
- * Set to ZSTD_urm_disableRowMatchFinder to never use row-based matchfinder.
- * Set to ZSTD_urm_enableRowMatchFinder to force usage of row-based matchfinder.
+ * By default, in ZSTD_ps_auto, the library will decide at runtime whether to use
+ * the row-based matchfinder based on support for SIMD instructions and the window log.
+ * Note that this only pertains to compression strategies: greedy, lazy, and lazy2
  */
 #define ZSTD_c_useRowMatchFinder ZSTD_c_experimentalParam14
 
@@ -1906,12 +2137,85 @@
  */
 #define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15
 
+/* ZSTD_c_prefetchCDictTables
+ * Controlled with ZSTD_paramSwitch_e enum. Default is ZSTD_ps_auto.
+ *
+ * In some situations, zstd uses CDict tables in-place rather than copying them
+ * into the working context. (See docs on ZSTD_dictAttachPref_e above for details).
+ * In such situations, compression speed is seriously impacted when CDict tables are
+ * "cold" (outside CPU cache). This parameter instructs zstd to prefetch CDict tables
+ * when they are used in-place.
+ *
+ * For sufficiently small inputs, the cost of the prefetch will outweigh the benefit.
+ * For sufficiently large inputs, zstd will by default memcpy() CDict tables
+ * into the working context, so there is no need to prefetch. This parameter is
+ * targeted at a middle range of input sizes, where a prefetch is cheap enough to be
+ * useful but memcpy() is too expensive. The exact range of input sizes where this
+ * makes sense is best determined by careful experimentation.
+ *
+ * Note: for this parameter, ZSTD_ps_auto is currently equivalent to ZSTD_ps_disable,
+ * but in the future zstd may conditionally enable this feature via an auto-detection
+ * heuristic for cold CDicts.
+ * Use ZSTD_ps_disable to opt out of prefetching under any circumstances.
+ */
+#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16
+
+/* ZSTD_c_enableSeqProducerFallback
+ * Allowed values are 0 (disable) and 1 (enable). The default setting is 0.
+ *
+ * Controls whether zstd will fall back to an internal sequence producer if an
+ * external sequence producer is registered and returns an error code. This fallback
+ * is block-by-block: the internal sequence producer will only be called for blocks
+ * where the external sequence producer returns an error code. Fallback parsing will
+ * follow any other cParam settings, such as compression level, the same as in a
+ * normal (fully-internal) compression operation.
+ *
+ * The user is strongly encouraged to read the full Block-Level Sequence Producer API
+ * documentation (below) before setting this parameter. */
+#define ZSTD_c_enableSeqProducerFallback ZSTD_c_experimentalParam17
+
+/* ZSTD_c_maxBlockSize
+ * Allowed values are between 1KB and ZSTD_BLOCKSIZE_MAX (128KB).
+ * The default is ZSTD_BLOCKSIZE_MAX, and setting to 0 will set to the default.
+ *
+ * This parameter can be used to set an upper bound on the blocksize
+ * that overrides the default ZSTD_BLOCKSIZE_MAX. It cannot be used to set upper
+ * bounds greater than ZSTD_BLOCKSIZE_MAX or bounds lower than 1KB (will make
+ * compressBound() inaccurate). Only currently meant to be used for testing.
+ *
+ */
+#define ZSTD_c_maxBlockSize ZSTD_c_experimentalParam18
+
+/* ZSTD_c_searchForExternalRepcodes
+ * This parameter affects how zstd parses external sequences, such as sequences
+ * provided through the compressSequences() API or from an external block-level
+ * sequence producer.
+ *
+ * If set to ZSTD_ps_enable, the library will check for repeated offsets in
+ * external sequences, even if those repcodes are not explicitly indicated in
+ * the "rep" field. Note that this is the only way to exploit repcode matches
+ * while using compressSequences() or an external sequence producer, since zstd
+ * currently ignores the "rep" field of external sequences.
+ *
+ * If set to ZSTD_ps_disable, the library will not exploit repeated offsets in
+ * external sequences, regardless of whether the "rep" field has been set. This
+ * reduces sequence compression overhead by about 25% while sacrificing some
+ * compression ratio.
+ *
+ * The default value is ZSTD_ps_auto, for which the library will enable/disable
+ * based on compression level.
+ *
+ * Note: for now, this param only has an effect if ZSTD_c_blockDelimiters is
+ * set to ZSTD_sf_explicitBlockDelimiters. That may change in the future.
+ */
+#define ZSTD_c_searchForExternalRepcodes ZSTD_c_experimentalParam19
+
 /*! ZSTD_CCtx_getParameter() :
  *  Get the requested compression parameter value, selected by enum ZSTD_cParameter,
  *  and store it into int* value.
  * @return : 0, or an error code (which can be tested with ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
 
 
 /*! ZSTD_CCtx_params :
@@ -1931,25 +2235,25 @@
  *  This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams()
  *  for static allocation of CCtx for single-threaded compression.
  */
-ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
-ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);  /* accept NULL pointer */
+ZSTDLIB_STATIC_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
+ZSTDLIB_STATIC_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);  /* accept NULL pointer */
 
 /*! ZSTD_CCtxParams_reset() :
  *  Reset params to default values.
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_reset(ZSTD_CCtx_params* params);
 
 /*! ZSTD_CCtxParams_init() :
  *  Initializes the compression parameters of cctxParams according to
  *  compression level. All other parameters are reset to their default values.
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel);
 
 /*! ZSTD_CCtxParams_init_advanced() :
  *  Initializes the compression and frame parameters of cctxParams according to
  *  params. All other parameters are reset to their default values.
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
 
 /*! ZSTD_CCtxParams_setParameter() : Requires v1.4.0+
  *  Similar to ZSTD_CCtx_setParameter.
@@ -1959,14 +2263,14 @@
  * @result : a code representing success or failure (which can be tested with
  *           ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
 
 /*! ZSTD_CCtxParams_getParameter() :
  * Similar to ZSTD_CCtx_getParameter.
  * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter.
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
+ZSTDLIB_STATIC_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
 
 /*! ZSTD_CCtx_setParametersUsingCCtxParams() :
  *  Apply a set of ZSTD_CCtx_params to the compression context.
@@ -1975,7 +2279,7 @@
  *    if nbWorkers>=1, new parameters will be picked up at next job,
  *       with a few restrictions (windowLog, pledgedSrcSize, nbWorkers, jobSize, and overlapLog are not updated).
  */
-ZSTDLIB_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
+ZSTDLIB_STATIC_API size_t ZSTD_CCtx_setParametersUsingCCtxParams(
         ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params);
 
 /*! ZSTD_compressStream2_simpleArgs() :
@@ -1984,7 +2288,7 @@
  *  This variant might be helpful for binders from dynamic languages
  *  which have troubles handling structures containing memory pointers.
  */
-ZSTDLIB_API size_t ZSTD_compressStream2_simpleArgs (
+ZSTDLIB_STATIC_API size_t ZSTD_compressStream2_simpleArgs (
                             ZSTD_CCtx* cctx,
                             void* dst, size_t dstCapacity, size_t* dstPos,
                       const void* src, size_t srcSize, size_t* srcPos,
@@ -2000,33 +2304,33 @@
  *  Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0.
  *  Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled.
  *  Note 3 : Skippable Frame Identifiers are considered valid. */
-ZSTDLIB_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
+ZSTDLIB_STATIC_API unsigned ZSTD_isFrame(const void* buffer, size_t size);
 
 /*! ZSTD_createDDict_byReference() :
  *  Create a digested dictionary, ready to start decompression operation without startup delay.
  *  Dictionary content is referenced, and therefore stays in dictBuffer.
  *  It is important that dictBuffer outlives DDict,
  *  it must remain read accessible throughout the lifetime of DDict */
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
+ZSTDLIB_STATIC_API ZSTD_DDict* ZSTD_createDDict_byReference(const void* dictBuffer, size_t dictSize);
 
 /*! ZSTD_DCtx_loadDictionary_byReference() :
  *  Same as ZSTD_DCtx_loadDictionary(),
  *  but references `dict` content instead of copying it into `dctx`.
  *  This saves memory if `dict` remains around.,
  *  However, it's imperative that `dict` remains accessible (and unmodified) while being used, so it must outlive decompression. */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_byReference(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
 
 /*! ZSTD_DCtx_loadDictionary_advanced() :
  *  Same as ZSTD_DCtx_loadDictionary(),
  *  but gives direct control over
  *  how to load the dictionary (by copy ? by reference ?)
  *  and how to interpret it (automatic ? force raw mode ? full mode only ?). */
-ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_loadDictionary_advanced(ZSTD_DCtx* dctx, const void* dict, size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMethod, ZSTD_dictContentType_e dictContentType);
 
 /*! ZSTD_DCtx_refPrefix_advanced() :
  *  Same as ZSTD_DCtx_refPrefix(), but gives finer control over
  *  how to interpret prefix content (automatic ? force raw mode (default) ? full mode only ?) */
-ZSTDLIB_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t prefixSize, ZSTD_dictContentType_e dictContentType);
 
 /*! ZSTD_DCtx_setMaxWindowSize() :
  *  Refuses allocating internal buffers for frames requiring a window size larger than provided limit.
@@ -2035,14 +2339,14 @@
  *  By default, a decompression context accepts all window sizes <= (1 << ZSTD_WINDOWLOG_LIMIT_DEFAULT)
  * @return : 0, or an error code (which can be tested using ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
 
 /*! ZSTD_DCtx_getParameter() :
  *  Get the requested decompression parameter value, selected by enum ZSTD_dParameter,
  *  and store it into int* value.
  * @return : 0, or an error code (which can be tested with ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value);
+ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value);
 
 /* ZSTD_d_format
  * experimental parameter,
@@ -2062,7 +2366,7 @@
  * in the range [dst, dst + pos) MUST not be modified during decompression
  * or you will get data corruption.
  *
- * When this flags is enabled zstd won't allocate an output buffer, because
+ * When this flag is enabled zstd won't allocate an output buffer, because
  * it can write directly to the ZSTD_outBuffer, but it will still allocate
  * an input buffer large enough to fit any compressed block. This will also
  * avoid the memcpy() from the internal output buffer to the ZSTD_outBuffer.
@@ -2115,6 +2419,17 @@
  */
 #define ZSTD_d_refMultipleDDicts ZSTD_d_experimentalParam4
 
+/* ZSTD_d_disableHuffmanAssembly
+ * Set to 1 to disable the Huffman assembly implementation.
+ * The default value is 0, which allows zstd to use the Huffman assembly
+ * implementation if available.
+ *
+ * This parameter can be used to disable Huffman assembly at runtime.
+ * If you want to disable it at compile time you can define the macro
+ * ZSTD_DISABLE_ASM.
+ */
+#define ZSTD_d_disableHuffmanAssembly ZSTD_d_experimentalParam5
+
 
 /*! ZSTD_DCtx_setFormat() :
  *  This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter().
@@ -2123,6 +2438,7 @@
  *  such ZSTD_f_zstd1_magicless for example.
  * @return : 0, or an error code (which can be tested using ZSTD_isError()). */
 ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
+ZSTDLIB_STATIC_API
 size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
 
 /*! ZSTD_decompressStream_simpleArgs() :
@@ -2131,7 +2447,7 @@
  *  This can be helpful for binders from dynamic languages
  *  which have troubles handling structures containing memory pointers.
  */
-ZSTDLIB_API size_t ZSTD_decompressStream_simpleArgs (
+ZSTDLIB_STATIC_API size_t ZSTD_decompressStream_simpleArgs (
                             ZSTD_DCtx* dctx,
                             void* dst, size_t dstCapacity, size_t* dstPos,
                       const void* src, size_t srcSize, size_t* srcPos);
@@ -2159,6 +2475,7 @@
  * This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
                          int compressionLevel,
                          unsigned long long pledgedSrcSize);
@@ -2176,17 +2493,15 @@
  * This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
                      const void* dict, size_t dictSize,
                            int compressionLevel);
 
 /*! ZSTD_initCStream_advanced() :
- * This function is DEPRECATED, and is approximately equivalent to:
+ * This function is DEPRECATED, and is equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
- *     // Pseudocode: Set each zstd parameter and leave the rest as-is.
- *     for ((param, value) : params) {
- *         ZSTD_CCtx_setParameter(zcs, param, value);
- *     }
+ *     ZSTD_CCtx_setParams(zcs, params);
  *     ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
  *     ZSTD_CCtx_loadDictionary(zcs, dict, dictSize);
  *
@@ -2196,6 +2511,7 @@
  * This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
                     const void* dict, size_t dictSize,
                           ZSTD_parameters params,
@@ -2205,20 +2521,18 @@
  * This function is DEPRECATED, and equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_refCDict(zcs, cdict);
- * 
+ *
  * note : cdict will just be referenced, and must outlive compression session
  * This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
 
 /*! ZSTD_initCStream_usingCDict_advanced() :
- *   This function is DEPRECATED, and is approximately equivalent to:
+ *   This function is DEPRECATED, and is equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
- *     // Pseudocode: Set each zstd frame parameter and leave the rest as-is.
- *     for ((fParam, value) : fParams) {
- *         ZSTD_CCtx_setParameter(zcs, fParam, value);
- *     }
+ *     ZSTD_CCtx_setFParams(zcs, fParams);
  *     ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
  *     ZSTD_CCtx_refCDict(zcs, cdict);
  *
@@ -2228,6 +2542,7 @@
  * This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
                                const ZSTD_CDict* cdict,
                                      ZSTD_frameParameters fParams,
@@ -2252,6 +2567,7 @@
  *  This prototype will generate compilation warnings.
  */
 ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
 size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
 
 
@@ -2270,7 +2586,7 @@
  * Note : (ingested - consumed) is amount of input data buffered internally, not yet compressed.
  * Aggregates progression inside active worker threads.
  */
-ZSTDLIB_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
+ZSTDLIB_STATIC_API ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx);
 
 /*! ZSTD_toFlushNow() :
  *  Tell how many bytes are ready to be flushed immediately.
@@ -2285,7 +2601,7 @@
  *    therefore flush speed is limited by production speed of oldest job
  *    irrespective of the speed of concurrent (and newer) jobs.
  */
-ZSTDLIB_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx);
+ZSTDLIB_STATIC_API size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx);
 
 
 /*=====   Advanced Streaming decompression functions  =====*/
@@ -2297,9 +2613,9 @@
  *     ZSTD_DCtx_loadDictionary(zds, dict, dictSize);
  *
  * note: no dictionary will be used if dict == NULL or dictSize < 8
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
  */
-ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_loadDictionary, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
 
 /*!
  * This function is deprecated, and is equivalent to:
@@ -2308,9 +2624,9 @@
  *     ZSTD_DCtx_refDDict(zds, ddict);
  *
  * note : ddict is referenced, it must outlive decompression session
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
  */
-ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset + ZSTD_DCtx_refDDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);
 
 /*!
  * This function is deprecated, and is equivalent to:
@@ -2318,17 +2634,185 @@
  *     ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
  *
  * re-use decompression parameters from previous init; saves dictionary loading
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
  */
-ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
+ZSTD_DEPRECATED("use ZSTD_DCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
+
+
+/* ********************* BLOCK-LEVEL SEQUENCE PRODUCER API *********************
+ *
+ * *** OVERVIEW ***
+ * The Block-Level Sequence Producer API allows users to provide their own custom
+ * sequence producer which libzstd invokes to process each block. The produced list
+ * of sequences (literals and matches) is then post-processed by libzstd to produce
+ * valid compressed blocks.
+ *
+ * This block-level offload API is a more granular complement of the existing
+ * frame-level offload API compressSequences() (introduced in v1.5.1). It offers
+ * an easier migration story for applications already integrated with libzstd: the
+ * user application continues to invoke the same compression functions
+ * ZSTD_compress2() or ZSTD_compressStream2() as usual, and transparently benefits
+ * from the specific advantages of the external sequence producer. For example,
+ * the sequence producer could be tuned to take advantage of known characteristics
+ * of the input, to offer better speed / ratio, or could leverage hardware
+ * acceleration not available within libzstd itself.
+ *
+ * See contrib/externalSequenceProducer for an example program employing the
+ * Block-Level Sequence Producer API.
+ *
+ * *** USAGE ***
+ * The user is responsible for implementing a function of type
+ * ZSTD_sequenceProducer_F. For each block, zstd will pass the following
+ * arguments to the user-provided function:
+ *
+ *   - sequenceProducerState: a pointer to a user-managed state for the sequence
+ *     producer.
+ *
+ *   - outSeqs, outSeqsCapacity: an output buffer for the sequence producer.
+ *     outSeqsCapacity is guaranteed >= ZSTD_sequenceBound(srcSize). The memory
+ *     backing outSeqs is managed by the CCtx.
+ *
+ *   - src, srcSize: an input buffer for the sequence producer to parse.
+ *     srcSize is guaranteed to be <= ZSTD_BLOCKSIZE_MAX.
+ *
+ *   - dict, dictSize: a history buffer, which may be empty, which the sequence
+ *     producer may reference as it parses the src buffer. Currently, zstd will
+ *     always pass dictSize == 0 into external sequence producers, but this will
+ *     change in the future.
+ *
+ *   - compressionLevel: a signed integer representing the zstd compression level
+ *     set by the user for the current operation. The sequence producer may choose
+ *     to use this information to change its compression strategy and speed/ratio
+ *     tradeoff. Note: the compression level does not reflect zstd parameters set
+ *     through the advanced API.
+ *
+ *   - windowSize: a size_t representing the maximum allowed offset for external
+ *     sequences. Note that sequence offsets are sometimes allowed to exceed the
+ *     windowSize if a dictionary is present, see doc/zstd_compression_format.md
+ *     for details.
+ *
+ * The user-provided function shall return a size_t representing the number of
+ * sequences written to outSeqs. This return value will be treated as an error
+ * code if it is greater than outSeqsCapacity. The return value must be non-zero
+ * if srcSize is non-zero. The ZSTD_SEQUENCE_PRODUCER_ERROR macro is provided
+ * for convenience, but any value greater than outSeqsCapacity will be treated as
+ * an error code.
+ *
+ * If the user-provided function does not return an error code, the sequences
+ * written to outSeqs must be a valid parse of the src buffer. Data corruption may
+ * occur if the parse is not valid. A parse is defined to be valid if the
+ * following conditions hold:
+ *   - The sum of matchLengths and literalLengths must equal srcSize.
+ *   - All sequences in the parse, except for the final sequence, must have
+ *     matchLength >= ZSTD_MINMATCH_MIN. The final sequence must have
+ *     matchLength >= ZSTD_MINMATCH_MIN or matchLength == 0.
+ *   - All offsets must respect the windowSize parameter as specified in
+ *     doc/zstd_compression_format.md.
+ *   - If the final sequence has matchLength == 0, it must also have offset == 0.
+ *
+ * zstd will only validate these conditions (and fail compression if they do not
+ * hold) if the ZSTD_c_validateSequences cParam is enabled. Note that sequence
+ * validation has a performance cost.
+ *
+ * If the user-provided function returns an error, zstd will either fall back
+ * to an internal sequence producer or fail the compression operation. The user can
+ * choose between the two behaviors by setting the ZSTD_c_enableSeqProducerFallback
+ * cParam. Fallback compression will follow any other cParam settings, such as
+ * compression level, the same as in a normal compression operation.
+ *
+ * The user shall instruct zstd to use a particular ZSTD_sequenceProducer_F
+ * function by calling
+ *         ZSTD_registerSequenceProducer(cctx,
+ *                                       sequenceProducerState,
+ *                                       sequenceProducer)
+ * This setting will persist until the next parameter reset of the CCtx.
+ *
+ * The sequenceProducerState must be initialized by the user before calling
+ * ZSTD_registerSequenceProducer(). The user is responsible for destroying the
+ * sequenceProducerState.
+ *
+ * *** LIMITATIONS ***
+ * This API is compatible with all zstd compression APIs which respect advanced parameters.
+ * However, there are three limitations:
+ *
+ * First, the ZSTD_c_enableLongDistanceMatching cParam is not currently supported.
+ * COMPRESSION WILL FAIL if it is enabled and the user tries to compress with a block-level
+ * external sequence producer.
+ *   - Note that ZSTD_c_enableLongDistanceMatching is auto-enabled by default in some
+ *     cases (see its documentation for details). Users must explicitly set
+ *     ZSTD_c_enableLongDistanceMatching to ZSTD_ps_disable in such cases if an external
+ *     sequence producer is registered.
+ *   - As of this writing, ZSTD_c_enableLongDistanceMatching is disabled by default
+ *     whenever ZSTD_c_windowLog < 128MB, but that's subject to change. Users should
+ *     check the docs on ZSTD_c_enableLongDistanceMatching whenever the Block-Level Sequence
+ *     Producer API is used in conjunction with advanced settings (like ZSTD_c_windowLog).
+ *
+ * Second, history buffers are not currently supported. Concretely, zstd will always pass
+ * dictSize == 0 to the external sequence producer (for now). This has two implications:
+ *   - Dictionaries are not currently supported. Compression will *not* fail if the user
+ *     references a dictionary, but the dictionary won't have any effect.
+ *   - Stream history is not currently supported. All advanced compression APIs, including
+ *     streaming APIs, work with external sequence producers, but each block is treated as
+ *     an independent chunk without history from previous blocks.
+ *
+ * Third, multi-threading within a single compression is not currently supported. In other words,
+ * COMPRESSION WILL FAIL if ZSTD_c_nbWorkers > 0 and an external sequence producer is registered.
+ * Multi-threading across compressions is fine: simply create one CCtx per thread.
+ *
+ * Long-term, we plan to overcome all three limitations. There is no technical blocker to
+ * overcoming them. It is purely a question of engineering effort.
+ */
+
+#define ZSTD_SEQUENCE_PRODUCER_ERROR ((size_t)(-1))
+
+typedef size_t ZSTD_sequenceProducer_F (
+  void* sequenceProducerState,
+  ZSTD_Sequence* outSeqs, size_t outSeqsCapacity,
+  const void* src, size_t srcSize,
+  const void* dict, size_t dictSize,
+  int compressionLevel,
+  size_t windowSize
+);
+
+/*! ZSTD_registerSequenceProducer() :
+ * Instruct zstd to use a block-level external sequence producer function.
+ *
+ * The sequenceProducerState must be initialized by the caller, and the caller is
+ * responsible for managing its lifetime. This parameter is sticky across
+ * compressions. It will remain set until the user explicitly resets compression
+ * parameters.
+ *
+ * Sequence producer registration is considered to be an "advanced parameter",
+ * part of the "advanced API". This means it will only have an effect on compression
+ * APIs which respect advanced parameters, such as compress2() and compressStream2().
+ * Older compression APIs such as compressCCtx(), which predate the introduction of
+ * "advanced parameters", will ignore any external sequence producer setting.
+ *
+ * The sequence producer can be "cleared" by registering a NULL function pointer. This
+ * removes all limitations described above in the "LIMITATIONS" section of the API docs.
+ *
+ * The user is strongly encouraged to read the full API documentation (above) before
+ * calling this function. */
+ZSTDLIB_STATIC_API void
+ZSTD_registerSequenceProducer(
+  ZSTD_CCtx* cctx,
+  void* sequenceProducerState,
+  ZSTD_sequenceProducer_F* sequenceProducer
+);
 
 
 /*********************************************************************
-*  Buffer-less and synchronous inner streaming functions
+*  Buffer-less and synchronous inner streaming functions (DEPRECATED)
 *
-*  This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
-*  But it's also a complex one, with several restrictions, documented below.
-*  Prefer normal streaming API for an easier experience.
+*  This API is deprecated, and will be removed in a future version.
+*  It allows streaming (de)compression with user allocated buffers.
+*  However, it is hard to use, and not as well tested as the rest of
+*  our API.
+*
+*  Please use the normal streaming API instead: ZSTD_compressStream2,
+*  and ZSTD_decompressStream.
+*  If there is functionality that you need, but it doesn't provide,
+*  please open an issue on our GitHub.
 ********************************************************************* */
 
 /**
@@ -2340,7 +2824,6 @@
 
   Start by initializing a context.
   Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
-  It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
 
   Then, consume your input using ZSTD_compressContinue().
   There are some important considerations to keep in mind when using this advanced function :
@@ -2362,18 +2845,28 @@
 */
 
 /*=====   Buffer-less streaming compression functions  =====*/
-ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
-ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
 
-ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("This function will likely be removed in a future release. It is misleading and has very limited utility.")
+ZSTDLIB_STATIC_API
+size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
+
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The buffer-less API is deprecated in favor of the normal streaming API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 
 /* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */
 ZSTD_DEPRECATED("use advanced API to access custom parameters")
+ZSTDLIB_STATIC_API
 size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
 ZSTD_DEPRECATED("use advanced API to access custom parameters")
+ZSTDLIB_STATIC_API
 size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
 /**
   Buffer-less streaming decompression (synchronous mode)
@@ -2386,8 +2879,8 @@
   Frame header is extracted from the beginning of compressed frame, so providing only the frame's beginning is enough.
   Data fragment must be large enough to ensure successful decoding.
  `ZSTD_frameHeaderSize_max` bytes is guaranteed to always be large enough.
-  @result : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled.
-           >0 : `srcSize` is too small, please provide at least @result bytes on next attempt.
+  result  : 0 : successful decoding, the `ZSTD_frameHeader` structure is correctly filled.
+           >0 : `srcSize` is too small, please provide at least result bytes on next attempt.
            errorCode, which can be tested using ZSTD_isError().
 
   It fills a ZSTD_frameHeader structure with important information to correctly decode the frame,
@@ -2406,7 +2899,7 @@
 
   The most memory efficient way is to use a round buffer of sufficient size.
   Sufficient size is determined by invoking ZSTD_decodingBufferSize_min(),
-  which can @return an error code if required value is too large for current system (in 32-bits mode).
+  which can return an error code if required value is too large for current system (in 32-bits mode).
   In a round buffer methodology, ZSTD_decompressContinue() decompresses each block next to previous one,
   up to the moment there is not enough room left in the buffer to guarantee decoding another full block,
   which maximum size is provided in `ZSTD_frameHeader` structure, field `blockSizeMax`.
@@ -2426,7 +2919,7 @@
   ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' to ZSTD_decompressContinue().
   ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will fail.
 
- @result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity).
+  result of ZSTD_decompressContinue() is the number of bytes regenerated within 'dst' (necessarily <= dstCapacity).
   It can be zero : it just means ZSTD_decompressContinue() has decoded some metadata item.
   It can also be an error code, which can be tested with ZSTD_isError().
 
@@ -2449,49 +2942,42 @@
 */
 
 /*=====   Buffer-less streaming decompression functions  =====*/
-typedef enum { ZSTD_frame, ZSTD_skippableFrame } ZSTD_frameType_e;
-typedef struct {
-    unsigned long long frameContentSize; /* if == ZSTD_CONTENTSIZE_UNKNOWN, it means this field is not available. 0 means "empty" */
-    unsigned long long windowSize;       /* can be very large, up to <= frameContentSize */
-    unsigned blockSizeMax;
-    ZSTD_frameType_e frameType;          /* if == ZSTD_skippableFrame, frameContentSize is the size of skippable content */
-    unsigned headerSize;
-    unsigned dictID;
-    unsigned checksumFlag;
-} ZSTD_frameHeader;
 
-/*! ZSTD_getFrameHeader() :
- *  decode Frame Header, or requires larger `srcSize`.
- * @return : 0, `zfhPtr` is correctly filled,
- *          >0, `srcSize` is too small, value is wanted `srcSize` amount,
- *           or an error code, which can be tested using ZSTD_isError() */
-ZSTDLIB_API size_t ZSTD_getFrameHeader(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize);   /**< doesn't consume input */
-/*! ZSTD_getFrameHeader_advanced() :
- *  same as ZSTD_getFrameHeader(),
- *  with added capability to select a format (like ZSTD_f_zstd1_magicless) */
-ZSTDLIB_API size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format);
-ZSTDLIB_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize);  /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTDLIB_STATIC_API size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long long frameContentSize);  /**< when frame content size is not known, pass in frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN */
 
-ZSTDLIB_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
-ZSTDLIB_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
-ZSTDLIB_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBegin_usingDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
 
-ZSTDLIB_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
-ZSTDLIB_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 
 /* misc */
-ZSTDLIB_API void   ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
+ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.")
+ZSTDLIB_STATIC_API void   ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
 typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
-ZSTDLIB_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
+ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
 
 
 
 
-/* ============================ */
-/**       Block level API       */
-/* ============================ */
+/* ========================================= */
+/**       Block level API (DEPRECATED)       */
+/* ========================================= */
 
 /*!
+
+    This API is deprecated in favor of the regular compression API.
+    You can get the frame header down to 2 bytes by setting:
+      - ZSTD_c_format = ZSTD_f_zstd1_magicless
+      - ZSTD_c_contentSizeFlag = 0
+      - ZSTD_c_checksumFlag = 0
+      - ZSTD_c_dictIDFlag = 0
+
+    This API is not as well tested as our normal API, so we recommend not using it.
+    We will be removing it in a future version. If the normal API doesn't provide
+    the functionality you need, please open a GitHub issue.
+
     Block functions produce and decode raw zstd blocks, without frame metadata.
     Frame metadata cost is typically ~12 bytes, which can be non-negligible for very small blocks (< 100 bytes).
     But users will have to take in charge needed metadata to regenerate data, such as compressed and content sizes.
@@ -2502,7 +2988,6 @@
     - It is necessary to init context before starting
       + compression : any ZSTD_compressBegin*() variant, including with dictionary
       + decompression : any ZSTD_decompressBegin*() variant, including with dictionary
-      + copyCCtx() and copyDCtx() can be used too
     - Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB
       + If input is larger than a block size, it's necessary to split input data into multiple blocks
       + For inputs larger than a single block, consider using regular ZSTD_compress() instead.
@@ -2519,11 +3004,14 @@
 */
 
 /*=====   Raw zstd block functions  =====*/
-ZSTDLIB_API size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
-ZSTDLIB_API size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
-ZSTDLIB_API size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
-
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_getBlockSize   (const ZSTD_CCtx* cctx);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_compressBlock  (ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_decompressBlock(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
+ZSTD_DEPRECATED("The block API is deprecated in favor of the normal compression API. See docs.")
+ZSTDLIB_STATIC_API size_t ZSTD_insertBlock    (ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize);  /**< insert uncompressed block into `dctx` history. Useful for multi-blocks decompression. */
 
 #endif   /* ZSTD_H_ZSTD_STATIC_LINKING_ONLY */
 
diff --git a/Utilities/cmzstd/lib/zstd_errors.h b/Utilities/cmzstd/lib/zstd_errors.h
index fa3686b..dc75eee 100644
--- a/Utilities/cmzstd/lib/zstd_errors.h
+++ b/Utilities/cmzstd/lib/zstd_errors.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -20,19 +20,31 @@
 
 
 /* =====   ZSTDERRORLIB_API : control library symbols visibility   ===== */
-#ifndef ZSTDERRORLIB_VISIBILITY
-#  if defined(__GNUC__) && (__GNUC__ >= 4)
-#    define ZSTDERRORLIB_VISIBILITY __attribute__ ((visibility ("default")))
+#ifndef ZSTDERRORLIB_VISIBLE
+   /* Backwards compatibility with old macro name */
+#  ifdef ZSTDERRORLIB_VISIBILITY
+#    define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY
+#  elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default")))
 #  else
-#    define ZSTDERRORLIB_VISIBILITY
+#    define ZSTDERRORLIB_VISIBLE
 #  endif
 #endif
+
+#ifndef ZSTDERRORLIB_HIDDEN
+#  if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
+#    define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden")))
+#  else
+#    define ZSTDERRORLIB_HIDDEN
+#  endif
+#endif
+
 #if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
-#  define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBILITY
+#  define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE
 #elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
-#  define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#  define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
 #else
-#  define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBILITY
+#  define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE
 #endif
 
 /*-*********************************************
@@ -58,14 +70,17 @@
   ZSTD_error_frameParameter_windowTooLarge = 16,
   ZSTD_error_corruption_detected = 20,
   ZSTD_error_checksum_wrong      = 22,
+  ZSTD_error_literals_headerWrong = 24,
   ZSTD_error_dictionary_corrupted      = 30,
   ZSTD_error_dictionary_wrong          = 32,
   ZSTD_error_dictionaryCreation_failed = 34,
   ZSTD_error_parameter_unsupported   = 40,
+  ZSTD_error_parameter_combination_unsupported = 41,
   ZSTD_error_parameter_outOfBound    = 42,
   ZSTD_error_tableLog_tooLarge       = 44,
   ZSTD_error_maxSymbolValue_tooLarge = 46,
   ZSTD_error_maxSymbolValue_tooSmall = 48,
+  ZSTD_error_stabilityCondition_notRespected = 50,
   ZSTD_error_stage_wrong       = 60,
   ZSTD_error_init_missing      = 62,
   ZSTD_error_memory_allocation = 64,
@@ -73,11 +88,15 @@
   ZSTD_error_dstSize_tooSmall = 70,
   ZSTD_error_srcSize_wrong    = 72,
   ZSTD_error_dstBuffer_null   = 74,
+  ZSTD_error_noForwardProgress_destFull = 80,
+  ZSTD_error_noForwardProgress_inputEmpty = 82,
   /* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
   ZSTD_error_frameIndex_tooLarge = 100,
   ZSTD_error_seekableIO          = 102,
   ZSTD_error_dstBuffer_wrong     = 104,
   ZSTD_error_srcBuffer_wrong     = 105,
+  ZSTD_error_sequenceProducer_failed = 106,
+  ZSTD_error_externalSequences_invalid = 107,
   ZSTD_error_maxCode = 120  /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
 } ZSTD_ErrorCode;
 
diff --git a/bootstrap b/bootstrap
index 115c354..01fcfae 100755
--- a/bootstrap
+++ b/bootstrap
@@ -364,6 +364,7 @@
   cmFindFileCommand \
   cmFindLibraryCommand \
   cmFindPackageCommand \
+  cmFindPackageStack \
   cmFindPathCommand \
   cmFindProgramCommand \
   cmForEachCommand \
@@ -593,6 +594,7 @@
   librhash/sha256.c \
   librhash/sha3.c \
   librhash/sha512.c \
+  librhash/util.c \
   "
 
 JSONCPP_CXX_SOURCES="\
@@ -1791,6 +1793,7 @@
   libs="${libs} -luv"
 fi
 
+librhash_c_flags="-DNO_IMPORT_EXPORT"
 if test "x${bootstrap_system_librhash}" != "x"; then
   if test `which pkg-config`; then
     use_librhash_flags="`pkg-config --cflags librhash`"
@@ -1945,7 +1948,7 @@
 if test "x${bootstrap_system_librhash}" = "x"; then
   for a in ${LIBRHASH_C_SOURCES}; do
     src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmlibrhash/${a}"`
-    write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" ""
+    write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" "${librhash_c_flags}"
   done
 fi
 if test "x${bootstrap_system_jsoncpp}" = "x"; then