Merge topic 'openwatcom-linker-id'

2e549c1df4 CMakeDetermineLinkerId: support the OpenWatcom linker ID

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12012
diff --git a/.clang-tidy b/.clang-tidy
index ccd74be..ae0dc23 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -3,8 +3,10 @@
 bugprone-*,\
 -bugprone-assignment-in-if-condition,\
 -bugprone-crtp-constructor-accessibility,\
+-bugprone-derived-method-shadowing-base-method,\
 -bugprone-easily-swappable-parameters,\
 -bugprone-empty-catch,\
+-bugprone-exception-escape,\
 -bugprone-implicit-widening-of-multiplication-result,\
 -bugprone-inc-dec-in-conditions,\
 -bugprone-macro-parentheses,\
@@ -13,8 +15,10 @@
 -bugprone-return-const-ref-from-parameter,\
 -bugprone-suspicious-stringview-data-usage,\
 -bugprone-switch-missing-default-case,\
+-bugprone-throwing-static-initialization,\
 -bugprone-too-small-loop-variable,\
 -bugprone-unchecked-optional-access,\
+-bugprone-unchecked-string-to-number-conversion,\
 -bugprone-unused-local-non-trivial-variable,\
 -bugprone-unused-return-value,\
 -bugprone-use-after-move,\
@@ -22,13 +26,17 @@
 -misc-confusable-identifiers,\
 -misc-const-correctness,\
 -misc-include-cleaner,\
+-misc-multiple-inheritance,\
 -misc-non-private-member-variables-in-classes,\
 -misc-no-recursion,\
+-misc-override-with-different-visibility,\
+-misc-predictable-rand,\
 -misc-static-assert,\
 -misc-use-anonymous-namespace,\
 -misc-use-internal-linkage,\
 modernize-*,\
 -modernize-avoid-c-arrays,\
+-modernize-avoid-c-style-cast,\
 -modernize-concat-nested-namespaces,\
 -modernize-macro-to-enum,\
 -modernize-return-braced-init-list,\
@@ -60,6 +68,9 @@
 -readability-make-member-function-const,\
 -readability-math-missing-parentheses,\
 -readability-named-parameter,\
+-readability-redundant-casting,\
+-readability-redundant-parentheses,\
+-readability-redundant-typename,\
 -readability-simplify-boolean-expr,\
 -readability-static-accessed-through-instance,\
 -readability-suspicious-call-argument,\
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fd56c98..8c78b99 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -65,7 +65,7 @@
 
 p:doc-package:
     extends:
-        - .fedora43_sphinx_package
+        - .fedora44_sphinx_package
         - .cmake_prep_doc_linux
         - .linux_x86_64_tags
         - .cmake_doc_artifacts
@@ -129,18 +129,30 @@
         - .cmake_cdash_artifacts
         - .rules
 
-l:tidy-fedora43:
+l:tidy-fedora44:
     extends:
-        - .fedora43_tidy
+        - .fedora44_tidy
         - .cmake_build_linux
         - .cmake_tidy_artifacts
         - .linux_x86_64_tags
         - .cmake_cdash_artifacts
         - .rules
 
-l:sphinx-fedora43:
+l:pvs-studio-fedora44:
     extends:
-        - .fedora43_sphinx
+        - .fedora44_pvs_studio
+        - .cmake_build_linux
+        - .linux_x86_64_tags
+        - .cmake_cdash_artifacts
+        - .rules
+    variables:
+        CMAKE_CI_JOB_CONTINUOUS: "true"
+        CMAKE_CI_JOB_NIGHTLY: "true"
+        CMAKE_CI_JOB_NO_MR: "true"
+
+l:sphinx-fedora44:
+    extends:
+        - .fedora44_sphinx
         - .cmake_build_linux
         - .cmake_sphinx_artifacts
         - .linux_x86_64_tags
@@ -149,34 +161,34 @@
         CMAKE_CI_JOB_CONTINUOUS: "true"
         CMAKE_CI_JOB_HELP: "true"
 
-l:clang-analyzer-fedora43:
+l:clang-analyzer-fedora44:
     extends:
-        - .fedora43_clang_analyzer
+        - .fedora44_clang_analyzer
         - .cmake_build_linux
         - .linux_x86_64_tags
         - .rules
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-l:clang-fuzzing-fedora43:
+l:clang-fuzzing-fedora44:
     extends:
-        - .fedora43_clang_fuzzing
+        - .fedora44_clang_fuzzing
         - .cmake_build_linux
         - .linux_x86_64_tags
         - .rules
 
-l:clazy-fedora43:
+l:clazy-fedora44:
     extends:
-        - .fedora43_clazy
+        - .fedora44_clazy
         - .cmake_build_linux
         - .linux_x86_64_tags
         - .rules
 
 # Coverage builds
 
-b:fedora43-gcc-gcov:
+b:fedora44-gcc-gcov:
     extends:
-        - .fedora43_gcc_gcov
+        - .fedora44_gcc_gcov
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_x86_64_tags
@@ -185,9 +197,9 @@
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-t:fedora43-gcc-gcov:
+t:fedora44-gcc-gcov:
     extends:
-        - .fedora43_gcc_gcov
+        - .fedora44_gcc_gcov
         - .cmake_test_linux
         - .cmake_coverage_artifacts
         - .linux_x86_64_tags_x11
@@ -195,25 +207,25 @@
     # Run coverage analysis even if the tests fail.
     allow_failure: true
     needs:
-        - b:fedora43-gcc-gcov
+        - b:fedora44-gcc-gcov
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-a:fedora43-gcc-gcov:
+a:fedora44-gcc-gcov:
     extends:
-        - .fedora43_gcc_gcov
+        - .fedora44_gcc_gcov
         - .cmake_coverage_linux
         - .cmake_cdash_artifacts
         - .linux_x86_64_tags
         - .rules
     needs:
-        - t:fedora43-gcc-gcov
+        - t:fedora44-gcc-gcov
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:fedora43-bullseye-coverage:
+b:fedora44-bullseye-coverage:
     extends:
-        - .fedora43_bullseye_coverage
+        - .fedora44_bullseye_coverage
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_x86_64_tags
@@ -223,9 +235,9 @@
         CMAKE_CI_JOB_NO_MR: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-t:fedora43-bullseye-coverage:
+t:fedora44-bullseye-coverage:
     extends:
-        - .fedora43_bullseye_coverage
+        - .fedora44_bullseye_coverage
         - .cmake_test_linux
         - .cmake_coverage_artifacts
         - .linux_x86_64_tags_x11
@@ -233,20 +245,20 @@
     # Run coverage analysis even if the tests fail.
     allow_failure: true
     needs:
-        - b:fedora43-bullseye-coverage
+        - b:fedora44-bullseye-coverage
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_NO_MR: "true"
 
-a:fedora43-bullseye-coverage:
+a:fedora44-bullseye-coverage:
     extends:
-        - .fedora43_bullseye_coverage
+        - .fedora44_bullseye_coverage
         - .cmake_coverage_linux
         - .cmake_cdash_artifacts
         - .linux_x86_64_tags
         - .rules
     needs:
-        - t:fedora43-bullseye-coverage
+        - t:fedora44-bullseye-coverage
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_NO_MR: "true"
@@ -340,9 +352,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-hip-radeon:
+t:fedora44-hip-radeon:
     extends:
-        - .fedora43_hip_radeon
+        - .fedora44_hip_radeon
         - .cmake_test_linux_release
         - .linux_x86_64_tags_rocm6.2
         - .rules
@@ -350,9 +362,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-makefiles-lfortran:
+t:fedora44-makefiles-lfortran:
     extends:
-        - .fedora43_makefiles_lfortran
+        - .fedora44_makefiles_lfortran
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -360,9 +372,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-ninja-lfortran:
+t:fedora44-ninja-lfortran:
     extends:
-        - .fedora43_ninja_lfortran
+        - .fedora44_ninja_lfortran
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -370,17 +382,17 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-ninja-multi:
+t:fedora44-ninja-multi:
     extends:
-        - .fedora43_ninja_multi
+        - .fedora44_ninja_multi
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
         - .needs_centos7_x86_64
 
-t:fedora43-ninja-clang:
+t:fedora44-ninja-clang:
     extends:
-        - .fedora43_ninja_clang
+        - .fedora44_ninja_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -388,9 +400,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-ninja-gcc-std-reloc:
+t:fedora44-ninja-gcc-std-reloc:
     extends:
-        - .fedora43_ninja_gcc_std_reloc
+        - .fedora44_ninja_gcc_std_reloc
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -398,9 +410,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-ninja-multi-clang:
+t:fedora44-ninja-multi-clang:
     extends:
-        - .fedora43_ninja_multi_clang
+        - .fedora44_ninja_multi_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -408,9 +420,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-makefiles-clang:
+t:fedora44-makefiles-clang:
     extends:
-        - .fedora43_makefiles_clang
+        - .fedora44_makefiles_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -418,17 +430,17 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-makefiles:
+t:fedora44-makefiles:
     extends:
-        - .fedora43_makefiles
+        - .fedora44_makefiles
         - .cmake_test_linux_release
         - .linux_x86_64_v3_tags
         - .rules
         - .needs_centos7_x86_64
 
-t:fedora43-makefiles-nospace:
+t:fedora44-makefiles-nospace:
     extends:
-        - .fedora43_makefiles
+        - .fedora44_makefiles
         - .cmake_test_linux_release
         - .linux_x86_64_v3_tags
         - .cmake_junit_artifacts
@@ -436,12 +448,12 @@
         - .needs_centos7_x86_64
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
-        CMAKE_CI_BUILD_NAME: fedora43_makefiles_nospace
+        CMAKE_CI_BUILD_NAME: fedora44_makefiles_nospace
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-fastbuild:
+t:fedora44-fastbuild:
     extends:
-        - .fedora43_fastbuild
+        - .fedora44_fastbuild
         - .cmake_test_linux_release
         - .linux_x86_64_v3_tags
         - .rules
@@ -449,9 +461,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-instrumentation:
+t:fedora44-instrumentation:
     extends:
-        - .fedora43_ninja_instrumentation
+        - .fedora44_ninja_instrumentation
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .rules
@@ -658,9 +670,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:fedora43-ninja:
+b:fedora44-ninja:
     extends:
-        - .fedora43_ninja
+        - .fedora44_ninja
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_x86_64_tags
@@ -669,9 +681,9 @@
         CMAKE_CI_JOB_CONTINUOUS: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-b:fedora43-makefiles-symlinked:
+b:fedora44-makefiles-symlinked:
     extends:
-        - .fedora43_makefiles_symlinked
+        - .fedora44_makefiles_symlinked
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_x86_64_tags
@@ -710,9 +722,9 @@
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-b:fedora43-extdeps:
+b:fedora44-extdeps:
     extends:
-        - .fedora43_extdeps
+        - .fedora44_extdeps
         - .cmake_build_linux_standalone
         - .linux_x86_64_tags
         - .rules
@@ -720,27 +732,27 @@
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-t:fedora43-ninja:
+t:fedora44-ninja:
     extends:
-        - .fedora43_ninja
+        - .fedora44_ninja
         - .cmake_test_linux
         - .linux_x86_64_tags_x11
         - .cmake_test_artifacts
         - .rules
     needs:
-        - b:fedora43-ninja
+        - b:fedora44-ninja
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
 
-t:fedora43-makefiles-symlinked:
+t:fedora44-makefiles-symlinked:
     extends:
-        - .fedora43_makefiles_symlinked
+        - .fedora44_makefiles_symlinked
         - .cmake_test_linux
         - .linux_x86_64_tags_x11
         - .cmake_test_artifacts
         - .rules
     needs:
-        - b:fedora43-makefiles-symlinked
+        - b:fedora44-makefiles-symlinked
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -933,9 +945,9 @@
 
 ## Sanitizer builds
 
-b:fedora43-asan:
+b:fedora44-asan:
     extends:
-        - .fedora43_asan
+        - .fedora44_asan
         - .cmake_build_linux
         - .cmake_build_artifacts
         - .linux_x86_64_tags
@@ -944,21 +956,21 @@
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_RUN_MANUALLY: "true"
 
-t:fedora43-asan:
+t:fedora44-asan:
     extends:
-        - .fedora43_asan
+        - .fedora44_asan
         - .cmake_memcheck_linux
         - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .rules
     needs:
-        - b:fedora43-asan
+        - b:fedora44-asan
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:fedora43-ninja-valgrind:
+t:fedora44-ninja-valgrind:
     extends:
-        - .fedora43_ninja_valgrind
+        - .fedora44_ninja_valgrind
         - .cmake_test_linux_release
         # Use a fast CPU so that timeouts are less of a concern.
         - .linux_x86_64_tags_fast_x11
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore
index 4d33c62..c0e73eb 100644
--- a/.gitlab/.gitignore
+++ b/.gitlab/.gitignore
@@ -20,6 +20,7 @@
 /open-watcom*
 /orangec
 /pellesc
+/pvs-studio
 /python*
 /qt*
 /sccache*
diff --git a/.gitlab/ci/bullseye.sh b/.gitlab/ci/bullseye.sh
index 8c4cd2b..132588c 100755
--- a/.gitlab/ci/bullseye.sh
+++ b/.gitlab/ci/bullseye.sh
@@ -7,12 +7,12 @@
     exit 1
 fi
 
-readonly version="9.22.3"
+readonly version="9.25.1"
 
 case "$(uname -s)-$(uname -m)" in
     Linux-x86_64)
         tarball="BullseyeCoverage-$version-Linux-x64.tar.xz"
-        sha256sum="d5be7e65d9363161b67fa77a30407c7c200d995af79a422c4e2e278802ba0776"
+        sha256sum="cc77ef1ffd846e9911a5a22c41a8e24c5e1f1c4c59c0c688b6756fcb14fa4af5"
         shatool="sha256sum"
         ;;
     *)
diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1
index 94f6694..ee0f4db 100644
--- a/.gitlab/ci/cmake.ps1
+++ b/.gitlab/ci/cmake.ps1
@@ -1,12 +1,12 @@
 $erroractionpreference = "stop"
 
-$version = "4.2.0"
+$version = "4.3.2"
 
 if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") {
-    $sha256sum = "CF35A516C4F5F4646B301E51C8E24B168CC012C3B1453B8F675303B54EB0EF45"
+    $sha256sum = "83D20C23F5C5F64B3B328785E35B23C532E33057A97ED6294ACACA3781B78A01"
     $platform = "windows-x86_64"
 } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") {
-    $sha256sum = "9EB3E88083DDA569A4086C48517F5D49BD7B505DADA79075EA7CB6E2AC6E0A1E"
+    $sha256sum = "42A2A88FC57E70D04A92F6EAAAC2094241995F53584BC2AFDAE7E6EF982B3781"
     $platform = "windows-arm64"
 } else {
     throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh
index a0e2a5a..bc41ddd 100755
--- a/.gitlab/ci/cmake.sh
+++ b/.gitlab/ci/cmake.sh
@@ -2,22 +2,22 @@
 
 set -e
 
-readonly version="4.2.0"
+readonly version="4.3.2"
 
 case "$(uname -s)-$(uname -m)" in
     Linux-x86_64)
         shatool="sha256sum"
-        sha256sum="bbcebd4c433eab3af03a8c80bb5d84e8dfc3ff8a4ab9d01547b21240c23f7c2c"
+        sha256sum="791ae3604841ca03cb3889a3ad89165346e4b180ae3448efd4b0caa9ef46d245"
         platform="linux-x86_64"
         ;;
     Linux-aarch64)
         shatool="sha256sum"
-        sha256sum="86f52e9769cd7745e5227931e689e122dc1afc5648cc9b04db601b165f3ac993"
+        sha256sum="377079ab739f5765176f427609d9a2015b756ea20d5cba908d279c3731a2f481"
         platform="linux-aarch64"
         ;;
     Darwin-*)
         shatool="shasum -a 256"
-        sha256sum="b8b040a06343b2b6bc090b03a9c2bb4e98037518846989fb7c40ebbf30655c5d"
+        sha256sum="808ab43a0db04c8eec9ed7db12b90d7be1c8e2e75f4a060724d604a2043ccaf7"
         platform="macos-universal"
         ;;
     *)
diff --git a/.gitlab/ci/configure_fedora43_clang_analyzer.cmake b/.gitlab/ci/configure_fedora43_clang_analyzer.cmake
deleted file mode 100644
index c18dfe6..0000000
--- a/.gitlab/ci/configure_fedora43_clang_analyzer.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-set(configure_no_sccache 1)
-set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake b/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake
deleted file mode 100644
index 918c2c3..0000000
--- a/.gitlab/ci/configure_fedora43_makefiles_lfortran.cmake
+++ /dev/null
@@ -1 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake b/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake
deleted file mode 100644
index 918c2c3..0000000
--- a/.gitlab/ci/configure_fedora43_ninja_lfortran.cmake
+++ /dev/null
@@ -1 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake b/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake
deleted file mode 100644
index d156e20..0000000
--- a/.gitlab/ci/configure_fedora43_ninja_valgrind.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-set(CMake_TEST_Qt5 ON CACHE BOOL "")
-set(CMake_TEST_Qt6 ON CACHE BOOL "")
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_valgrind.cmake")
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_ninja.cmake")
-include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora43_asan.cmake b/.gitlab/ci/configure_fedora44_asan.cmake
similarity index 72%
rename from .gitlab/ci/configure_fedora43_asan.cmake
rename to .gitlab/ci/configure_fedora44_asan.cmake
index 9913430..88f40c3 100644
--- a/.gitlab/ci/configure_fedora43_asan.cmake
+++ b/.gitlab/ci/configure_fedora44_asan.cmake
@@ -2,4 +2,4 @@
 set(CMAKE_CXX_FLAGS "-fsanitize=address" CACHE STRING "")
 set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_bullseye_coverage.cmake b/.gitlab/ci/configure_fedora44_bullseye_coverage.cmake
similarity index 94%
rename from .gitlab/ci/configure_fedora43_bullseye_coverage.cmake
rename to .gitlab/ci/configure_fedora44_bullseye_coverage.cmake
index e9cf156..d2e44a9 100644
--- a/.gitlab/ci/configure_fedora43_bullseye_coverage.cmake
+++ b/.gitlab/ci/configure_fedora44_bullseye_coverage.cmake
@@ -19,4 +19,4 @@
 set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION_URL_BAD "https://badtls-v1-1.kitware.com:8011" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora44_clang_analyzer.cmake b/.gitlab/ci/configure_fedora44_clang_analyzer.cmake
new file mode 100644
index 0000000..58046b9
--- /dev/null
+++ b/.gitlab/ci/configure_fedora44_clang_analyzer.cmake
@@ -0,0 +1,4 @@
+set(configure_no_sccache 1)
+set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_clang_fuzzing.cmake b/.gitlab/ci/configure_fedora44_clang_fuzzing.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_clang_fuzzing.cmake
rename to .gitlab/ci/configure_fedora44_clang_fuzzing.cmake
diff --git a/.gitlab/ci/configure_fedora43_clazy.cmake b/.gitlab/ci/configure_fedora44_clazy.cmake
similarity index 66%
rename from .gitlab/ci/configure_fedora43_clazy.cmake
rename to .gitlab/ci/configure_fedora44_clazy.cmake
index 1943447..7f2ec78 100644
--- a/.gitlab/ci/configure_fedora43_clazy.cmake
+++ b/.gitlab/ci/configure_fedora44_clazy.cmake
@@ -2,4 +2,4 @@
 set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
 set(CMAKE_USE_SYSTEM_JSONCPP ON CACHE BOOL "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_common.cmake b/.gitlab/ci/configure_fedora44_common.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_common.cmake
rename to .gitlab/ci/configure_fedora44_common.cmake
diff --git a/.gitlab/ci/configure_fedora43_common_clang.cmake b/.gitlab/ci/configure_fedora44_common_clang.cmake
similarity index 91%
rename from .gitlab/ci/configure_fedora43_common_clang.cmake
rename to .gitlab/ci/configure_fedora44_common_clang.cmake
index 941a040..c7b38b6 100644
--- a/.gitlab/ci/configure_fedora43_common_clang.cmake
+++ b/.gitlab/ci/configure_fedora44_common_clang.cmake
@@ -1,4 +1,4 @@
-set(CMAKE_Fortran_COMPILER "/usr/bin/flang-21" CACHE FILEPATH "")
+set(CMAKE_Fortran_COMPILER "/usr/bin/flang" CACHE FILEPATH "")
 set(CMAKE_Fortran_COMPILER_ID "LLVMFlang" CACHE STRING "")
 set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 "1" CACHE BOOL "")
 
diff --git a/.gitlab/ci/configure_fedora43_common_lfortran.cmake b/.gitlab/ci/configure_fedora44_common_lfortran.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_common_lfortran.cmake
rename to .gitlab/ci/configure_fedora44_common_lfortran.cmake
diff --git a/.gitlab/ci/configure_fedora43_extdeps.cmake b/.gitlab/ci/configure_fedora44_extdeps.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_extdeps.cmake
rename to .gitlab/ci/configure_fedora44_extdeps.cmake
diff --git a/.gitlab/ci/configure_fedora43_fastbuild.cmake b/.gitlab/ci/configure_fedora44_fastbuild.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_fastbuild.cmake
rename to .gitlab/ci/configure_fedora44_fastbuild.cmake
diff --git a/.gitlab/ci/configure_fedora43_gcc_gcov.cmake b/.gitlab/ci/configure_fedora44_gcc_gcov.cmake
similarity index 95%
rename from .gitlab/ci/configure_fedora43_gcc_gcov.cmake
rename to .gitlab/ci/configure_fedora44_gcc_gcov.cmake
index a40e0cc..e54437e 100644
--- a/.gitlab/ci/configure_fedora43_gcc_gcov.cmake
+++ b/.gitlab/ci/configure_fedora44_gcc_gcov.cmake
@@ -28,4 +28,4 @@
 # Do not bootstrap for the coverage test suite.
 set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_hip_radeon.cmake b/.gitlab/ci/configure_fedora44_hip_radeon.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_hip_radeon.cmake
rename to .gitlab/ci/configure_fedora44_hip_radeon.cmake
diff --git a/.gitlab/ci/configure_fedora43_makefiles.cmake b/.gitlab/ci/configure_fedora44_makefiles.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_makefiles.cmake
rename to .gitlab/ci/configure_fedora44_makefiles.cmake
diff --git a/.gitlab/ci/configure_fedora43_makefiles_clang.cmake b/.gitlab/ci/configure_fedora44_makefiles_clang.cmake
similarity index 68%
rename from .gitlab/ci/configure_fedora43_makefiles_clang.cmake
rename to .gitlab/ci/configure_fedora44_makefiles_clang.cmake
index e3f1ef9..02bf288 100644
--- a/.gitlab/ci/configure_fedora43_makefiles_clang.cmake
+++ b/.gitlab/ci/configure_fedora44_makefiles_clang.cmake
@@ -2,4 +2,4 @@
   set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "")
 endif()
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_clang.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake b/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake
new file mode 100644
index 0000000..2289116
--- /dev/null
+++ b/.gitlab/ci/configure_fedora44_makefiles_lfortran.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora44_makefiles_symlinked.cmake
similarity index 71%
rename from .gitlab/ci/configure_fedora43_makefiles_symlinked.cmake
rename to .gitlab/ci/configure_fedora44_makefiles_symlinked.cmake
index c5ef84c..e14b28f 100644
--- a/.gitlab/ci/configure_fedora43_makefiles_symlinked.cmake
+++ b/.gitlab/ci/configure_fedora44_makefiles_symlinked.cmake
@@ -2,4 +2,4 @@
 set(CMake_TEST_GUI "ON" CACHE BOOL "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake")
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja.cmake b/.gitlab/ci/configure_fedora44_ninja.cmake
similarity index 96%
rename from .gitlab/ci/configure_fedora43_ninja.cmake
rename to .gitlab/ci/configure_fedora44_ninja.cmake
index 94e6e81..d536871 100644
--- a/.gitlab/ci/configure_fedora43_ninja.cmake
+++ b/.gitlab/ci/configure_fedora44_ninja.cmake
@@ -42,4 +42,4 @@
 # Qt 6 requires C++17, so use Qt 5.
 set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_clang.cmake b/.gitlab/ci/configure_fedora44_ninja_clang.cmake
similarity index 75%
rename from .gitlab/ci/configure_fedora43_ninja_clang.cmake
rename to .gitlab/ci/configure_fedora44_ninja_clang.cmake
index 6cf0f85..5e007f4 100644
--- a/.gitlab/ci/configure_fedora43_ninja_clang.cmake
+++ b/.gitlab/ci/configure_fedora44_ninja_clang.cmake
@@ -1,3 +1,3 @@
 set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_clang.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_gcc_std_reloc.cmake b/.gitlab/ci/configure_fedora44_ninja_gcc_std_reloc.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_ninja_gcc_std_reloc.cmake
rename to .gitlab/ci/configure_fedora44_ninja_gcc_std_reloc.cmake
diff --git a/.gitlab/ci/configure_fedora43_ninja_instrumentation.cmake b/.gitlab/ci/configure_fedora44_ninja_instrumentation.cmake
similarity index 60%
rename from .gitlab/ci/configure_fedora43_ninja_instrumentation.cmake
rename to .gitlab/ci/configure_fedora44_ninja_instrumentation.cmake
index daf845f..1033477 100644
--- a/.gitlab/ci/configure_fedora43_ninja_instrumentation.cmake
+++ b/.gitlab/ci/configure_fedora44_ninja_instrumentation.cmake
@@ -1,3 +1,3 @@
 set(CMake_ENABLE_INSTRUMENTATION "ON" CACHE BOOL "")
 set(CMake_TEST_GUI "OFF" CACHE BOOL "")
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_ninja.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_ninja.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake b/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake
new file mode 100644
index 0000000..2289116
--- /dev/null
+++ b/.gitlab/ci/configure_fedora44_ninja_lfortran.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_lfortran.cmake")
diff --git a/.gitlab/ci/configure_fedora43_ninja_multi.cmake b/.gitlab/ci/configure_fedora44_ninja_multi.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_ninja_multi.cmake
rename to .gitlab/ci/configure_fedora44_ninja_multi.cmake
diff --git a/.gitlab/ci/configure_fedora43_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora44_ninja_multi_clang.cmake
similarity index 74%
rename from .gitlab/ci/configure_fedora43_ninja_multi_clang.cmake
rename to .gitlab/ci/configure_fedora44_ninja_multi_clang.cmake
index 522d012..3a54edf 100644
--- a/.gitlab/ci/configure_fedora43_ninja_multi_clang.cmake
+++ b/.gitlab/ci/configure_fedora44_ninja_multi_clang.cmake
@@ -1,3 +1,3 @@
 set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common_clang.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake b/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake
new file mode 100644
index 0000000..53f1b83
--- /dev/null
+++ b/.gitlab/ci/configure_fedora44_ninja_valgrind.cmake
@@ -0,0 +1,6 @@
+set(CMake_TEST_Qt5 ON CACHE BOOL "")
+set(CMake_TEST_Qt6 ON CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_valgrind.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_ninja.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora44_pvs_studio.cmake b/.gitlab/ci/configure_fedora44_pvs_studio.cmake
new file mode 100644
index 0000000..b15137e
--- /dev/null
+++ b/.gitlab/ci/configure_fedora44_pvs_studio.cmake
@@ -0,0 +1,3 @@
+set(configure_no_sccache 1)
+set(CMake_RUN_PVS_STUDIO ON CACHE BOOL "")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_sphinx.cmake b/.gitlab/ci/configure_fedora44_sphinx.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_sphinx.cmake
rename to .gitlab/ci/configure_fedora44_sphinx.cmake
diff --git a/.gitlab/ci/configure_fedora43_sphinx_package.cmake b/.gitlab/ci/configure_fedora44_sphinx_package.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_sphinx_package.cmake
rename to .gitlab/ci/configure_fedora44_sphinx_package.cmake
diff --git a/.gitlab/ci/configure_fedora43_tidy.cmake b/.gitlab/ci/configure_fedora44_tidy.cmake
similarity index 85%
rename from .gitlab/ci/configure_fedora43_tidy.cmake
rename to .gitlab/ci/configure_fedora44_tidy.cmake
index 5b0bd62..18e7743 100644
--- a/.gitlab/ci/configure_fedora43_tidy.cmake
+++ b/.gitlab/ci/configure_fedora44_tidy.cmake
@@ -4,4 +4,4 @@
 set(CMake_CLANG_TIDY_EXPORT_FIXES_DIR "$ENV{CI_PROJECT_DIR}/.gitlab/clang-tidy-fixes" CACHE PATH "")
 set(CMake_QT_MAJOR_VERSION "5" CACHE STRING "")
 
-include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora44_common.cmake")
diff --git a/.gitlab/ci/configure_fedora43_valgrind.cmake b/.gitlab/ci/configure_fedora44_valgrind.cmake
similarity index 100%
rename from .gitlab/ci/configure_fedora43_valgrind.cmake
rename to .gitlab/ci/configure_fedora44_valgrind.cmake
diff --git a/.gitlab/ci/configure_windows_pellesc_common.cmake b/.gitlab/ci/configure_windows_pellesc_common.cmake
index bc2cc51..7027e7c 100644
--- a/.gitlab/ci/configure_windows_pellesc_common.cmake
+++ b/.gitlab/ci/configure_windows_pellesc_common.cmake
@@ -1,6 +1,8 @@
 set(CMake_TEST_CXX OFF CACHE BOOL "")
 set(CMake_TEST_Java OFF CACHE BOOL "")
 
+set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "")
+
 set(configure_no_sccache 1)
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/ctest_memcheck_fedora43.valgrind.supp b/.gitlab/ci/ctest_memcheck_fedora44.valgrind.supp
similarity index 100%
rename from .gitlab/ci/ctest_memcheck_fedora43.valgrind.supp
rename to .gitlab/ci/ctest_memcheck_fedora44.valgrind.supp
diff --git a/.gitlab/ci/ctest_memcheck_fedora43_asan.lsan.supp b/.gitlab/ci/ctest_memcheck_fedora44_asan.lsan.supp
similarity index 100%
rename from .gitlab/ci/ctest_memcheck_fedora43_asan.lsan.supp
rename to .gitlab/ci/ctest_memcheck_fedora44_asan.lsan.supp
diff --git a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh
index 1ad42c4..3e76005 100755
--- a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh
+++ b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh
@@ -8,7 +8,10 @@
      --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \
                  7D2BAF1CF37B13E2069D6956105BD0E739499BDB
 
-curl -sSL https://get.rvm.io | bash -s stable
+curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer     -o rvm-installer &&
+curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc &&
+gpg2 --verify rvm-installer.asc rvm-installer &&
+bash rvm-installer stable
 
 # keep version in sync with `env_debian*_ninja.sh`
 /usr/local/rvm/bin/rvm install ruby-3.2.2
diff --git a/.gitlab/ci/docker/fedora43-hip/Dockerfile b/.gitlab/ci/docker/fedora44-hip/Dockerfile
similarity index 94%
rename from .gitlab/ci/docker/fedora43-hip/Dockerfile
rename to .gitlab/ci/docker/fedora44-hip/Dockerfile
index 0f0cbed..463d19f 100644
--- a/.gitlab/ci/docker/fedora43-hip/Dockerfile
+++ b/.gitlab/ci/docker/fedora44-hip/Dockerfile
@@ -1,6 +1,6 @@
 # syntax=docker/dockerfile:1
 
-ARG BASE_IMAGE=fedora:43
+ARG BASE_IMAGE=fedora:44
 
 FROM ${BASE_IMAGE} AS dnf-cache
 LABEL maintainer="Brad King <brad.king@kitware.com>"
diff --git a/.gitlab/ci/docker/fedora43-hip/deps_packages.lst b/.gitlab/ci/docker/fedora44-hip/deps_packages.lst
similarity index 93%
rename from .gitlab/ci/docker/fedora43-hip/deps_packages.lst
rename to .gitlab/ci/docker/fedora44-hip/deps_packages.lst
index abae13a..bac9e53 100644
--- a/.gitlab/ci/docker/fedora43-hip/deps_packages.lst
+++ b/.gitlab/ci/docker/fedora44-hip/deps_packages.lst
@@ -7,7 +7,6 @@
 make
 
 # Install HIP language toolchain.
-hsakmt-devel
 lld
 llvm
 rocm-comgr-devel
diff --git a/.gitlab/ci/docker/fedora43-hip/dnf.conf b/.gitlab/ci/docker/fedora44-hip/dnf.conf
similarity index 100%
rename from .gitlab/ci/docker/fedora43-hip/dnf.conf
rename to .gitlab/ci/docker/fedora44-hip/dnf.conf
diff --git a/.gitlab/ci/docker/fedora43/Dockerfile b/.gitlab/ci/docker/fedora44/Dockerfile
similarity index 98%
rename from .gitlab/ci/docker/fedora43/Dockerfile
rename to .gitlab/ci/docker/fedora44/Dockerfile
index 86ee3b8..d36ccc9 100644
--- a/.gitlab/ci/docker/fedora43/Dockerfile
+++ b/.gitlab/ci/docker/fedora44/Dockerfile
@@ -1,6 +1,6 @@
 # syntax=docker/dockerfile:1
 
-ARG BASE_IMAGE=fedora:43
+ARG BASE_IMAGE=fedora:44
 
 FROM ${BASE_IMAGE} AS dnf-cache
 # Populate DNF cache w/ the fresh metadata and prefetch packages.
@@ -132,4 +132,4 @@
     tar -C /usr/local -xf /root/rvm.tar
 
 RUN --mount=type=bind,from=rbenv-build,source=/root,target=/root \
-    tar -C / -xf /root/rbenv.tar && rbenv global 3.4.3
+    tar -C / -xf /root/rbenv.tar && rbenv global 3.4.9
diff --git a/.gitlab/ci/docker/fedora43/build_iwyu.sh b/.gitlab/ci/docker/fedora44/build_iwyu.sh
similarity index 100%
rename from .gitlab/ci/docker/fedora43/build_iwyu.sh
rename to .gitlab/ci/docker/fedora44/build_iwyu.sh
diff --git a/.gitlab/ci/docker/fedora43/build_rbenv.sh b/.gitlab/ci/docker/fedora44/build_rbenv.sh
similarity index 92%
rename from .gitlab/ci/docker/fedora43/build_rbenv.sh
rename to .gitlab/ci/docker/fedora44/build_rbenv.sh
index feb574f..f5ddd06 100755
--- a/.gitlab/ci/docker/fedora43/build_rbenv.sh
+++ b/.gitlab/ci/docker/fedora44/build_rbenv.sh
@@ -7,7 +7,7 @@
 # Ruby rbenv
 export RUBY_CONFIGURE_OPTS=--disable-install-doc
 export RUBY_BUILD_CURL_OPTS=-C-
-rbenv install 3.4.3 -k -s -v
+rbenv install 3.4.9 -k -s -v
 
 cat <<EOF >/tmp/exclude.lst
 *LICENSE*
diff --git a/.gitlab/ci/docker/fedora43/build_rust.sh b/.gitlab/ci/docker/fedora44/build_rust.sh
similarity index 88%
rename from .gitlab/ci/docker/fedora43/build_rust.sh
rename to .gitlab/ci/docker/fedora44/build_rust.sh
index 5ab91f7..1ee87eb 100755
--- a/.gitlab/ci/docker/fedora43/build_rust.sh
+++ b/.gitlab/ci/docker/fedora44/build_rust.sh
@@ -2,7 +2,7 @@
 
 set -e
 
-typos_version=1.44.0
+typos_version=1.45.2
 cargo install --root /usr/local --version "$typos_version" typos-cli
 
 strip /usr/local/bin/typos
diff --git a/.gitlab/ci/docker/fedora43/build_rvm.sh b/.gitlab/ci/docker/fedora44/build_rvm.sh
similarity index 71%
rename from .gitlab/ci/docker/fedora43/build_rvm.sh
rename to .gitlab/ci/docker/fedora44/build_rvm.sh
index 22c4333..9da34fe 100755
--- a/.gitlab/ci/docker/fedora43/build_rvm.sh
+++ b/.gitlab/ci/docker/fedora44/build_rvm.sh
@@ -8,7 +8,10 @@
      --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \
                  7D2BAF1CF37B13E2069D6956105BD0E739499BDB
 
-curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles
+curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer     -o rvm-installer &&
+curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc &&
+gpg2 --verify rvm-installer.asc rvm-installer &&
+bash rvm-installer stable --ignore-dotfiles
 
 export rvm_silence_banner=1
 
diff --git a/.gitlab/ci/docker/fedora43/deps_packages.lst b/.gitlab/ci/docker/fedora44/deps_packages.lst
similarity index 98%
rename from .gitlab/ci/docker/fedora43/deps_packages.lst
rename to .gitlab/ci/docker/fedora44/deps_packages.lst
index 143c296..b763bb7 100644
--- a/.gitlab/ci/docker/fedora43/deps_packages.lst
+++ b/.gitlab/ci/docker/fedora44/deps_packages.lst
@@ -96,7 +96,7 @@
 hdf5-openmpi-devel
 ImageMagick-c++-devel
 jasper-devel
-java-21-openjdk-devel
+java-25-openjdk-devel
 jsoncpp-devel
 lapack-devel
 libarchive-devel
diff --git a/.gitlab/ci/docker/fedora43/dnf.conf b/.gitlab/ci/docker/fedora44/dnf.conf
similarity index 100%
rename from .gitlab/ci/docker/fedora43/dnf.conf
rename to .gitlab/ci/docker/fedora44/dnf.conf
diff --git a/.gitlab/ci/docker/fedora43/install_deps.sh b/.gitlab/ci/docker/fedora44/install_deps.sh
similarity index 100%
rename from .gitlab/ci/docker/fedora43/install_deps.sh
rename to .gitlab/ci/docker/fedora44/install_deps.sh
diff --git a/.gitlab/ci/docker/fedora43/iwyu_packages.lst b/.gitlab/ci/docker/fedora44/iwyu_packages.lst
similarity index 100%
rename from .gitlab/ci/docker/fedora43/iwyu_packages.lst
rename to .gitlab/ci/docker/fedora44/iwyu_packages.lst
diff --git a/.gitlab/ci/docker/fedora43/rbenv_packages.lst b/.gitlab/ci/docker/fedora44/rbenv_packages.lst
similarity index 93%
rename from .gitlab/ci/docker/fedora43/rbenv_packages.lst
rename to .gitlab/ci/docker/fedora44/rbenv_packages.lst
index d16afff..7cba990 100644
--- a/.gitlab/ci/docker/fedora43/rbenv_packages.lst
+++ b/.gitlab/ci/docker/fedora44/rbenv_packages.lst
@@ -1,5 +1,6 @@
 rbenv
 ruby-build-rbenv
+ruby-build-ruby
 
 # Packages needed for `rbenv`
 # https://github.com/rbenv/ruby-build/wiki#fedora
diff --git a/.gitlab/ci/docker/fedora43/rust_packages.lst b/.gitlab/ci/docker/fedora44/rust_packages.lst
similarity index 100%
rename from .gitlab/ci/docker/fedora43/rust_packages.lst
rename to .gitlab/ci/docker/fedora44/rust_packages.lst
diff --git a/.gitlab/ci/docker/fedora43/rvm_packages.lst b/.gitlab/ci/docker/fedora44/rvm_packages.lst
similarity index 100%
rename from .gitlab/ci/docker/fedora43/rvm_packages.lst
rename to .gitlab/ci/docker/fedora44/rvm_packages.lst
diff --git a/.gitlab/ci/env_fedora43_common_clang.sh b/.gitlab/ci/env_fedora43_common_clang.sh
deleted file mode 100644
index 55533e7..0000000
--- a/.gitlab/ci/env_fedora43_common_clang.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-export CC=/usr/bin/clang-21
-export CXX=/usr/bin/clang++-21
-export FC=/usr/bin/flang-21
diff --git a/.gitlab/ci/env_fedora43_hip_radeon.sh b/.gitlab/ci/env_fedora43_hip_radeon.sh
deleted file mode 100644
index 2c942a8..0000000
--- a/.gitlab/ci/env_fedora43_hip_radeon.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-export HIPCXX=/usr/bin/clang++-21
-export HIPFLAGS='--rocm-path=/usr --rocm-device-lib-path=/usr/lib64/rocm/llvm/lib/clang/19/amdgcn/bitcode'
diff --git a/.gitlab/ci/env_fedora43_makefiles_clang.sh b/.gitlab/ci/env_fedora43_makefiles_clang.sh
deleted file mode 100644
index 3953f88..0000000
--- a/.gitlab/ci/env_fedora43_makefiles_clang.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_makefiles_lfortran.sh b/.gitlab/ci/env_fedora43_makefiles_lfortran.sh
deleted file mode 100644
index 6b02339..0000000
--- a/.gitlab/ci/env_fedora43_makefiles_lfortran.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake
deleted file mode 100644
index fb004e8..0000000
--- a/.gitlab/ci/env_fedora43_makefiles_symlinked.cmake
+++ /dev/null
@@ -1 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/env_fedora43_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora43_makefiles_symlinked.sh b/.gitlab/ci/env_fedora43_makefiles_symlinked.sh
deleted file mode 100644
index 448b592..0000000
--- a/.gitlab/ci/env_fedora43_makefiles_symlinked.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_makefiles.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_clang.sh b/.gitlab/ci/env_fedora43_ninja_clang.sh
deleted file mode 100644
index 3953f88..0000000
--- a/.gitlab/ci/env_fedora43_ninja_clang.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_instrumentation.sh b/.gitlab/ci/env_fedora43_ninja_instrumentation.sh
deleted file mode 100644
index cc89d0b..0000000
--- a/.gitlab/ci/env_fedora43_ninja_instrumentation.sh
+++ /dev/null
@@ -1 +0,0 @@
-source .gitlab/ci/env_fedora43_ninja.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_lfortran.sh b/.gitlab/ci/env_fedora43_ninja_lfortran.sh
deleted file mode 100644
index 6b02339..0000000
--- a/.gitlab/ci/env_fedora43_ninja_lfortran.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_multi_clang.sh b/.gitlab/ci/env_fedora43_ninja_multi_clang.sh
deleted file mode 100644
index 3953f88..0000000
--- a/.gitlab/ci/env_fedora43_ninja_multi_clang.sh
+++ /dev/null
@@ -1 +0,0 @@
-. .gitlab/ci/env_fedora43_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_valgrind.sh b/.gitlab/ci/env_fedora43_ninja_valgrind.sh
deleted file mode 100644
index cc89d0b..0000000
--- a/.gitlab/ci/env_fedora43_ninja_valgrind.sh
+++ /dev/null
@@ -1 +0,0 @@
-source .gitlab/ci/env_fedora43_ninja.sh
diff --git a/.gitlab/ci/env_fedora43_asan.sh b/.gitlab/ci/env_fedora44_asan.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_asan.sh
rename to .gitlab/ci/env_fedora44_asan.sh
diff --git a/.gitlab/ci/env_fedora43_bullseye_coverage.sh b/.gitlab/ci/env_fedora44_bullseye_coverage.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_bullseye_coverage.sh
rename to .gitlab/ci/env_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/env_fedora43_clang_analyzer.sh b/.gitlab/ci/env_fedora44_clang_analyzer.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_clang_analyzer.sh
rename to .gitlab/ci/env_fedora44_clang_analyzer.sh
diff --git a/.gitlab/ci/env_fedora43_clang_fuzzing.sh b/.gitlab/ci/env_fedora44_clang_fuzzing.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_clang_fuzzing.sh
rename to .gitlab/ci/env_fedora44_clang_fuzzing.sh
diff --git a/.gitlab/ci/env_fedora43_clazy.sh b/.gitlab/ci/env_fedora44_clazy.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_clazy.sh
rename to .gitlab/ci/env_fedora44_clazy.sh
diff --git a/.gitlab/ci/env_fedora44_common_clang.sh b/.gitlab/ci/env_fedora44_common_clang.sh
new file mode 100644
index 0000000..2200841
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_common_clang.sh
@@ -0,0 +1,3 @@
+export CC=/usr/bin/clang
+export CXX=/usr/bin/clang++
+export FC=/usr/bin/flang
diff --git a/.gitlab/ci/env_fedora43_common_lfortran.sh b/.gitlab/ci/env_fedora44_common_lfortran.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_common_lfortran.sh
rename to .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_extdeps.sh b/.gitlab/ci/env_fedora44_extdeps.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_extdeps.sh
rename to .gitlab/ci/env_fedora44_extdeps.sh
diff --git a/.gitlab/ci/env_fedora43_fastbuild.sh b/.gitlab/ci/env_fedora44_fastbuild.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_fastbuild.sh
rename to .gitlab/ci/env_fedora44_fastbuild.sh
diff --git a/.gitlab/ci/env_fedora44_hip_radeon.patch b/.gitlab/ci/env_fedora44_hip_radeon.patch
new file mode 100644
index 0000000..a6411ed
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_hip_radeon.patch
@@ -0,0 +1,14 @@
+--- a/usr/lib/clang/22/include/cuda_wrappers/new
++++ b/usr/lib/clang/22/include/cuda_wrappers/new
+@@ -98,9 +98,9 @@
+ __device__ inline void *operator new[](__SIZE_TYPE__, void *__ptr) CUDA_NOEXCEPT {
+   return __ptr;
+ }
+-#endif
+ __device__ inline void operator delete(void *, void *) CUDA_NOEXCEPT {}
+ __device__ inline void operator delete[](void *, void *) CUDA_NOEXCEPT {}
++#endif
+
+ #pragma pop_macro("CUDA_NOEXCEPT")
+
+---
diff --git a/.gitlab/ci/env_fedora44_hip_radeon.sh b/.gitlab/ci/env_fedora44_hip_radeon.sh
new file mode 100644
index 0000000..dfa0121
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_hip_radeon.sh
@@ -0,0 +1,4 @@
+export HIPCXX=/usr/bin/clang++
+export HIPFLAGS='--rocm-path=/usr --rocm-device-lib-path=/usr/lib64/rocm/llvm/lib/clang/20/lib/amdgcn/bitcode'
+
+git apply --unsafe-paths --directory=/ .gitlab/ci/env_fedora44_hip_radeon.patch
diff --git a/.gitlab/ci/env_fedora43_makefiles.cmake b/.gitlab/ci/env_fedora44_makefiles.cmake
similarity index 100%
rename from .gitlab/ci/env_fedora43_makefiles.cmake
rename to .gitlab/ci/env_fedora44_makefiles.cmake
diff --git a/.gitlab/ci/env_fedora43_makefiles.sh b/.gitlab/ci/env_fedora44_makefiles.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_makefiles.sh
rename to .gitlab/ci/env_fedora44_makefiles.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_clang.sh b/.gitlab/ci/env_fedora44_makefiles_clang.sh
new file mode 100644
index 0000000..4d2ba81
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_makefiles_clang.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_lfortran.sh b/.gitlab/ci/env_fedora44_makefiles_lfortran.sh
new file mode 100644
index 0000000..409404e
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_makefiles_lfortran.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake
new file mode 100644
index 0000000..8c39210
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_makefiles_symlinked.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/env_fedora44_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora44_makefiles_symlinked.sh b/.gitlab/ci/env_fedora44_makefiles_symlinked.sh
new file mode 100644
index 0000000..84a747c
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_makefiles_symlinked.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_makefiles.sh
diff --git a/.gitlab/ci/env_fedora43_ninja.sh b/.gitlab/ci/env_fedora44_ninja.sh
similarity index 83%
rename from .gitlab/ci/env_fedora43_ninja.sh
rename to .gitlab/ci/env_fedora44_ninja.sh
index 18c2158..3607fb1 100644
--- a/.gitlab/ci/env_fedora43_ninja.sh
+++ b/.gitlab/ci/env_fedora44_ninja.sh
@@ -4,9 +4,9 @@
 fi
 
 # This job builds CMake as C++11.  Prevent C++17 headers from being used.
-if test "$CMAKE_CONFIGURATION" = "fedora43_ninja" -a "$CI_JOB_STAGE" = "build"; then
+if test "$CMAKE_CONFIGURATION" = "fedora44_ninja" -a "$CI_JOB_STAGE" = "build"; then
   for i in filesystem optional string_view; do
-    sed -i -e '$a#error "Use <cm/'"$i"'> instead"' "/usr/include/c++/15/$i"
+    sed -i -e '$a#error "Use <cm/'"$i"'> instead"' "/usr/include/c++/16/$i"
   done
 fi
 
diff --git a/.gitlab/ci/env_fedora44_ninja_clang.sh b/.gitlab/ci/env_fedora44_ninja_clang.sh
new file mode 100644
index 0000000..4d2ba81
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_ninja_clang.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_gcc_std_reloc.sh b/.gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh
similarity index 85%
rename from .gitlab/ci/env_fedora43_ninja_gcc_std_reloc.sh
rename to .gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh
index 91b824d..ae640b7 100644
--- a/.gitlab/ci/env_fedora43_ninja_gcc_std_reloc.sh
+++ b/.gitlab/ci/env_fedora44_ninja_gcc_std_reloc.sh
@@ -1,6 +1,6 @@
 # "Misplace" the `libstdc++.modules.json` file so that
 # `CMAKE_CXX_STDLIB_MODULES_JSON` is needed to use `import std`.
-stdlib_modules_dir="/usr/lib/gcc/x86_64-redhat-linux/15"
+stdlib_modules_dir="/usr/lib/gcc/x86_64-redhat-linux/16"
 mkdir "$stdlib_modules_dir.reloc"
 mv "$stdlib_modules_dir/libstdc++.modules.json" "$stdlib_modules_dir.reloc"
 export CMAKE_CI_CXX_STDLIB_MODULES_JSON="$stdlib_modules_dir.reloc/libstdc++.modules.json"
diff --git a/.gitlab/ci/env_fedora44_ninja_instrumentation.sh b/.gitlab/ci/env_fedora44_ninja_instrumentation.sh
new file mode 100644
index 0000000..99f7538
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_ninja_instrumentation.sh
@@ -0,0 +1 @@
+source .gitlab/ci/env_fedora44_ninja.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_lfortran.sh b/.gitlab/ci/env_fedora44_ninja_lfortran.sh
new file mode 100644
index 0000000..409404e
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_ninja_lfortran.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_common_lfortran.sh
diff --git a/.gitlab/ci/env_fedora43_ninja_multi.sh b/.gitlab/ci/env_fedora44_ninja_multi.sh
similarity index 100%
rename from .gitlab/ci/env_fedora43_ninja_multi.sh
rename to .gitlab/ci/env_fedora44_ninja_multi.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_multi_clang.sh b/.gitlab/ci/env_fedora44_ninja_multi_clang.sh
new file mode 100644
index 0000000..4d2ba81
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_ninja_multi_clang.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora44_common_clang.sh
diff --git a/.gitlab/ci/env_fedora44_ninja_valgrind.sh b/.gitlab/ci/env_fedora44_ninja_valgrind.sh
new file mode 100644
index 0000000..99f7538
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_ninja_valgrind.sh
@@ -0,0 +1 @@
+source .gitlab/ci/env_fedora44_ninja.sh
diff --git a/.gitlab/ci/env_fedora44_pvs_studio.sh b/.gitlab/ci/env_fedora44_pvs_studio.sh
new file mode 100644
index 0000000..36dba85
--- /dev/null
+++ b/.gitlab/ci/env_fedora44_pvs_studio.sh
@@ -0,0 +1 @@
+. .gitlab/ci/pvs-studio-env.sh
diff --git a/.gitlab/ci/env_windows_pellesc13.01.ps1 b/.gitlab/ci/env_windows_pellesc13.01.ps1
index 4a8b1ca..84a38ac 100644
--- a/.gitlab/ci/env_windows_pellesc13.01.ps1
+++ b/.gitlab/ci/env_windows_pellesc13.01.ps1
@@ -1,6 +1,2 @@
 . .gitlab/ci/ninja-env.ps1
 . .gitlab/ci/pellesc-env.ps1
-
-# FIXME(#21536): Avoid requiring the end user to enable
-# Pelles C's Microsoft extensions to use Windows APIs.
-$env:CFLAGS = "-Ze"
diff --git a/.gitlab/ci/post_build_fedora43_tidy.sh b/.gitlab/ci/post_build_fedora44_tidy.sh
similarity index 100%
rename from .gitlab/ci/post_build_fedora43_tidy.sh
rename to .gitlab/ci/post_build_fedora44_tidy.sh
diff --git a/.gitlab/ci/pre_build_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_build_fedora44_bullseye_coverage.sh
similarity index 100%
rename from .gitlab/ci/pre_build_fedora43_bullseye_coverage.sh
rename to .gitlab/ci/pre_build_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pre_configure_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_configure_fedora44_bullseye_coverage.sh
similarity index 100%
rename from .gitlab/ci/pre_configure_fedora43_bullseye_coverage.sh
rename to .gitlab/ci/pre_configure_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pre_configure_fedora43_tidy.sh b/.gitlab/ci/pre_configure_fedora44_tidy.sh
similarity index 100%
rename from .gitlab/ci/pre_configure_fedora43_tidy.sh
rename to .gitlab/ci/pre_configure_fedora44_tidy.sh
diff --git a/.gitlab/ci/pre_test_fedora43_bullseye_coverage.sh b/.gitlab/ci/pre_test_fedora44_bullseye_coverage.sh
similarity index 100%
rename from .gitlab/ci/pre_test_fedora43_bullseye_coverage.sh
rename to .gitlab/ci/pre_test_fedora44_bullseye_coverage.sh
diff --git a/.gitlab/ci/pvs-studio-env.sh b/.gitlab/ci/pvs-studio-env.sh
new file mode 100644
index 0000000..c179b30
--- /dev/null
+++ b/.gitlab/ci/pvs-studio-env.sh
@@ -0,0 +1,13 @@
+.gitlab/ci/pvs-studio.sh
+export PATH=$PWD/.gitlab/pvs-studio/bin:$PATH
+pvs-studio --version
+
+if test -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" -a -f "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY"; then
+  pvs-studio-analyzer credentials "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_USER")" "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_KEY")"
+else
+ echo "The CMAKE_CI_PVS_STUDIO_LICENSE_USER and CMAKE_CI_PVS_STUDIO_LICENSE_KEY files were not both provided!"
+ exit 1
+fi
+rm -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY"
+unset CMAKE_CI_PVS_STUDIO_LICENSE_KEY
+unset CMAKE_CI_PVS_STUDIO_LICENSE_USER
diff --git a/.gitlab/ci/pvs-studio.sh b/.gitlab/ci/pvs-studio.sh
new file mode 100755
index 0000000..b0eddf3
--- /dev/null
+++ b/.gitlab/ci/pvs-studio.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -e
+
+readonly version="7.42.105112.761"
+
+case "$(uname -s)-$(uname -m)" in
+    Linux-x86_64)
+        # https://files.pvs-studio.com/pvs-studio-7.42.105112.761-x86_64.tgz
+        shatool="sha256sum"
+        sha256sum="0ced11d17855cc172a860f8cc19a89f10adb6ab7185992fb57474118e9bdabd7"
+        filename="pvs-studio-$version-x86_64"
+        ;;
+    *)
+        echo "Unrecognized platform $(uname -s)-$(uname -m)"
+        exit 1
+        ;;
+esac
+readonly shatool
+readonly sha256sum
+
+cd .gitlab
+
+# This URL is only visible inside of Kitware's network.  See above filename table.
+baseurl="https://cmake.org/files/dependencies/internal"
+
+tarball="$filename.tgz"
+echo "$sha256sum  $tarball" > pvs-studio.sha256sum
+curl -OL "$baseurl/$tarball"
+$shatool --check pvs-studio.sha256sum
+tar xzf "$tarball"
+rm "$tarball" pvs-studio.sha256sum
+mv "$filename" "pvs-studio"
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 5e6f1ee..96fed40 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -5,7 +5,7 @@
 ### Release
 
 .linux_prep_source:
-    image: "fedora:43"
+    image: "fedora:44"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -58,7 +58,7 @@
 ### Debian
 
 .debian13:
-    image: "kitware/cmake:ci-debian13-x86_64-2026-02-08"
+    image: "kitware/cmake:ci-debian13-x86_64-2026-04-28"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -81,15 +81,15 @@
 
 ### Fedora
 
-.fedora43:
-    image: "kitware/cmake:ci-fedora43-x86_64-2026-02-08"
+.fedora44:
+    image: "kitware/cmake:ci-fedora44-x86_64-2026-04-30"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes"
         CMAKE_ARCH: x86_64
 
-.fedora43_hip:
-    image: "kitware/cmake:ci-fedora43-hip-x86_64-2025-10-29"
+.fedora44_hip:
+    image: "kitware/cmake:ci-fedora44-hip-x86_64-2026-04-30"
 
     variables:
         # FIXME(rocclr): device modules fail loading from binaries in paths with spaces
@@ -98,26 +98,26 @@
 
 #### Coverage builds
 
-.fedora43_gcc_gcov:
-    extends: .fedora43
+.fedora44_gcc_gcov:
+    extends: .fedora44
 
     variables:
         # See issue #20001
         CMAKE_GENERATOR: "Unix Makefiles"
-        CMAKE_CONFIGURATION: fedora43_gcc_gcov
+        CMAKE_CONFIGURATION: fedora44_gcc_gcov
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_BUILD_TYPE: Debug
         CMAKE_CI_TEST_TIMEOUT: "1500"
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_bullseye_coverage:
-    extends: .fedora43
+.fedora44_bullseye_coverage:
+    extends: .fedora44
     environment:
         name: bullseye-coverage
     variables:
         # See issue #20001
         CMAKE_GENERATOR: "Unix Makefiles"
-        CMAKE_CONFIGURATION: fedora43_bullseye_coverage
+        CMAKE_CONFIGURATION: fedora44_bullseye_coverage
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_BUILD_TYPE: Debug
         CMAKE_CI_TEST_TIMEOUT: "1500"
@@ -125,53 +125,61 @@
 
 #### Lint builds
 
-.fedora43_tidy:
-    extends: .fedora43
+.fedora44_tidy:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_tidy
+        CMAKE_CONFIGURATION: fedora44_tidy
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_clang_analyzer:
-    extends: .fedora43
+.fedora44_pvs_studio:
+    extends: .fedora44
+    environment:
+        name: pvs-studio
+    variables:
+        CMAKE_CONFIGURATION: fedora44_pvs_studio
+        CMAKE_CI_NO_INSTALL: 1
+
+.fedora44_clang_analyzer:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_clang_analyzer
+        CMAKE_CONFIGURATION: fedora44_clang_analyzer
         CMAKE_CI_BUILD_TYPE: Debug
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_clang_fuzzing:
-    extends: .fedora43
+.fedora44_clang_fuzzing:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_clang_fuzzing
+        CMAKE_CONFIGURATION: fedora44_clang_fuzzing
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_clazy:
-    extends: .fedora43
+.fedora44_clazy:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_clazy
+        CMAKE_CONFIGURATION: fedora44_clazy
         CMAKE_CI_BUILD_TYPE: Debug
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_sphinx:
-    extends: .fedora43
+.fedora44_sphinx:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_sphinx
+        CMAKE_CONFIGURATION: fedora44_sphinx
         CTEST_NO_WARNINGS_ALLOWED: 1
         CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx"
         CMAKE_CI_NO_INSTALL: 1
 
-.fedora43_sphinx_package:
-    extends: .fedora43
+.fedora44_sphinx_package:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_sphinx_package
+        CMAKE_CONFIGURATION: fedora44_sphinx_package
         CTEST_SOURCE_SUBDIRECTORY: "Utilities/Sphinx"
 
 #### Build and test
@@ -227,53 +235,53 @@
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora43_extdeps:
-    extends: .fedora43
+.fedora44_extdeps:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_extdeps
+        CMAKE_CONFIGURATION: fedora44_extdeps
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora43_ninja:
-    extends: .fedora43
+.fedora44_ninja:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja
+        CMAKE_CONFIGURATION: fedora44_ninja
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.fedora43_makefiles:
-    extends: .fedora43
+.fedora44_makefiles:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_makefiles
+        CMAKE_CONFIGURATION: fedora44_makefiles
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Unix Makefiles"
 
-.fedora43_makefiles_symlinked:
-    extends: .fedora43
+.fedora44_makefiles_symlinked:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_makefiles_symlinked
+        CMAKE_CONFIGURATION: fedora44_makefiles_symlinked
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Unix Makefiles"
         CMAKE_CI_IN_SYMLINK_TREE: 1
         CMAKE_CI_BUILD_DIR: "real_work/work/build"
 
-.fedora43_fastbuild:
-    extends: .fedora43
+.fedora44_fastbuild:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_fastbuild
+        CMAKE_CONFIGURATION: fedora44_fastbuild
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "FASTBuild"
 
-.fedora43_ninja_instrumentation:
-    extends: .fedora43
+.fedora44_ninja_instrumentation:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_instrumentation
+        CMAKE_CONFIGURATION: fedora44_ninja_instrumentation
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Ninja"
 
@@ -292,57 +300,57 @@
     variables:
         CMAKE_CONFIGURATION: debian13_ninja_clang
 
-.fedora43_makefiles_clang:
-    extends: .fedora43
+.fedora44_makefiles_clang:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_makefiles_clang
+        CMAKE_CONFIGURATION: fedora44_makefiles_clang
         CMAKE_GENERATOR: "Unix Makefiles"
 
-.fedora43_makefiles_lfortran:
-    extends: .fedora43
+.fedora44_makefiles_lfortran:
+    extends: .fedora44
 
     variables:
         # FIXME(lfortran): -rpath flags with spaces not forwarded
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
-        CMAKE_CONFIGURATION: fedora43_makefiles_lfortran
+        CMAKE_CONFIGURATION: fedora44_makefiles_lfortran
         CMAKE_GENERATOR: "Unix Makefiles"
         CTEST_LABELS: "Fortran"
 
-.fedora43_ninja_lfortran:
-    extends: .fedora43
+.fedora44_ninja_lfortran:
+    extends: .fedora44
 
     variables:
         # FIXME(lfortran): -rpath flags with spaces not forwarded
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
-        CMAKE_CONFIGURATION: fedora43_ninja_lfortran
+        CMAKE_CONFIGURATION: fedora44_ninja_lfortran
         CTEST_LABELS: "Fortran"
 
-.fedora43_ninja_multi:
-    extends: .fedora43
+.fedora44_ninja_multi:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_multi
+        CMAKE_CONFIGURATION: fedora44_ninja_multi
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
-.fedora43_ninja_clang:
-    extends: .fedora43
+.fedora44_ninja_clang:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_clang
+        CMAKE_CONFIGURATION: fedora44_ninja_clang
 
-.fedora43_ninja_gcc_std_reloc:
-    extends: .fedora43
+.fedora44_ninja_gcc_std_reloc:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_gcc_std_reloc
+        CMAKE_CONFIGURATION: fedora44_ninja_gcc_std_reloc
         CTEST_LABELS: "CXXModules"
 
-.fedora43_ninja_multi_clang:
-    extends: .fedora43
+.fedora44_ninja_multi_clang:
+    extends: .fedora44
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_multi_clang
+        CMAKE_CONFIGURATION: fedora44_ninja_multi_clang
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
 ### Sanitizers
@@ -365,23 +373,23 @@
         CTEST_MEMORYCHECK_TYPE: Valgrind
         CMAKE_CI_RUN_MEMCHECK: "true"
         CMAKE_CI_TEST_TIMEOUT: "1500"
-        CMAKE_VALGRIND_CONFIGURATION: fedora43
+        CMAKE_VALGRIND_CONFIGURATION: fedora44
 
-.fedora43_asan:
+.fedora44_asan:
     extends:
-        - .fedora43
+        - .fedora44
         - .fedora_asan_addon
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_asan
+        CMAKE_CONFIGURATION: fedora44_asan
 
-.fedora43_ninja_valgrind:
+.fedora44_ninja_valgrind:
     extends:
-        - .fedora43
+        - .fedora44
         - .fedora_valgrind_addon
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_ninja_valgrind
+        CMAKE_CONFIGURATION: fedora44_ninja_valgrind
 
 ### Intel Compiler
 
@@ -591,11 +599,11 @@
         CMAKE_CONFIGURATION: debian13_hip_radeon
         CTEST_LABELS: "HIP"
 
-.fedora43_hip_radeon:
-    extends: .fedora43_hip
+.fedora44_hip_radeon:
+    extends: .fedora44_hip
 
     variables:
-        CMAKE_CONFIGURATION: fedora43_hip_radeon
+        CMAKE_CONFIGURATION: fedora44_hip_radeon
         CTEST_LABELS: "HIP"
 
 .hip6.3_nvidia:
@@ -750,14 +758,14 @@
 
 .cmake_version_update_linux:
     stage: build
-    extends: .fedora43
+    extends: .fedora44
     script:
         - .gitlab/ci/cmake_version_update.sh
     interruptible: false # The job internally fetches and retries.
 
 .cmake_spellcheck_linux:
     stage: build
-    extends: .fedora43
+    extends: .fedora44
     script:
         - .gitlab/ci/codespell.bash
         - .gitlab/ci/typos.bash
@@ -928,7 +936,7 @@
 .cmake_org_help:
     stage: build
     extends:
-        - .fedora43
+        - .fedora44
         - .linux_x86_64_tags
         - .cmake_org_help_artifacts
     script:
diff --git a/.gitlab/upload.yml b/.gitlab/upload.yml
index 47316d1..7e7516b 100644
--- a/.gitlab/upload.yml
+++ b/.gitlab/upload.yml
@@ -1,7 +1,7 @@
 # Steps for uploading artifacts
 
 .rsync_upload_package:
-    image: "fedora:43"
+    image: "fedora:44"
     stage: upload
     tags:
         - cmake
@@ -21,7 +21,7 @@
 
 .rsync_upload_help:
     stage: upload
-    image: "fedora:43"
+    image: "fedora:44"
     tags:
         - cmake
         - docker
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 64acb89..b986c76 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -113,7 +113,7 @@
 
     # NOTE See BUG https://github.com/crate-ci/typos/issues/390
   - repo: https://github.com/adhtruong/mirrors-typos
-    rev: v1.44.0
+    rev: v1.45.2
     hooks:
       - id: typos
         # NOTE Override hook's default args to prevent automatic
diff --git a/.pvsconfig b/.pvsconfig
new file mode 100644
index 0000000..4d7413f
--- /dev/null
+++ b/.pvsconfig
@@ -0,0 +1,100 @@
+# Identical sub-expressions to the left and to the right of 'foo' operator.
+//-V::501
+# Pointer to local variable 'X' is stored outside the scope of this variable. Such a pointer will become invalid.
+//-V::506
+# The 'new type(n)' pattern was detected. Probably meant: 'new type[n]'.
+//-V::508
+# The 'x' variable is assigned values twice successively. Perhaps this is a mistake.
+//-V::519
+# Possible null pointer dereference.
+//-V::522
+# Constant value is represented by an octal form.
+//-V::536
+# Iterators are passed as arguments to 'Foo' function. Consider inspecting the expression.
+//-V::539
+# Expression is always true/false.
+//-V::547
+# Expression of the 'A - B > 0' kind will work as 'A != B'.
+//-V::555
+# Possible array overrun.
+//-V::557
+# Part of conditional expression is always true/false.
+//-V::560
+# It is suspicious that the argument of sizeof() operator is the expression.
+//-V::568
+# Variable is assigned to itself.
+//-V::570
+# Recurring check. This condition was already verified in previous line.
+//-V::571
+# Function receives suspicious argument.
+//-V::575
+# Conditional expressions of 'if' statements located next to each other are identical.
+//-V::581
+# The 'Foo' function is called twice to deallocate the same resource.
+//-V::586
+# Expression is enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or typo is present.
+//-V::592
+# Pointer was used before its check for nullptr. Check lines: N1, N2.
+//-V::595
+# Possible division or mod by zero.
+//-V::609
+# Condition of a loop is always true/false.
+//-V::654
+# Possible meaningless check for null, as memory was allocated using 'new' operator. Memory allocation will lead to an exception.
+//-V::668
+# An object is used as an argument to its own method.
+//-V::678
+# Pattern A || (A && ...) was detected. The expression is excessive or contains a logical error.
+//-V::686
+# The class implements a copy constructor/operator=, but lacks the operator=/copy constructor.
+//-V::690
+# Excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'.
+//-V::728
+# Not all members of a class are initialized inside the constructor.
+//-V::730
+# Suspicious access to element of '...' array by a constant index inside a loop.
+//-V::767
+# The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used.
+//-V::769
+# Two similar code fragments were found.
+//-V::778
+# Value of a variable is checked after it is used. Possible error in program's logic. Check lines: N1, N2.
+//-V::781
+# Variable is assigned but not used by the end of the function.
+//-V::1001
+# Pointer was used unsafely after its check for nullptr.
+//-V::1004
+# Value from the uninitialized optional is used. It may be an error.
+//-V::1007
+# The value is out of range of enum values. This causes unspecified or undefined behavior.
+//-V::1016
+# A pointer without owner is added to the container by the 'emplace_back' method. A memory leak will occur in case of an exception.
+//-V::1023
+# Possible overflow. Consider casting operands, not the result.
+//-V::1028
+# Variable is used after it is moved.
+//-V::1030
+# Two or more case-branches perform the same actions.
+//-V::1037
+# This file is marked with copyleft license, which requires you to open the derived source code.
+//-V::1042
+# Loop break conditions do not depend on the number of iterations.
+//-V::1044
+# Variable 'foo' was assigned the same value.
+//-V::1048
+# It is possible that an assigned variable should be checked in the next condition. Consider checking for typos.
+//-V::1051
+# Calling the 'foo' virtual function in the constructor/destructor may lead to unexpected result at runtime.
+//-V::1053
+# Return value is not always used. Consider inspecting the 'foo' function.
+//-V::1071
+# Conditional initialization inside the constructor may leave some members uninitialized.
+//-V::1077
+# Call of the 'Foo' function will lead to buffer underflow.
+//-V::1086
+# Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wake-up.
+//-V::1089
+# The 'emplace' / 'insert' function call contains potentially dangerous move operation. Moved object can be destroyed even if there is no insertion.
+//-V::1098
+# Preprocessing directive is present within a macro argument. This leads to undefined behavior.
+//-V::1119
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index fb3d64a..a2b2167 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -415,6 +415,7 @@
             \ Swift_LANGUAGE_VERSION
             \ Swift_MODULE_DIRECTORY
             \ Swift_MODULE_NAME
+            \ Swift_PACKAGE_NAME
             \ Swift_SEPARATE_MODULE_EMISSION
             \ TARGET_ARCHIVES_MAY_BE_SHARED_LIBS
             \ TARGET_MESSAGES
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 06778c7..b391315 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -391,6 +391,22 @@
   list(APPEND CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${CMake_IWYU_OPTIONS})
 endif()
 
+option(CMake_RUN_PVS_STUDIO "Run pvs-studio-analyzer with the compiler." OFF)
+if(CMake_RUN_PVS_STUDIO)
+  if(CMake_SOURCE_DIR STREQUAL CMake_BINARY_DIR)
+    message(FATAL_ERROR "CMake_RUN_PVS_STUDIO requires an out-of-source build!")
+  endif()
+  find_program(PVS_STUDIO_COMMAND NAMES pvs-studio-analyzer)
+  if(NOT PVS_STUDIO_COMMAND)
+    message(FATAL_ERROR "CMake_RUN_PVS_STUDIO is ON but pvs-studio-analyzer is not found!")
+  endif()
+  set(CMAKE_CXX_PVS_STUDIO
+    "${PVS_STUDIO_COMMAND}" analyze
+      -a GA
+      --apply-pvs-configs
+      --project-root "${CMAKE_SOURCE_DIR}"
+  )
+endif()
 
 #-----------------------------------------------------------------------
 # a macro that only sets the FOLDER target property if it's
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in
index 75642c9..591da55 100644
--- a/CTestCustom.cmake.in
+++ b/CTestCustom.cmake.in
@@ -51,6 +51,7 @@
   "Warning: Function .* can throw only the exceptions thrown by the function .* it overrides\\."
   "WarningMessagesDialog\\.cxx"
   "warning.*directory name.*CMake-Xcode.*/bin/.*does not exist.*"
+  "stl_algo(base)?\\.h:[0-9:]* warning: loop not unrolled"
   "stl_deque.h:1051"
   "Tests/CMakeLib/testCTestResourceSpec.cxx:.*warning: missing initializer for member.*cmCTestResourceSpec::.*" # GCC 4.8 disagrees with later compilers on C++11 initializer list conversion
   "(Lexer|Parser).*warning.*conversion.*may (alter its value|change the sign)"
diff --git a/Help/command/ctest_build.rst b/Help/command/ctest_build.rst
index bce1739..10922a7 100644
--- a/Help/command/ctest_build.rst
+++ b/Help/command/ctest_build.rst
@@ -15,6 +15,7 @@
               [NUMBER_WARNINGS <num-warn-var>]
               [RETURN_VALUE <result-var>]
               [CAPTURE_CMAKE_ERROR <result-var>]
+              [PRESET <preset>]
               )
 
 Build the project and store results in ``Build.xml``
@@ -75,6 +76,15 @@
 ``NUMBER_WARNINGS <num-warn-var>``
   Store the number of build warnings detected in the given variable.
 
+``PRESET <preset>``
+  .. versionadded:: 4.4
+
+  Specify a :manual:`preset <cmake-presets(7)>` to use when building the
+  project.  Any value set in the CTest script will take priority over a
+  corresponding setting from the preset.  For example, the ``TARGET``
+  argument will override the :preset:`buildPresets.targets` setting from
+  the chosen preset.
+
 ``RETURN_VALUE <result-var>``
   Store the return value of the native build tool in the given variable.
 
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst
index ad43cd2..47dfe23 100644
--- a/Help/command/ctest_test.rst
+++ b/Help/command/ctest_test.rst
@@ -183,6 +183,13 @@
   file is **not** uploaded to CDash because it would be redundant with
   CTest's ``Test.xml`` file.
 
+``COVERAGE_TOOL <tool>``
+  .. versionadded:: 4.4
+
+  Specify the ``<tool>`` used for collecting coverage during the running
+  of the tests.  See the CTest :ref:`CoverageTool <ctest-CoverageTool>`
+  setting for details.
+
 ``QUIET``
   .. versionadded:: 3.3
 
diff --git a/Help/command/install.rst b/Help/command/install.rst
index 7ed27e7..e69d266 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -60,6 +60,10 @@
   ``<dir>`` should be a relative path.  An absolute path is allowed,
   but not recommended.
 
+  .. versionadded:: 4.4
+    The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be
+    enabled to warn or error out when an absolute destination is provided.
+
   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``
diff --git a/Help/command/list.rst b/Help/command/list.rst
index 88516f0..a84e2cb 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -19,7 +19,7 @@
 
   `Modification`_
     list(`APPEND`_ <list> [<element>...])
-    list(`FILTER`_ <list> {INCLUDE | EXCLUDE} REGEX <regex>)
+    list(`FILTER`_ <list> <INCLUDE|EXCLUDE> <MODE>)
     list(`INSERT`_ <list> <index> [<element>...])
     list(`POP_BACK`_ <list> [<out-var>...])
     list(`POP_FRONT`_ <list> [<out-var>...])
@@ -120,15 +120,54 @@
   that empty list.
 
 .. signature::
-  list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
+  list(FILTER <list> <INCLUDE|EXCLUDE> <MODE>)
 
-.. versionadded:: 3.6
+  .. versionadded:: 3.6
 
-Includes or removes items from the list that match the mode's pattern.
-In ``REGEX`` mode, items will be matched against the given regular expression.
+  Includes or removes items from the list that match the mode's pattern.
 
-For more information on regular expressions look under
-:ref:`string(REGEX) <Regex Specification>`.
+  ``<MODE>`` must be one of the following:
+
+    ``REGEX``
+      Items will be matched against the given regular expression.
+
+      .. code-block:: cmake
+
+        list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
+
+      For more information on regular expressions look under
+      :ref:`string(REGEX) <Regex Specification>`.
+
+    ``PREDICATE``
+      Specify a user-defined callable as a predicate.
+
+      .. code-block:: cmake
+
+        list(FILTER <list> <INCLUDE|EXCLUDE> PREDICATE <function>)
+
+      .. versionadded:: 4.4
+
+      ``<function>`` is a user-defined :command:`function` that acts as a
+      unary predicate.  The callable must accept exactly two parameters: the
+      input value and the name of an output variable.  The callable must set the
+      output variable to a boolean value in the calling scope.
+      The output variable is interpreted using standard CMake boolean evaluation.
+      If the callable does not set the output variable, it is an error.
+
+      Example:
+
+      .. code-block:: cmake
+
+        function(file_exists path result)
+          if(EXISTS "${path}")
+            set(${result} TRUE PARENT_SCOPE)
+          else()
+            set(${result} FALSE PARENT_SCOPE)
+          endif()
+        endfunction()
+
+        set(candidate_files main.c missing.c utils.c)
+        list(FILTER candidate_files INCLUDE PREDICATE file_exists)
 
 .. signature::
   list(INSERT <list> <element_index> <element> [<element> ...])
@@ -316,6 +355,39 @@
 
         list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
 
+    ``PREDICATE``
+      Specify a user-defined callable as a predicate.
+      Only elements for which the callable returns a true value will be
+      transformed.
+
+      .. code-block:: cmake
+
+        list(TRANSFORM <list> <ACTION> PREDICATE <function> ...)
+
+      .. versionadded:: 4.4
+
+      ``<function>`` is a user-defined :command:`function` with exactly
+      two formal parameters: the input value and the name of an output
+      variable.  The callable must set the output variable to a boolean
+      value.  Standard CMake boolean evaluation is used.
+      If the callable does not set the output variable, it is an error.
+
+      Example:
+
+      .. code-block:: cmake
+
+        function(is_relative path result)
+          if(NOT IS_ABSOLUTE "${path}")
+            set(${result} TRUE PARENT_SCOPE)
+          else()
+            set(${result} FALSE PARENT_SCOPE)
+          endif()
+        endfunction()
+
+        set(search_paths /usr/include src lib /opt/lib)
+        list(TRANSFORM search_paths PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/"
+             PREDICATE is_relative)
+
 
 Ordering
 ^^^^^^^^
diff --git a/Help/dev/README.rst b/Help/dev/README.rst
index 84766a6..050ec07 100644
--- a/Help/dev/README.rst
+++ b/Help/dev/README.rst
@@ -41,12 +41,14 @@
 * The `CMake Testing Guide`_.
 * The `CMake Experimental Features Guide`_.
 * The `CMake Debugging Guide`_.
+* The `CMake Diagnostics Guide`_.
 
 .. _`CMake Source Code Guide`: source.rst
 .. _`CMake Documentation Guide`: documentation.rst
 .. _`CMake Testing Guide`: testing.rst
 .. _`CMake Experimental Features Guide`: experimental.rst
 .. _`CMake Debugging Guide`: debug.rst
+.. _`CMake Diagnostics Guide`: diagnostics.rst
 
 Maintainer Documentation
 ========================
diff --git a/Help/dev/diagnostics.rst b/Help/dev/diagnostics.rst
new file mode 100644
index 0000000..394f4d6
--- /dev/null
+++ b/Help/dev/diagnostics.rst
@@ -0,0 +1,157 @@
+CMake Diagnostics Guide
+***********************
+
+The following is a guide on how CMake should issue diagnostics.
+See documentation on `CMake Development`_ for more information.
+
+.. _`CMake Development`: README.rst
+
+TL;DR Porting Guide (from CMake < 4.4)
+======================================
+
+I want to write ``IssueMessage(MessageType::AUTHOR_WARNING, ...``:
+  Write ``IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...`` instead.
+
+I want to write ``IssueMessage(MessageType::DEPRECATION_WARNING, ...``:
+  Write ``IssueDiagnostic(cmDiagnostics::CMD_DEPRECATED, ...`` instead.
+
+I want to write ``IssueMessage(MessageType::AUTHOR_ERROR, ...``:
+  No, you don't.  Under the old system, there isn't a meaningful difference
+  between ``AUTHOR_WARNING`` and ``AUTHOR_ERROR``; at least, not as arguments
+  to ``IssueMessage``.
+
+I want to write ``IssueMessage(MessageType::DEPRECATION_ERROR, ...``:
+  See ``AUTHOR_ERROR``.
+
+History
+=======
+
+Prior to CMake 4.4, CMake did not have a formal system for issuing diagnostics.
+The messaging system had message types for author diagnostics and deprecation
+diagnostics which conflated category and severity.  Worse, although these types
+*purported* to allow the caller to specify severity, this information is not
+actually honored.  Other diagnostics existed which did not use these types.
+
+More importantly, the mechanisms for managing severity differed depending on
+the diagnostic.  Variables to control a diagnostic might be documented,
+undocumented, or non-existent.  Spelling of command-line options to control
+diagnostics was inconsistent, as was the ability to make diagnostics fatal.
+Adding new categories required significant effort, as the logic to handle a
+diagnostic was bespoke at every layer.
+
+CMake Diagnostic Framework
+==========================
+
+CMake 4.4 introduced a structured framework for issuing and managing
+diagnostics.  The ``cmDiagnostics.h`` header enumerates all available
+diagnostic categories.  All code to manage diagnostics leverages either this
+table or helper code generated from the same, such that it is the *only*
+non-documentation location that needs to be updated to add a new diagnostic
+category.  The framework is also responsible for determining the severity of a
+diagnostic, based on the combination of category defaults and user preferences,
+and for presenting the user with a consistent set of control mechanisms.
+
+Numeric identifiers associated with diagnostic categories are only used
+internally and are never persisted. This means that numbers can be reassigned
+at any time. This also permits ensuring that numbers are consecutive, which
+allows iterating over all categories using a simple ``for`` loop.  (Note that
+such loops will typically start from ``1``, not ``0``, as ``0`` is reserved
+for the 'NONE' category which is used primarily as a virtual parent.)  Where
+necessary, macros provided by ``cmDiagnostics.h`` can generate code for each
+diagnostic category.
+
+Refer to ``Help/manual/cmake-diagnostics.7.rst`` for user-facing documentation.
+
+Issuing Diagnostics
+===================
+
+Diagnostics are the CMake equivalent of compiler warnings, with categorization
+providing both additional information and additional response control to users.
+
+Diagnostics are issued via the ``IssueDiagnostic`` method.  This method is
+provided on various objects, with ``cmMakefile`` being the most prominent, and
+typically exists alongside an ``IssueMessage`` method.  The latter is not
+deprecated, and should still be used to issue messages outside of the
+diagnostic categories, such as hard errors or informational messages.
+
+The historic ``{AUTHOR,DEPRECATION}_{WARNING,ERROR}`` message types, however,
+have been removed.  These are replaced by ``IssueDiagnostic`` and the
+``CMD_{AUTHOR,DEPRECATED}`` diagnostic categories.  Because the methods have
+similar signatures, porting in many cases can be accomplished by replacing the
+method name and first argument (the former message type, newly the diagnostic
+category).  For example, where prior versions of CMake wrote:
+
+.. code:: c++
+
+  mf->IssueMessage(MessageType::AUTHOR_WARNING, ...);
+
+CMake 4.4 and later should write:
+
+.. code:: c++
+
+  mf->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, ...);
+
+(Note that the ``"cmDiagnostics.h"`` header may need to be newly included.)
+
+As previously mentioned, the purported severity was ignored by ``cmMessenger``,
+which instead used its own internal state to decide whether a message should
+be a warning or an error.  This logic overrode the severity component of the
+message type as passed into the method.  As a result, although the new method
+does not accept a severity, this is actually consistent with the historic
+behavior and does not represent any loss of functionality.
+
+.. note::
+
+  When issuing diagnostics targeted at build-system authors, please consult the
+  complete list of available diagnostics, as a more specific category may be
+  available.  In some cases, it may even make sense to add a new category.
+
+Adding Diagnostic Categories
+============================
+
+Adding new diagnostic categories involves four steps.
+
+1.  The new category must be added to ``cmDiagnostics.h``.  The diagnostics
+    table looks like this:
+
+    .. code:: c++
+
+      #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT)      \
+        SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12)          \
+        SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1)     \
+        ...
+
+    Add a new entry.  All entries must begin with ``SELECT(ACTION,``.  The
+    second argument is the *default* state of the diagnostic and should
+    typically be ``Warn``, but may be ``Ignore`` if the diagnostic should be
+    opt-in.  Defaulting to ``SendError`` would be exceptional, and defaulting
+    to ``FatalError`` is not recommended under any circumstances.  The third
+    argument is the *parent* of the diagnostic (see below).  The fourth is the
+    actual name of the diagnostic.  The fifth and final argument is the CMake
+    preset version corresponding to the introduction of the argument, which
+    should be the latest preset version as used by the version of CMake under
+    development.
+
+    .. note::
+
+      In the event that the development version has not already introduced a
+      new preset version, adding a new diagnostic will additionally require
+      introducing a new preset version.
+
+    Diagnostics are hierarchical and **must** be listed in order of a
+    depth-first search.  (If you imagine the order of items in a typical tree
+    view which has been fully expanded, that is depth-first order.)  Each
+    diagnostic has a direct parent.  Recursive alterations to a diagnostic will
+    also affect all descendant diagnostics.
+
+2.  A documentation page for the new diagnostic must be created under
+    ``Help/diagnostic``, and a link to the same added to the Diagnostics
+    manual, ``Help/manual/cmake-diagnostics.7.rst``.
+
+3.  The new presets fields must be mentioned in the CMake presets version
+    history, found at the bottom of ``Help/manual/cmake-presets.7.rst``.  Note
+    that the documentation of the fields themselves is generated.
+
+4.  The ``Utilities/Scripts/regenerate-presets.py`` script must be run to
+    generate the updated presets schema and documentation.  Don't forget to
+    review and commit the changes made by this script.
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 30d53ff..61b77c0 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -37,7 +37,7 @@
 
 * The package name associated with specific targets may be specified
   using the ``CMAKE_EXPORT_FIND_PACKAGE_NAME`` variable and/or
-``EXPORT_FIND_PACKAGE_NAME`` target property.
+  ``EXPORT_FIND_PACKAGE_NAME`` target property.
 
 Export |CPS| Package Information for ``install(EXPORT)``
 ========================================================
@@ -102,7 +102,7 @@
 * Targets with the property set to a true value will have their C++ build
   information exported to the build database.
 
-Software Bill Of Materials |SBOM|
+Software Bill Of Materials (SBOM)
 =================================
 
 In order to activate support for the :command:`install(SBOM)` command,
diff --git a/Help/dev/source.rst b/Help/dev/source.rst
index ec4e12c..046be1e 100644
--- a/Help/dev/source.rst
+++ b/Help/dev/source.rst
@@ -287,10 +287,10 @@
 * ``<cmext/type_traits>``:
 
   * ``cm::is_pair``:
-   Checks if a type is a std::pair<> type.
+    Checks if a type is a std::pair<> type.
 
   * ``cm::is_tuple``:
-   Checks if a type is a std::tuple<> type.
+    Checks if a type is a std::tuple<> type.
 
   * ``cm::is_container``:
     Checks if a type is a container type.
diff --git a/Help/diagnostic/CMD_AUTHOR.rst b/Help/diagnostic/CMD_AUTHOR.rst
new file mode 100644
index 0000000..c309f31
--- /dev/null
+++ b/Help/diagnostic/CMD_AUTHOR.rst
@@ -0,0 +1,16 @@
+CMD_AUTHOR
+----------
+
+.. diagnostic::
+  :default: warn
+
+  Warn about a build system's incorrect use of CMake, or of a CMake interface
+  provided by a dependency.  This is the category triggered by
+  :command:`message(AUTHOR_WARNING)`.  It is also the ancestor of many other
+  diagnostic categories.
+
+  The most important aspect of this category is that it represents issues with
+  a project's build system which typically require alteration to the same.
+  This is to say that users simply trying to build a project obtained elsewhere
+  will typically not be interested in these warnings, except to perhaps report
+  them to the project's developer(s).
diff --git a/Help/diagnostic/CMD_DEPRECATED.rst b/Help/diagnostic/CMD_DEPRECATED.rst
new file mode 100644
index 0000000..dfb23d5
--- /dev/null
+++ b/Help/diagnostic/CMD_DEPRECATED.rst
@@ -0,0 +1,9 @@
+CMD_DEPRECATED
+--------------
+
+.. diagnostic::
+  :default: warn
+  :parent: CMD_AUTHOR
+
+  Warn about use of a deprecated function or package.  This is the category
+  triggered by :command:`message(DEPRECATION)`.
diff --git a/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst
new file mode 100644
index 0000000..b7c6e08
--- /dev/null
+++ b/Help/diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION.rst
@@ -0,0 +1,10 @@
+CMD_INSTALL_ABSOLUTE_DESTINATION
+--------------------------------
+
+.. diagnostic::
+  :default: ignore
+  :parent: CMD_AUTHOR
+
+  Warn when an :command:`install` command specifies an absolute ``DESTINATION``
+  path.  Absolute destinations are typically undesirable because they prevent
+  the installation prefix from being overridden at install time.
diff --git a/Help/diagnostic/CMD_UNINITIALIZED.rst b/Help/diagnostic/CMD_UNINITIALIZED.rst
new file mode 100644
index 0000000..8c2edc8
--- /dev/null
+++ b/Help/diagnostic/CMD_UNINITIALIZED.rst
@@ -0,0 +1,7 @@
+CMD_UNINITIALIZED
+-----------------
+
+.. diagnostic::
+  :default: ignore
+
+  Warn if an uninitialized variable is dereferenced.
diff --git a/Help/diagnostic/CMD_UNUSED_CLI.rst b/Help/diagnostic/CMD_UNUSED_CLI.rst
new file mode 100644
index 0000000..718598e
--- /dev/null
+++ b/Help/diagnostic/CMD_UNUSED_CLI.rst
@@ -0,0 +1,12 @@
+CMD_UNUSED_CLI
+--------------
+
+.. diagnostic::
+  :default: ignore
+
+  Warn about variables that are declared on the command line, but not used.
+
+  This diagnostic is issued outside of the configuration / generation phases.
+  Accordingly, while the action of this warning category can be queried as
+  usual, the :command:`cmake_diagnostic` command is unable to affect the action
+  of this diagnostic.
diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst
index f335e1f..809cf73 100644
--- a/Help/guide/ide-integration/index.rst
+++ b/Help/guide/ide-integration/index.rst
@@ -40,7 +40,7 @@
 user-specific counterpart, ``CMakeUserPresets.json``. This file contains
 information on the various configure presets that a user may want. Each preset
 may have a different compiler, build flags, etc. The details of this format are
-explained in the :manual:`cmake(1)` manual.
+explained in the :manual:`cmake-presets(7)` manual.
 
 IDE vendors are encouraged to read and evaluate this file the same way CMake
 does, and present the user with the presets listed in the file. Users should be
@@ -71,12 +71,12 @@
 of the platform to be exceeded, the IDE should instead construct a temporary
 cache script and pass it with the :option:`-C <cmake -C>` flag.
 
-While reading, parsing, and evaluating the contents of ``CMakePresets.json`` is
+While reading, parsing, and evaluating the contents of CMake presets files is
 straightforward, it is not trivial. In addition to the documentation, IDE
 vendors may also wish to refer to the CMake source code and test cases for a
 better understanding of how to implement the format.
 :download:`This file <../../manual/presets/schema.json>` provides a
-machine-readable JSON schema for the ``CMakePresets.json`` format that IDE
+machine-readable JSON schema for the CMake presets file format that IDE
 vendors may find useful for validation and providing editing assistance.
 
 Configuring
diff --git a/Help/guide/tutorial/Configuration and Cache Variables.rst b/Help/guide/tutorial/Configuration and Cache Variables.rst
index ec93090..7da09ed 100644
--- a/Help/guide/tutorial/Configuration and Cache Variables.rst
+++ b/Help/guide/tutorial/Configuration and Cache Variables.rst
@@ -506,6 +506,12 @@
   Command line flags can be mixed with presets. Command line flags have
   precedence over values found in a preset.
 
+.. note::
+  On CMake 4.4 and newer, CMake can also load presets from any file specified
+  with :option:`cmake --presets-file`. This can be useful when reusing settings
+  across multiple projects, since it avoids having to duplicate them in
+  separate ``CMakePresets.json`` files for each project.
+
 Presets also support limited macros, variables that can be brace-expanded
 inside the preset. The only one of interest to us is the ``${sourceDir}`` macro,
 which expands to the root directory of the project. We can use this to set our
@@ -557,7 +563,7 @@
 Build and Run
 -------------
 
-We can now use the preset file to manage our configuration.
+We can now use the presets file to manage our configuration.
 
 .. code-block:: console
 
diff --git a/Help/guide/user-interaction/index.rst b/Help/guide/user-interaction/index.rst
index f11b23d..12d6b84 100644
--- a/Help/guide/user-interaction/index.rst
+++ b/Help/guide/user-interaction/index.rst
@@ -425,7 +425,7 @@
 presets can set the build directory, generator, cache
 variables, environment variables, and other command-line
 options. All of these options can be overridden by the
-user. The full details of the ``CMakePresets.json`` format
+user. The full details of the CMake presets format
 are listed in the :manual:`cmake-presets(7)` manual.
 
 Using presets on the command-line
diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst
index 66a08d5..de2b705 100644
--- a/Help/manual/cmake-diagnostics.7.rst
+++ b/Help/manual/cmake-diagnostics.7.rst
@@ -32,59 +32,62 @@
 * Issues that may impact the ability of the project
   to be built with newer versions of CMake.
 
-Diagnostic Actions
-------------------
+Controlling Diagnostics
+=======================
 
-The action taken when a particular diagnostic is triggered depends on the
-diagnostic category.  Most categories will warn by default.  The
-:command:`cmake_diagnostic` command and :option:`-W <cmake -W>` options can be
-used to control what action occurs when a diagnostic of a particular category
-is triggered.  The possible actions are described in the documentation of the
-same.
+Each diagnostic category has an associated action to be taken when that
+diagnostic is triggered.  Most categories will warn by default.  The available
+actions are described in the :command:`cmake_diagnostic` command documentation.
+
+CMake maintains a diagnostic state stack that is similar to the policy state.
+The initial state of the stack is determined by four factors, which are, in
+order of precedence:
+
+* The default action associated with the diagnostic.
+
+* The action associated with the diagnostic stored in the CMake variable cache,
+  which is used to persist the initial state between CMake runs.
+
+* The :preset:`configurePresets.warnings` and :preset:`configurePresets.errors`
+  fields of :manual:`CMake Presets <cmake-presets(7)>`.
+
+* The :option:`-W[no-][error=] <cmake -W>` command line arguments.
+
+.. note::
+
+  Because command line arguments operate both recursively and in the order
+  specified, some combinations of diagnostic arguments may result in later
+  arguments completely overwriting the action of earlier arguments.  For
+  example, ``-Wno-child -Wparent`` will result in the ``child`` warning being
+  enabled, because ``-Wparent`` promotes both ``parent`` and ``child`` to at
+  least ``WARN`` severity.  CMake presets are evaluated in order from most
+  ancestral to least ancestral.
+
+During script execution, the :command:`cmake_diagnostic` command can be used to
+query or alter the state, or to perform limited stack manipulations.
+
+When a diagnostic is issued at configure time (or during script execution, when
+CMake is running in script mode), the current diagnostic state controls the
+action.  Diagnostics issued at generate time, or outside of the configuration
+and generation phases must make use of recorded state information. While CMake
+strives to preserve this information in a way that matches the recorded state
+to the state as of the CMake command which ultimately causes a diagnostic to be
+issued, CMake may sometimes fall back to the state when processing of a
+subdirectory completed, or even the root state.  This may limit the ability of
+the :command:`cmake_diagnostic` command to control such diagnostics, especially
+if called from a function or included file.  This is especially the case for
+diagnostics that are not directly coupled to a CMake command.
 
 Diagnostic Categories
 =====================
 
-The following categories are defined.
+The following categories are defined:
 
-``CMD_AUTHOR`` (``-Wauthor``)
------------------------------
+.. toctree::
+   :maxdepth: 1
 
-:Default: Warn
-
-Warn about a build system's incorrect use of CMake, or of a CMake interface
-provided by a dependency.  This is the category triggered by
-:command:`message(AUTHOR_WARNING)`.  It is also the ancestor of many other
-diagnostic categories.
-
-The most important aspect of this category is that it represents issues with
-a project's build system which typically require alteration to the same.  This
-is to say that users simply trying to build a project obtained elsewhere will
-typically not be interested in these warnings, except to perhaps report them
-to the project's developer(s).
-
-``CMD_DEPRECATED`` (``-Wdeprecated``)
--------------------------------------
-
-:Default: Warn
-:Parent: ``CMD_AUTHOR``
-
-Warn about use of a deprecated function or package.  This is the category
-triggered by :command:`message(DEPRECATION)`.
-
-``CMD_UNINITIALIZED`` (``-Wuninitialized``)
--------------------------------------------
-
-:Default: Ignore
-
-Warn if an uninitialized variable is dereferenced.
-
-``CMD_UNUSED_CLI`` (``-Wunused-cli``)
--------------------------------------
-
-:Default: Warn
-
-Warn about variables that are declared on the command line, but not used.
-
-Although the action of this warning category can be queried as usual, changes
-made using the :command:`cmake_diagnostic` command have no effect.
+   /diagnostic/CMD_AUTHOR
+   /diagnostic/CMD_DEPRECATED
+   /diagnostic/CMD_INSTALL_ABSOLUTE_DESTINATION
+   /diagnostic/CMD_UNINITIALIZED
+   /diagnostic/CMD_UNUSED_CLI
diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst
index 857d498..49b348f 100644
--- a/Help/manual/cmake-file-api.7.rst
+++ b/Help/manual/cmake-file-api.7.rst
@@ -1017,6 +1017,9 @@
 
     This field was added in codemodel version 2.4.
 
+    Since codemodel version 2.11, this field additionally accounts for
+    ``SOURCES`` file sets.
+
   ``fileSetDirectories``
     Optional member that is present when ``type`` is ``fileSet``. The value
     is a list of strings with the file set's base directories (determined by
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index f21586c..2ab7290 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -100,6 +100,8 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0217: The MACROS directory property does not exist anymore. </policy/CMP0217>
+   CMP0216: Swift targets have a default project name. </policy/CMP0216>
    CMP0215: Ninja generators emit Swift modules separately from compilation. </policy/CMP0215>
    CMP0214: Honor CMAKE_EXE_LINKER_FLAGS for Swift executable targets. </policy/CMP0214>
    CMP0213: file(ARCHIVE_{CREATE,EXTRACT}) encode archive paths as UTF-8 by default. </policy/CMP0213>
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index e135cdf..d5f7c98 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -1,4 +1,4 @@
-.. cmake-manual-description: CMakePresets.json
+.. cmake-manual-description: CMake Presets Reference
 
 cmake-presets(7)
 ****************
@@ -16,8 +16,12 @@
 for common ways to configure a project. This may be done to support CI builds,
 or for users who frequently use the same build. CMake supports two main files,
 ``CMakePresets.json`` and ``CMakeUserPresets.json``, that allow users to
-specify common configure options and share them with others. CMake also
-supports files included with the ``include`` field.
+specify common configure options and share them with others.
+
+.. presets-versionadded:: 4
+
+  CMake also supports files included with the :preset:`include` field.  See
+  `Includes`_ for more details.
 
 ``CMakePresets.json`` and ``CMakeUserPresets.json`` live in the project's root
 directory. They both have exactly the same format, and both are optional
@@ -31,6 +35,13 @@
 project is using Git, ``CMakePresets.json`` may be tracked, and
 ``CMakeUserPresets.json`` should be added to the ``.gitignore``.
 
+.. versionadded:: 4.4
+
+  CMake also supports specifying a file from which to read presets via the
+  :cmake-option:`--presets-file` option.  If this option is specified, neither
+  of ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+  present, and any presets defined in those files will be ignored/unavailable.
+
 Format
 ======
 
@@ -41,7 +52,7 @@
 
 .. presets-versionadded:: 10
 
-  Preset files may include comments using the key ``$comment`` at any level
+  Presets files may include comments using the key ``$comment`` at any level
   within the JSON object to provide documentation.
 
 The root object recognizes the following fields:
@@ -51,14 +62,15 @@
 Includes
 ^^^^^^^^
 
-``CMakePresets.json`` and ``CMakeUserPresets.json`` can include other files
-with the ``include`` field in file version ``4`` and later. Files included
-by these files can also include other files. If ``CMakePresets.json`` and
-``CMakeUserPresets.json`` are both present, ``CMakeUserPresets.json``
-implicitly includes ``CMakePresets.json``, even with no ``include`` field,
-in all versions of the format.
+.. presets-versionadded:: 4
 
-If a preset file contains presets that inherit from presets in another file,
+CMake presets files can include other files with the :preset:`include` field.
+Files included in this manner can also include other files.  If
+``CMakePresets.json`` and ``CMakeUserPresets.json`` are both present,
+``CMakeUserPresets.json`` implicitly includes ``CMakePresets.json``, even with
+no :preset:`include` field, in all versions of the format.
+
+If a presets file contains presets that inherit from presets in another file,
 the file must include the other file either directly or indirectly.
 Include cycles are not allowed among files. If ``a.json`` includes
 ``b.json``, ``b.json`` cannot include ``a.json``. However, a file may be
@@ -70,14 +82,14 @@
 
 .. presets-versionchanged:: 7
 
-  The ``include`` field supports `macro expansion`_, but only ``$penv{}`` macro
-  expansion.
+  The :preset:`include` field supports `macro expansion`_, but only ``$penv{}``
+  macro expansion.
 
 .. presets-versionchanged:: 9
 
-  The ``include`` field supports `macro expansion`_, except for ``$env{}`` and
-  preset-specific macros (i.e., those derived from the fields inside a preset's
-  definition like ``presetName``).
+  The :preset:`include` field supports `macro expansion`_, except for
+  ``$env{}`` and preset-specific macros (i.e., those derived from the fields
+  inside a preset's definition like ``presetName``).
 
 .. _`Configure Preset`:
 
@@ -268,12 +280,12 @@
 ``${fileDir}``
   .. presets-versionadded:: 4
 
-  Path to the directory containing the preset file which defines the preset
+  Path to the directory containing the presets file which defines the preset
   being used.
 
   .. presets-versionchanged:: 12
 
-    This macro *always* expands to the directory of the current preset file
+    This macro *always* expands to the directory of the current presets file
     containing the macro, regardless of the preset being used.
 
     For example, consider the following scenario.
@@ -296,7 +308,7 @@
     .. note::
 
       Since the ``${fileDir}`` macro in version 12 is expanded in the context
-      of the current preset file, it is the version of the current file, rather
+      of the current presets file, it is the version of the current file, rather
       than the version of the root file containing the preset being used, which
       enables this alternative behavior.
 
@@ -351,9 +363,8 @@
 Versions
 ========
 
-The JSON schema of ``CMakePresets.json`` and ``CMakeUserPresets.json``
-follows a version scheme where new versions are added and allowed in newer
-versions of CMake.
+The JSON schema of CMake presets files follows a version scheme where new
+versions are added and allowed in newer versions of CMake.
 
 A list of the supported versions along with the version of CMake in which
 they were added and a summary of the new features and changes is given below.
@@ -431,7 +442,7 @@
 
     * Changes to `Includes`_
 
-      * The ``include`` field now supports ``$penv{}`` `macro expansion`_.
+      * The :preset:`include` field now supports ``$penv{}`` `macro expansion`_.
 
   ``8``
     .. versionadded:: 3.28
@@ -443,7 +454,8 @@
 
     * Changes to `Includes`_
 
-      * The ``include`` field now supports other types of `macro expansion`_.
+      * The :preset:`include` field now supports other types of
+        `macro expansion`_.
 
   ``10``
     .. versionadded:: 3.31
@@ -474,10 +486,13 @@
       * The ``uninitialized`` and ``unusedCli`` fields were added to
         :preset:`configurePresets.errors`.
 
+      * The ``installAbsoluteDestination`` field was added to
+        :preset:`configurePresets.warnings` and :preset:`configurePresets.errors`.
+
     * Changes to `Macro Expansion`_
 
       * The `${fileDir} <CMakePresets fileDir_>`_ macro now always expands to
-        the directory of preset file containing the ``${fileDir}`` macro,
+        the directory of presets file containing the ``${fileDir}`` macro,
         regardless of whether it is inherited by another preset in a different
         directory.
 
@@ -490,4 +505,4 @@
 ======
 
 :download:`This file </manual/presets/schema.json>` provides a machine-readable
-JSON schema for the ``CMakePresets.json`` format.
+JSON schema for the CMake presets file format.
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 4ec3b6e..aec1892 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -84,7 +84,6 @@
    /prop_dir/LINK_DIRECTORIES
    /prop_dir/LINK_OPTIONS
    /prop_dir/LISTFILE_STACK
-   /prop_dir/MACROS
    /prop_dir/PARENT_DIRECTORY
    /prop_dir/RULE_LAUNCH_COMPILE
    /prop_dir/RULE_LAUNCH_CUSTOM
@@ -427,6 +426,7 @@
    /prop_tgt/Swift_LANGUAGE_VERSION
    /prop_tgt/Swift_MODULE_DIRECTORY
    /prop_tgt/Swift_MODULE_NAME
+   /prop_tgt/Swift_PACKAGE_NAME
    /prop_tgt/Swift_SEPARATE_MODULE_EMISSION
    /prop_tgt/SYMBOLIC
    /prop_tgt/SYSTEM
@@ -545,6 +545,7 @@
    /prop_fs/COMPILE_DEFINITIONS
    /prop_fs/COMPILE_OPTIONS
    /prop_fs/CXX_SCAN_FOR_MODULES
+   /prop_fs/JOB_POOL_COMPILE
    /prop_fs/INCLUDE_DIRECTORIES
    /prop_fs/INDEPENDENT_FILES
    /prop_fs/INTERFACE_COMPILE_DEFINITIONS
@@ -552,6 +553,7 @@
    /prop_fs/INTERFACE_INCLUDE_DIRECTORIES
    /prop_fs/INTERFACE_SOURCES
    /prop_fs/SCOPE
+   /prop_fs/SKIP_LINTING
    /prop_fs/SOURCES
    /prop_fs/TYPE
 
@@ -701,6 +703,7 @@
    /prop_dir/COMPILE_DEFINITIONS_CONFIG
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION
    /prop_dir/INTERPROCEDURAL_OPTIMIZATION_CONFIG
+   /prop_dir/MACROS
    /prop_dir/TEST_INCLUDE_FILE
 
 
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index a2ddcef..71318dd 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -806,6 +806,7 @@
    /variable/CTEST_SVN_UPDATE_OPTIONS
    /variable/CTEST_TEST_LOAD
    /variable/CTEST_TEST_TIMEOUT
+   /variable/CTEST_TEST_COVERAGE_TOOL
    /variable/CTEST_TLS_VERIFY
    /variable/CTEST_TLS_VERSION
    /variable/CTEST_UPDATE_COMMAND
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index eef6f6d..7b7680f 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -686,12 +686,15 @@
 
 .. option:: --preset <preset>, --preset=<preset>
 
- Reads a :manual:`preset <cmake-presets(7)>` from ``CMakePresets.json`` and
- ``CMakeUserPresets.json`` files, which must be located in the same directory
- as the top level ``CMakeLists.txt`` file. The preset may specify the
- generator, the build directory, a list of variables, and other arguments to
- pass to CMake. At least one of ``CMakePresets.json`` or
- ``CMakeUserPresets.json`` must be present.
+ Reads a :manual:`preset <cmake-presets(7)>` from CMake presets files.
+ ``CMakePresets.json`` and ``CMakeUserPresets.json`` are the default files,
+ which must be located in the same directory as the top level
+ ``CMakeLists.txt`` file.  A file can also be specified with
+ :cmake-option:`--presets-file`.
+
+ The preset may specify the generator, the build directory, a list of
+ variables, and other arguments to pass to CMake.
+
  The :manual:`CMake GUI <cmake-gui(1)>` also recognizes and supports
  ``CMakePresets.json`` and ``CMakeUserPresets.json`` files. For full details
  on these files, see :manual:`cmake-presets(7)`.
@@ -699,25 +702,49 @@
  The presets are read before all other command line options, although the
  :cmake-option:`-S` option can be used to specify the source directory
  containing the ``CMakePresets.json`` and ``CMakeUserPresets.json`` files.
- If :cmake-option:`-S` is not given, the current directory is assumed to
- be the top level source directory and must contain the presets files. The
- options specified by the chosen preset (variables, generator, etc.) can all
- be overridden by manually specifying them on the command line. For example,
- if the preset sets a variable called ``MYVAR`` to ``1``, but the user sets
- it to ``2`` with a ``-D`` argument, the value ``2`` is preferred.
+ If neither :cmake-option:`-S` nor :cmake-option:`--presets-file` are given,
+ the current working directory is assumed to be the top level source directory
+ and must contain ``CMakePresets.json`` and/or ``CMakeUserPresets.json``.
+
+ The options specified by the chosen preset (variables, generator, etc.)
+ can all be overridden by manually specifying them on the command line.
+ For example, if the preset sets a variable called ``MYVAR`` to ``1``,
+ but the user sets it to ``2`` with a :cmake-option:`-D` argument,
+ the value ``2`` is preferred.
 
  .. versionadded:: 3.21
-   The ``-B`` option may optionally be specified with a different binary
-   directory than the one specified by the ``binaryDir`` key of the
-   configure preset.
+   The :cmake-option:`-B` option may optionally be specified with a different
+   binary directory than the one specified by the
+   :preset:`configurePresets.binaryDir` field.
+
+ .. versionchanged:: 4.4
+   If :cmake-option:`--presets-file` is specified, neither of
+   ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+   present.  In prior versions, the presence of these files in the top-level
+   source directory (whether via :cmake-option:`-S` or the current working
+   directory) was strictly required.
+
+.. option:: --presets-file <file>, --presets-file=<file>
+
+ .. versionadded:: 4.4
+
+ Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The
+ specified path may be absolute or relative to the current working directory.
+ If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and
+ ``CMakeUserPresets.json`` will be ignored.
 
 .. option:: --list-presets[=<type>]
 
  Lists the available presets of the specified ``<type>``.  Valid values for
  ``<type>`` are ``configure``, ``build``, ``test``, ``package``, or ``all``.
- If ``<type>`` is omitted, ``configure`` is assumed.  The current working
- directory must contain CMake preset files unless the :cmake-option:`-S`
- option is used to specify a different top level source directory.
+ If ``<type>`` is omitted, ``configure`` is assumed.
+
+ .. versionchanged:: 4.4
+   If :cmake-option:`--presets-file` is specified, the presets defined in the
+   given ``<file>`` will be listed.  Otherwise, the top-level source directory
+   (whether via :cmake-option:`-S` or the current working directory) must
+   contain ``CMakePresets.json`` and/or ``CMakeUserPresets.json``.
+   In prior versions, the latter was strictly required.
 
 .. option:: --debugger
 
@@ -793,9 +820,10 @@
 
 .. option:: --preset <preset>, --preset=<preset>
 
-  Use a build preset to specify build options. The project binary directory
-  is inferred from the ``configurePreset`` key unless a directory is specified
-  after ``--build``.  See :manual:`preset <cmake-presets(7)>` for more details.
+  Use a build :manual:`preset <cmake-presets(7)>` to specify build options.
+  The project binary directory is inferred from the
+  :preset:`buildPresets.configurePreset` key unless a directory is specified
+  after ``--build``.
 
   .. versionadded:: 4.3
     ``cmake --build`` now supports specifying a build directory and
@@ -803,7 +831,19 @@
 
   .. versionchanged:: 4.4
     ``cmake --build <dir> --preset`` no longer needs to be called from the
-    directory containing the CMake presets files.
+    directory containing ``CMakePresets.json`` or ``CMakeUserPresets.json``.
+    If :cmake-build-option:`--presets-file` is specified, CMake will use that
+    file; otherwise, the presets file(s) can be inferred from the current
+    build directory's ``CMakeCache.txt``.
+
+.. option:: --presets-file <file>, --presets-file=<file>
+
+  .. versionadded:: 4.4
+
+  Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The
+  specified path may be absolute or relative to the current working directory.
+  If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and
+  ``CMakeUserPresets.json`` will be ignored.
 
 .. option:: --list-presets
 
@@ -811,7 +851,10 @@
 
   .. versionchanged:: 4.4
     ``cmake --build <dir> --list-presets`` no longer needs to be called from
-    the directory containing the CMake presets files.
+    the directory containing ``CMakePresets.json`` or ``CMakeUserPresets.json``.
+    If :cmake-build-option:`--presets-file` is specified, only presets defined
+    in the given ``<file>`` will be listed; otherwise, the presets file(s) are
+    inferred from the current build directory's ``CMakeCache.txt``.
 
 .. option:: -j [<jobs>], --parallel [<jobs>]
 
@@ -1909,10 +1952,8 @@
 
 .. option:: --preset <preset>, --preset=<preset>
 
-  Use a workflow preset to specify a workflow. The project binary directory
-  is inferred from the initial configure preset. The current working directory
-  must contain CMake preset files.
-  See :manual:`preset <cmake-presets(7)>` for more details.
+  Use a workflow :manual:`preset <cmake-presets(7)>` to specify a workflow.
+  The project binary directory is inferred from the initial configure preset.
 
   .. versionchanged:: 3.31
     When following immediately after the ``--workflow`` option,
@@ -1923,10 +1964,31 @@
 
       $ cmake --workflow my-preset
 
+  .. versionchanged:: 4.4
+    If :cmake-workflow-option:`--presets-file` is specified, neither of
+    ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+    present.  Otherwise, they are required to be present in the top level
+    source directory.  In prior versions, this was strictly required.
+
+.. option:: --presets-file <file>, --presets-file=<file>
+
+  .. versionadded:: 4.4
+
+  Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The
+  specified path may be absolute or relative to the current working directory.
+  If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and
+  ``CMakeUserPresets.json`` will be ignored.
+
 .. option:: --list-presets
 
-  Lists the available workflow presets. The current working directory must
-  contain CMake preset files.
+  Lists the available workflow presets.
+
+  .. versionchanged:: 4.4
+    If :cmake-workflow-option:`--presets-file` is specified, neither of
+    ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+    present, and only presets defined in the given ``<file>`` will be listed.
+    Otherwise, they are required to be present in the top level source
+    directory.  In prior versions, this was strictly required.
 
 .. option:: --fresh
 
diff --git a/Help/manual/cpack.1.rst b/Help/manual/cpack.1.rst
index b6ac331..686d153 100644
--- a/Help/manual/cpack.1.rst
+++ b/Help/manual/cpack.1.rst
@@ -120,13 +120,37 @@
 
   Override/define :variable:`CPACK_PACKAGE_VENDOR`.
 
-.. option:: --preset <presetName>
+.. option:: --preset <preset>, --preset=<preset>
 
-  Use a preset from :manual:`cmake-presets(7)`.
+  Use a package :manual:`preset <cmake-presets(7)>` to specify package
+  options. The project binary directory is inferred from the
+  :preset:`packagePresets.configurePreset` key.
+
+  .. versionchanged:: 4.4
+    If :cpack-option:`--presets-file` is specified, neither of
+    ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+    present.  Otherwise, they are required to be present in the top level
+    source directory.  In prior versions, this was strictly required.
+
+.. option:: --presets-file <file>, --presets-file=<file>
+
+  .. versionadded:: 4.4
+
+  Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The
+  specified path may be absolute or relative to the current working directory.
+  If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and
+  ``CMakeUserPresets.json`` will be ignored.
 
 .. option:: --list-presets
 
-  List presets from :manual:`cmake-presets(7)`.
+  Lists the available package presets.
+
+  .. versionchanged:: 4.4
+    If :cpack-option:`--presets-file` is specified, neither of
+    ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+    present, and only presets defined in the given ``<file>`` will be listed.
+    Otherwise, they are required to be present in the top level source
+    directory.  In prior versions, this was strictly required.
 
 .. include:: include/OPTIONS_HELP.rst
 
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index dba20c1..4ab9d75 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -69,20 +69,41 @@
 
 .. option:: --preset <preset>, --preset=<preset>
 
- Use a test preset to specify test options. The project binary directory
- is inferred from the ``configurePreset`` key. The current working directory
- must contain CMake preset files.
- See :manual:`preset <cmake-presets(7)>` for more details.
+ Use a test :manual:`preset <cmake-presets(7)>` to specify test options. The
+ project binary directory is inferred from the
+ :preset:`testPresets.configurePreset` key.
 
  .. versionadded:: 3.30
-   The ``--test-dir`` option may optionally be specified with a different
-   binary directory than the one specified by the ``configurePreset`` key
-   of the test preset.
+   The :ctest-option:`--test-dir` option may be specified with a different
+   binary directory than the one specified by the
+   :preset:`testPresets.configurePreset` key.
+
+ .. versionchanged:: 4.4
+   If :ctest-option:`--presets-file` is specified, neither of
+   ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+   present.  Otherwise, they are required to be present in the top level
+    source directory.  In prior versions, this was strictly required.
+
+.. option:: --presets-file <file>, --presets-file=<file>
+
+ .. versionadded:: 4.4
+
+ Reads :manual:`presets <cmake-presets(7)>` from the given ``<file>``. The
+ specified path may be absolute or relative to the current working directory.
+ If ``--presets-file`` is given, presets defined in ``CMakePresets.json`` and
+ ``CMakeUserPresets.json`` will be ignored.
 
 .. option:: --list-presets
 
  Lists the available test presets. The current working directory must contain
- CMake preset files.
+ ``CMakePresets.json`` and/or ``CMakeUserPresets.json``.
+
+ .. versionchanged:: 4.4
+   If :ctest-option:`--presets-file` is specified, neither of
+   ``CMakePresets.json`` nor ``CMakeUserPresets.json`` are required to be
+   present, and only presets defined in the given ``<file>`` will be listed.
+   Otherwise, they are required to be present in the top level source
+   directory.  In prior versions, this was strictly required.
 
 .. option:: -C <cfg>, --build-config <cfg>
 
@@ -1362,8 +1383,41 @@
   * `CTest Script`_ variable: :variable:`CTEST_TEST_TIMEOUT`
   * :module:`CTest` module variable: ``DART_TESTING_TIMEOUT``
 
+.. _`ctest-CoverageTool`:
+
+``CoverageTool``
+  .. versionadded:: 4.4
+
+  Specify the tool used for collecting coverage during the running
+  of the tests.  The tool may be one of:
+
+  ``LLVM-COV``
+    This value indicates the usage of Clang's source-based code coverage
+    which "operates on AST and preprocessor information directly".
+    For more information, see the `Clang Source-based Code Coverage`_
+    documentation.
+
+    CTest will run each test with the ``LLVM_PROFILE_FILE`` environment
+    variable set to ``<directory>/<test-name>_<process-id>.profraw``,
+    where ``<directory>`` is the absolute path to the directory where
+    the ``CTestTestfile.cmake`` that describes the test is located.
+    This configures Clang Source-based Code Coverage to uniquely identify
+    the coverage information gathered by each test without any collisions.
+
+    Any existing ``<directory>/<test-name>_*.profraw`` files for a test
+    are removed before running the test.  Any existing ``LLVM_PROFILE_FILE``
+    environment variable is ignored.
+
+  If no value is specified, no work is done and nothing is added to the
+  environment.
+
+  * `CTest Script`_ variable: :variable:`CTEST_TEST_COVERAGE_TOOL`
+  * :module:`CTest` module variable: :variable:`CTEST_TEST_COVERAGE_TOOL`
+
 To report extra test values to CDash, see :ref:`Additional Test Measurements`.
 
+.. _`Clang Source-based Code Coverage`: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+
 .. _`CTest Coverage Step`:
 
 CTest Coverage Step
diff --git a/Help/manual/file_api/schema_target.json b/Help/manual/file_api/schema_target.json
index 68cb14e..925c259 100644
--- a/Help/manual/file_api/schema_target.json
+++ b/Help/manual/file_api/schema_target.json
@@ -34,6 +34,9 @@
     },
     {
       "$ref": "#/definitions/codemodelV2_10"
+    },
+    {
+      "$ref": "#/definitions/codemodelV2_11"
     }
   ],
   "definitions": {
@@ -68,7 +71,25 @@
         },
         "minor": {
           "type": "integer",
-          "minimum": 10
+          "const": 10
+        }
+      },
+      "additionalProperties": false
+    },
+    "codemodelVersionV2_11": {
+      "type": "object",
+      "required": [
+        "major",
+        "minor"
+      ],
+      "properties": {
+        "major": {
+          "type": "integer",
+          "const": 2
+        },
+        "minor": {
+          "type": "integer",
+          "minimum": 11
         }
       },
       "additionalProperties": false
@@ -81,7 +102,22 @@
       "type": "string",
       "description": "Unique identifier for the target. The format is unspecified and should not be interpreted by clients."
     },
-    "typeV2_0": {
+    "fileset-typeV2_5": {
+      "type": "string",
+      "enum": [
+        "HEADERS",
+        "CXX_MODULES"
+      ]
+    },
+    "fileset-typeV2_11": {
+      "type": "string",
+      "enum": [
+        "HEADERS",
+        "CXX_MODULES",
+        "SOURCES"
+      ]
+    },
+    "target-typeV2_0": {
       "type": "string",
       "enum": [
         "EXECUTABLE",
@@ -93,7 +129,7 @@
         "UTILITY"
       ]
     },
-    "typeV2_9": {
+    "target-typeV2_9": {
       "type": "string",
       "enum": [
         "EXECUTABLE",
@@ -530,12 +566,45 @@
             "type": "string"
           },
           "type": {
+            "$ref": "#/definitions/fileset-typeV2_5"
+          },
+          "visibility": {
             "type": "string",
             "enum": [
-              "HEADERS",
-              "CXX_MODULES"
+              "PUBLIC",
+              "PRIVATE",
+              "INTERFACE"
             ]
           },
+          "baseDirectories": {
+            "type": "array",
+            "minItems": 1,
+            "items": {
+              "type": "string"
+            },
+            "description": "Base directories containing the file set's files. If a directory is inside the top level source directory, its path is specified relative to that directory. Otherwise, its path is absolute."
+          }
+        },
+        "additionalProperties": false
+      }
+    },
+    "fileSetsV2_11": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "required": [
+          "name",
+          "type",
+          "visibility",
+          "baseDirectories"
+        ],
+        "properties": {
+          "name": {
+            "type": "string"
+          },
+          "type": {
+            "$ref": "#/definitions/fileset-typeV2_11"
+          },
           "visibility": {
             "type": "string",
             "enum": [
@@ -1094,7 +1163,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1158,7 +1227,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1222,7 +1291,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1286,7 +1355,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1353,7 +1422,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1420,7 +1489,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1490,7 +1559,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_0"
+          "$ref": "#/definitions/target-typeV2_0"
         },
         "backtrace": {
           "$ref": "#/definitions/backtrace"
@@ -1567,7 +1636,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_9"
+          "$ref": "#/definitions/target-typeV2_9"
         },
         "imported": {
           "$ref": "#/definitions/importedV2_9"
@@ -1674,7 +1743,7 @@
           "$ref": "#/definitions/id"
         },
         "type": {
-          "$ref": "#/definitions/typeV2_9"
+          "$ref": "#/definitions/target-typeV2_9"
         },
         "imported": {
           "$ref": "#/definitions/importedV2_9"
@@ -1762,6 +1831,116 @@
         }
       },
       "additionalProperties": false
+    },
+    "codemodelV2_11": {
+      "required": [
+        "codemodelVersion",
+        "name",
+        "id",
+        "type",
+        "paths",
+        "sources",
+        "backtraceGraph"
+      ],
+      "properties": {
+        "codemodelVersion": {
+          "$ref": "#/definitions/codemodelVersionV2_11"
+        },
+        "name": {
+          "$ref": "#/definitions/name"
+        },
+        "id": {
+          "$ref": "#/definitions/id"
+        },
+        "type": {
+          "$ref": "#/definitions/target-typeV2_9"
+        },
+        "imported": {
+          "$ref": "#/definitions/importedV2_9"
+        },
+        "local": {
+          "$ref": "#/definitions/localV2_9"
+        },
+        "abstract": {
+          "$ref": "#/definitions/abstractV2_9"
+        },
+        "backtrace": {
+          "$ref": "#/definitions/backtrace"
+        },
+        "folder": {
+          "$ref": "#/definitions/folder"
+        },
+        "paths": {
+          "$ref": "#/definitions/paths"
+        },
+        "nameOnDisk": {
+          "$ref": "#/definitions/nameOnDisk"
+        },
+        "artifacts": {
+          "$ref": "#/definitions/artifacts"
+        },
+        "isGeneratorProvided": {
+          "$ref": "#/definitions/isGeneratorProvided"
+        },
+        "install": {
+          "$ref": "#/definitions/install"
+        },
+        "launchers": {
+          "$ref": "#/definitions/launchersV2_7"
+        },
+        "link": {
+          "$ref": "#/definitions/link"
+        },
+        "archive": {
+          "$ref": "#/definitions/archive"
+        },
+        "debugger": {
+          "$ref": "#/definitions/debuggerV2_8"
+        },
+        "dependencies": {
+          "$ref": "#/definitions/dependencies"
+        },
+        "linkLibraries": {
+          "$ref": "#/definitions/linkLibrariesV2_9"
+        },
+        "interfaceLinkLibraries": {
+          "$ref": "#/definitions/interfaceLinkLibrariesV2_9"
+        },
+        "compileDependencies": {
+          "$ref": "#/definitions/compileDependenciesV2_9"
+        },
+        "interfaceCompileDependencies": {
+          "$ref": "#/definitions/interfaceCompileDependenciesV2_9"
+        },
+        "objectDependencies": {
+          "$ref": "#/definitions/onlyTargetDependenciesArrayV2_9"
+        },
+        "orderDependencies": {
+          "$ref": "#/definitions/onlyTargetDependenciesArrayV2_9"
+        },
+        "fileSets": {
+          "$ref": "#/definitions/fileSetsV2_11"
+        },
+        "sources": {
+          "$ref": "#/definitions/sourcesV2_5"
+        },
+        "interfaceSources": {
+          "$ref": "#/definitions/interfaceSourcesV2_10"
+        },
+        "symbolic": {
+          "$ref": "#/definitions/symbolicV2_9"
+        },
+        "sourceGroups": {
+          "$ref": "#/definitions/sourceGroupsV2_10"
+        },
+        "compileGroups": {
+          "$ref": "#/definitions/compileGroupsV2_6"
+        },
+        "backtraceGraph": {
+          "$ref": "#/definitions/backtraceGraph"
+        }
+      },
+      "additionalProperties": false
     }
   }
 }
diff --git a/Help/manual/presets/buildPresets-properties.rst b/Help/manual/presets/buildPresets-properties.rst
index 842ee40..1256280 100644
--- a/Help/manual/presets/buildPresets-properties.rst
+++ b/Help/manual/presets/buildPresets-properties.rst
@@ -18,9 +18,10 @@
   An optional boolean specifying whether or not a preset should be
   hidden. If a preset is hidden, it cannot be used in the
   :cmake-build-option:`--preset` argument and does not have to have a
-  valid ``configurePreset``, even from inheritance. ``hidden``
-  presets are intended to be used as a base for other presets to
-  inherit via the ``inherits`` field.
+  valid :preset:`buildPresets.configurePreset`, even from
+  inheritance. ``hidden`` presets are intended to be used as a base
+  for other presets to inherit via the
+  :preset:`buildPresets.inherits` field.
 
 .. _`CMakePresets.buildPresets.inherits`:
 
@@ -136,7 +137,7 @@
   .. versionchanged:: 4.3
 
     This field does not accept negative integer values, regardless of
-    the version in the preset file.
+    the version in the presets file.
 
 .. _`CMakePresets.buildPresets.targets`:
 
diff --git a/Help/manual/presets/errors-properties.rst b/Help/manual/presets/errors-properties.rst
index 3f1216f..7bf5dac 100644
--- a/Help/manual/presets/errors-properties.rst
+++ b/Help/manual/presets/errors-properties.rst
@@ -32,6 +32,15 @@
   This may not be set to ``true``
   if ``warnings.dev`` is set to ``false``.
 
+.. _`CMakePresets.configurePresets.errors.installAbsoluteDestination`:
+
+``installAbsoluteDestination``
+  .. presets-versionadded:: 12
+
+  An optional boolean. Equivalent to passing :cmake-option:`-Werror=install-absolute-destination` or
+  :cmake-option:`-Wno-error=install-absolute-destination` on the command line.
+  This may not be set to ``true`` if ``warnings.installAbsoluteDestination`` is set to ``false``.
+
 .. _`CMakePresets.configurePresets.errors.uninitialized`:
 
 ``uninitialized``
diff --git a/Help/manual/presets/execution-properties.rst b/Help/manual/presets/execution-properties.rst
index ebe80fc..8e3d4a0 100644
--- a/Help/manual/presets/execution-properties.rst
+++ b/Help/manual/presets/execution-properties.rst
@@ -29,7 +29,7 @@
   .. versionchanged:: 4.3
 
     This field does not accept negative integer values,
-    regardless of the version in the preset file.
+    regardless of the version in the presets file.
 
 .. _`CMakePresets.testPresets.execution.resourceSpecFile`:
 
diff --git a/Help/manual/presets/packagePresets-properties.rst b/Help/manual/presets/packagePresets-properties.rst
index 2c21b53..863500f 100644
--- a/Help/manual/presets/packagePresets-properties.rst
+++ b/Help/manual/presets/packagePresets-properties.rst
@@ -17,9 +17,10 @@
   An optional boolean specifying whether or not a preset should be
   hidden. If a preset is hidden, it cannot be used in the
   :cpack-option:`--preset` argument and does not have to have a valid
-  ``configurePreset``, even from inheritance. ``hidden`` presets are
-  intended to be used as a base for other presets to inherit via the
-  ``inherits`` field.
+  :preset:`packagePresets.configurePreset`, even from
+  inheritance. ``hidden`` presets are intended to be used as a base
+  for other presets to inherit via the
+  :preset:`packagePresets.inherits` field.
 
 .. _`CMakePresets.packagePresets.inherits`:
 
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index 70f5024..128c829 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -1286,6 +1286,9 @@
         "deprecated": {
           "$ref": "#/definitions/configurePresets.warnings.deprecated@v1.."
         },
+        "installAbsoluteDestination": {
+          "$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12"
+        },
         "uninitialized": {
           "$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.."
         },
@@ -1312,6 +1315,10 @@
       "type": "boolean",
       "description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true."
     },
+    "configurePresets.warnings.installAbsoluteDestination@v12": {
+      "type": "boolean",
+      "description": "An optional boolean. Equivalent to passing -Winstall-absolute-destination or -Wno-install-absolute-destination on the command line. This may not be set to false if errors.installAbsoluteDestination is set to true."
+    },
     "configurePresets.warnings.uninitialized@v1..": {
       "type": "boolean",
       "description": "An optional boolean. Equivalent to passing -Wuninitialized or -Wno-uninitialized on the command line. This may not be set to false if errors.uninitialized is set to true."
@@ -1364,6 +1371,9 @@
         "deprecated": {
           "$ref": "#/definitions/configurePresets.errors.deprecated@v1.."
         },
+        "installAbsoluteDestination": {
+          "$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12"
+        },
         "uninitialized": {
           "$ref": "#/definitions/configurePresets.errors.uninitialized@v12"
         },
@@ -1387,6 +1397,10 @@
       "type": "boolean",
       "description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false."
     },
+    "configurePresets.errors.installAbsoluteDestination@v12": {
+      "type": "boolean",
+      "description": "An optional boolean. Equivalent to passing -Werror=install-absolute-destination or -Wno-error=install-absolute-destination on the command line. This may not be set to true if warnings.installAbsoluteDestination is set to false."
+    },
     "configurePresets.errors.uninitialized@v12": {
       "type": "boolean",
       "description": "An optional boolean. Equivalent to passing -Werror=uninitialized or -Wno-error=uninitialized on the command line. This may not be set to true if warnings.uninitialized is set to false."
@@ -1842,6 +1856,9 @@
     "buildPresets.environment@v2..": {
       "type": "object",
       "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.",
+      "propertyNames": {
+        "pattern": "^.+$"
+      },
       "properties": {},
       "additionalProperties": {
         "anyOf": [
diff --git a/Help/manual/presets/schema.yaml b/Help/manual/presets/schema.yaml
index fe56d73..7650fa4 100644
--- a/Help/manual/presets/schema.yaml
+++ b/Help/manual/presets/schema.yaml
@@ -963,9 +963,10 @@
             An optional boolean specifying whether or not a preset should be
             hidden. If a preset is hidden, it cannot be used in the
             :cmake-build-option:`--preset` argument and does not have to have a
-            valid ``configurePreset``, even from inheritance. ``hidden``
-            presets are intended to be used as a base for other presets to
-            inherit via the ``inherits`` field.
+            valid :preset:`buildPresets.configurePreset`, even from
+            inheritance. ``hidden`` presets are intended to be used as a base
+            for other presets to inherit via the
+            :preset:`buildPresets.inherits` field.
         inherits: *preset-inherits
         condition: *condition
         vendor: *preset-vendor
@@ -988,6 +989,8 @@
             ExternalProject may use a different (system default) compiler than
             the top-level CMake project.
           properties: {}
+          propertyNames:
+            pattern: '^.+$'
           additionalProperties: *environment-additional-properties
         configurePreset:
           type: string
@@ -1033,7 +1036,7 @@
             .. versionchanged:: 4.3
 
               This field does not accept negative integer values, regardless of
-              the version in the preset file.
+              the version in the presets file.
           minimum: 0
         targets:
           anyOf:
@@ -1181,9 +1184,10 @@
             An optional boolean specifying whether or not a preset should be
             hidden. If a preset is hidden, it cannot be used in the
             :ctest-option:`--preset` argument and does not have to have a valid
-            ``configurePreset``, even from inheritance. ``hidden`` presets are
-            intended to be used as a base for other presets to inherit via the
-            ``inherits`` field.
+            :preset:`testPresets.configurePreset`, even from
+            inheritance. ``hidden`` presets are intended to be used as a base
+            for other presets to inherit via the
+            :preset:`testPresets.inherits` field.
         inherits: *preset-inherits
         condition: *condition
         vendor: *preset-vendor
@@ -1629,7 +1633,7 @@
                 .. versionchanged:: 4.3
 
                   This field does not accept negative integer values,
-                  regardless of the version in the preset file.
+                  regardless of the version in the presets file.
               minimum: 0
             resourceSpecFile:
               type: string
@@ -1819,9 +1823,10 @@
             An optional boolean specifying whether or not a preset should be
             hidden. If a preset is hidden, it cannot be used in the
             :cpack-option:`--preset` argument and does not have to have a valid
-            ``configurePreset``, even from inheritance. ``hidden`` presets are
-            intended to be used as a base for other presets to inherit via the
-            ``inherits`` field.
+            :preset:`packagePresets.configurePreset`, even from
+            inheritance. ``hidden`` presets are intended to be used as a base
+            for other presets to inherit via the
+            :preset:`packagePresets.inherits` field.
         inherits: *preset-inherits
         condition: *condition
         vendor: *preset-vendor
diff --git a/Help/manual/presets/testPresets-properties.rst b/Help/manual/presets/testPresets-properties.rst
index 293d88f..6e71ab4 100644
--- a/Help/manual/presets/testPresets-properties.rst
+++ b/Help/manual/presets/testPresets-properties.rst
@@ -17,9 +17,10 @@
   An optional boolean specifying whether or not a preset should be
   hidden. If a preset is hidden, it cannot be used in the
   :ctest-option:`--preset` argument and does not have to have a valid
-  ``configurePreset``, even from inheritance. ``hidden`` presets are
-  intended to be used as a base for other presets to inherit via the
-  ``inherits`` field.
+  :preset:`testPresets.configurePreset`, even from
+  inheritance. ``hidden`` presets are intended to be used as a base
+  for other presets to inherit via the
+  :preset:`testPresets.inherits` field.
 
 .. _`CMakePresets.testPresets.inherits`:
 
diff --git a/Help/manual/presets/warnings-properties.rst b/Help/manual/presets/warnings-properties.rst
index acfce8f..0a31190 100644
--- a/Help/manual/presets/warnings-properties.rst
+++ b/Help/manual/presets/warnings-properties.rst
@@ -32,6 +32,15 @@
   This may not be set to ``false``
   if ``errors.dev`` is set to ``true``.
 
+.. _`CMakePresets.configurePresets.warnings.installAbsoluteDestination`:
+
+``installAbsoluteDestination``
+  .. presets-versionadded:: 12
+
+  An optional boolean. Equivalent to passing :option:`-Winstall-absolute-destination <cmake -W>` or
+  :option:`-Wno-install-absolute-destination <cmake -Wno->` on the command line.
+  This may not be set to ``false`` if ``errors.installAbsoluteDestination`` is set to ``true``.
+
 .. _`CMakePresets.configurePresets.warnings.uninitialized`:
 
 ``uninitialized``
diff --git a/Help/policy/CMP0216.rst b/Help/policy/CMP0216.rst
new file mode 100644
index 0000000..814440f
--- /dev/null
+++ b/Help/policy/CMP0216.rst
@@ -0,0 +1,32 @@
+CMP0216
+-------
+
+.. versionadded:: 4.4
+
+Swift targets have a default project name.
+
+CMake 4.3 and below did not compile Swift targets with ``-package-name``.
+The package name is useful because Swift package access control makes types,
+functions, and properties visible to other libraries within the same package,
+but not from sources outside of the package.  CMake 4.4 and above prefer to
+pass ``-package-name`` by default using the :variable:`PROJECT_NAME` variable
+value.  This policy provides compatibility for projects that have not been
+updated to expect a package name.
+
+.. note::
+
+   Package access control was added in Swift 5.8 and Xcode 15. This policy has
+   no effect when using an earlier version of Swift or Xcode.
+
+The ``OLD`` behavior for this policy is to build Swift source files without
+passing a package-name.  The ``NEW`` behavior for this policy is to build
+Swift source files with a default package name from :variable:`PROJECT_NAME`.
+
+Regardless of whether this policy is set, the :prop_tgt:`Swift_PACKAGE_NAME`
+target property may be set to explicitly specify or suppress a package name.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: include/STANDARD_ADVICE.rst
+
+.. include:: include/DEPRECATED.rst
diff --git a/Help/policy/CMP0217.rst b/Help/policy/CMP0217.rst
new file mode 100644
index 0000000..052b82c6
--- /dev/null
+++ b/Help/policy/CMP0217.rst
@@ -0,0 +1,24 @@
+CMP0217
+-------
+
+.. versionadded:: 4.4
+
+The :prop_dir:`MACROS` directory property does not exist anymore.
+
+In CMake 4.3 and below, the macros defined in a directory are registered in the
+:prop_dir:`MACROS` directory property.  However, the property does not have the
+same scope as macros, so it does not represent the actual list of macros.
+In CMake 4.4 and above, this property is not longer filled with the defined
+macros.  One may still use :command:`if(COMMAND myMacro) <if(COMMAND)>` to test
+whether one exists.
+
+The ``OLD`` behavior for this policy is to store macros definition in the
+:prop_dir:`MACROS` directory property.
+The ``NEW`` behavior for this policy is to no longer manage the
+:prop_dir:`MACROS` directory property.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: include/STANDARD_ADVICE.rst
+
+.. include:: include/DEPRECATED.rst
diff --git a/Help/prop_dir/MACROS.rst b/Help/prop_dir/MACROS.rst
index 245cc1b..7f1a7c5 100644
--- a/Help/prop_dir/MACROS.rst
+++ b/Help/prop_dir/MACROS.rst
@@ -1,8 +1,4 @@
 MACROS
 ------
 
-List of macro commands available in the current directory.
-
-This read-only property specifies the list of CMake macros currently
-defined.  It is intended for debugging purposes.  See the :command:`macro`
-command.
+This directory property does not exist anymore. See policy :policy:`CMP0217`.
diff --git a/Help/prop_fs/JOB_POOL_COMPILE.rst b/Help/prop_fs/JOB_POOL_COMPILE.rst
new file mode 100644
index 0000000..b460aff
--- /dev/null
+++ b/Help/prop_fs/JOB_POOL_COMPILE.rst
@@ -0,0 +1,28 @@
+JOB_POOL_COMPILE
+----------------
+
+.. versionadded:: 4.4
+
+Ninja only: Pool used for compiling.
+
+The number of parallel compile processes could be limited by defining
+pools with the global :prop_gbl:`JOB_POOLS`
+property and then specifying here the pool name.
+
+This allows to override the :prop_sf:`JOB_POOL_COMPILE` and
+:prop_tgt:`JOB_POOL_COMPILE` values for specific source files within a same
+target.
+
+For instance:
+
+.. code-block:: cmake
+
+  set_property(FILE_SET my_fileset TYPE SOURCE PROPERTY JOB_POOL_COMPILE two_jobs)
+
+This property is undefined by default.
+
+See Also
+^^^^^^^^
+
+* :prop_sf:`JOB_POOL_COMPILE` source file property
+* :prop_tgt:`JOB_POOL_COMPILE` target property
diff --git a/Help/prop_fs/SKIP_LINTING.rst b/Help/prop_fs/SKIP_LINTING.rst
new file mode 100644
index 0000000..7c6546e
--- /dev/null
+++ b/Help/prop_fs/SKIP_LINTING.rst
@@ -0,0 +1,54 @@
+SKIP_LINTING
+------------
+
+.. versionadded:: 4.4
+
+This property allows you to exclude a source files of the specific file set
+from the linting process. The linting process involves running
+tools such as :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT`,
+:prop_tgt:`<LANG>_PVS_STUDIO` and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` on
+the source files, as well as compiling header files as part of
+:prop_tgt:`VERIFY_INTERFACE_HEADER_SETS`. By setting ``SKIP_LINTING`` on a
+file set, the mentioned linting tools will not be executed for the files of
+that particular file set.  If the :prop_fs:`SKIP_LINTING` file set property
+is set, it takes precedence over this source-file and target-wide properties.
+
+This is a convenience alternative to setting the :prop_sf:`SKIP_LINTING`
+source file property individually on each source.
+
+Example
+^^^^^^^
+
+Consider a C++ project that includes multiple source files,
+such as ``main.cpp``, ``things.cpp``, and ``generatedBindings.cpp``.
+In this example, you want to exclude the ``things.cpp`` and
+``generatedBindings.cpp`` files from the linting process. To achieve this, you
+can use a file set and utilize the ``SKIP_LINTING`` property with the
+:command:`set_property(FILE_SET) <set_property>` command as shown below:
+
+.. code-block:: cmake
+
+  add_executable(MyApp main.cpp)
+  target_sources(MyApp PRIVATE FILE_SET skip_lint TYPE SOURCES
+                               FILES things.cpp generatedBindings.cpp)
+
+  set_property(FILE_SET skip_lint TARGET MyApp PROPERTY SKIP_LINTING ON)
+
+In the provided code snippet, the ``SKIP_LINTING`` property is set to true
+for the ``skip_lint`` file set. As a result, when the linting
+tools specified by :prop_tgt:`<LANG>_CPPLINT`, :prop_tgt:`<LANG>_CLANG_TIDY`,
+:prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT` or
+:prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE` are executed, they will skip analyzing
+the ``things.cpp`` and ``generatedBindings.cpp`` files.
+
+By using the ``SKIP_LINTING`` file set property, you can selectively exclude
+specific source files from the linting process. This allows you to focus the
+linting tools on the relevant parts of your project, enhancing the efficiency
+and effectiveness of the linting workflow.
+
+See Also
+^^^^^^^^
+
+* :prop_sf:`SKIP_LINTING` source file property
+* :prop_tgt:`SKIP_LINTING` target property
diff --git a/Help/prop_sf/JOB_POOL_COMPILE.rst b/Help/prop_sf/JOB_POOL_COMPILE.rst
index 79409da..2c9a8d4 100644
--- a/Help/prop_sf/JOB_POOL_COMPILE.rst
+++ b/Help/prop_sf/JOB_POOL_COMPILE.rst
@@ -19,3 +19,9 @@
   set_property(SOURCE main.cc PROPERTY JOB_POOL_COMPILE two_jobs)
 
 This property is undefined by default.
+
+See Also
+^^^^^^^^
+
+* :prop_fs:`JOB_POOL_COMPILE` file set property
+* :prop_tgt:`JOB_POOL_COMPILE` target property
diff --git a/Help/prop_sf/SKIP_LINTING.rst b/Help/prop_sf/SKIP_LINTING.rst
index 5d2c0c4..07e5cd1 100644
--- a/Help/prop_sf/SKIP_LINTING.rst
+++ b/Help/prop_sf/SKIP_LINTING.rst
@@ -43,7 +43,11 @@
 linting tools on the relevant parts of your project, enhancing the efficiency
 and effectiveness of the linting workflow.
 
+A convenient alternative, when multiple sources should be excluded from the
+linting process, is to use the file set's :prop_fs:`SKIP_LINTING` property.
+
 See Also
 ^^^^^^^^
 
+* :prop_fs:`SKIP_LINTING` file set property
 * :prop_tgt:`SKIP_LINTING` target property
diff --git a/Help/prop_tgt/JOB_POOL_COMPILE.rst b/Help/prop_tgt/JOB_POOL_COMPILE.rst
index 5d8e940..d70b010 100644
--- a/Help/prop_tgt/JOB_POOL_COMPILE.rst
+++ b/Help/prop_tgt/JOB_POOL_COMPILE.rst
@@ -15,3 +15,9 @@
 
 This property is initialized by the value of
 :variable:`CMAKE_JOB_POOL_COMPILE`.
+
+See Also
+^^^^^^^^
+
+* :prop_fs:`JOB_POOL_COMPILE` file set property
+* :prop_sf:`JOB_POOL_COMPILE` source file property
diff --git a/Help/prop_tgt/SKIP_LINTING.rst b/Help/prop_tgt/SKIP_LINTING.rst
index 031f653..996c4ee 100644
--- a/Help/prop_tgt/SKIP_LINTING.rst
+++ b/Help/prop_tgt/SKIP_LINTING.rst
@@ -10,16 +10,19 @@
 :prop_tgt:`<LANG>_CPPCHECK`, :prop_tgt:`<LANG>_ICSTAT` and
 :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`) will not be invoked for source files
 compiled by the target.  If the :prop_sf:`SKIP_LINTING` source-file property
-is set on a specific source, it takes precedence over this target-wide property.
+is set on a specific source or if the :prop_fs:`SKIP_LINTING` file set property
+is set, it takes precedence over this target-wide property.
 
 This is a convenience alternative to setting the :prop_sf:`SKIP_LINTING`
 source file property individually on each source.  If either the target's
-:prop_tgt:`SKIP_LINTING` or a source’s :prop_sf:`SKIP_LINTING` is enabled,
-that source will be excluded from linting.
+:prop_tgt:`SKIP_LINTING` or file set's :prop_fs:`SKIP_LINTING` including this
+source or a source’s :prop_sf:`SKIP_LINTING` is enabled, that source will be
+excluded from linting.
 
 The property has no effect on targets that do not have sources.
 
 See Also
 ^^^^^^^^
 
+* :prop_fs:`SKIP_LINTING` file set property
 * :prop_sf:`SKIP_LINTING` source file property
diff --git a/Help/prop_tgt/Swift_MODULE_NAME.rst b/Help/prop_tgt/Swift_MODULE_NAME.rst
index d941b54..8e7ee6b 100644
--- a/Help/prop_tgt/Swift_MODULE_NAME.rst
+++ b/Help/prop_tgt/Swift_MODULE_NAME.rst
@@ -4,4 +4,5 @@
 .. versionadded:: 3.15
 
 This property specifies the name of the Swift module.  It is defaulted to the
-name of the target.
+name of the target, with hyphens replaced by underscores (hyphens are not valid
+in Swift module identifiers).
diff --git a/Help/prop_tgt/Swift_PACKAGE_NAME.rst b/Help/prop_tgt/Swift_PACKAGE_NAME.rst
new file mode 100644
index 0000000..539e033
--- /dev/null
+++ b/Help/prop_tgt/Swift_PACKAGE_NAME.rst
@@ -0,0 +1,21 @@
+Swift_PACKAGE_NAME
+------------------
+
+.. versionadded:: 4.4
+
+Specify the Swift package name for a target.
+
+This property sets the package name passed to the Swift compiler via the
+``-package-name`` flag, which enables the package access control in Swift
+code.
+
+.. note::
+
+   Package access control was added in Swift 5.8 and Xcode 15. This target
+   property has no effect when using an earlier version of Swift or Xcode.
+
+If the property is not set, the :variable:`PROJECT_NAME` variable is used
+as the default package name.  See policy :policy:`CMP0216`.
+
+Setting this property to the empty string will result in the target being
+compiled without a package name.
diff --git a/Help/release/3.23.rst b/Help/release/3.23.rst
index 20e43c5..ece6941 100644
--- a/Help/release/3.23.rst
+++ b/Help/release/3.23.rst
@@ -19,7 +19,7 @@
   which allows the files to include other files.
 
 * :manual:`cmake-presets(7)` files now support a ``${fileDir}`` macro, which
-  contains the directory containing the preset file.
+  contains the directory containing the presets file.
 
 * :manual:`cmake-presets(7)` gained support for specifying the
   ``resolvePackageReferences`` command line option in a build preset to control
diff --git a/Help/release/dev/FILE_SET-JOB_POOL_COMPILE.rst b/Help/release/dev/FILE_SET-JOB_POOL_COMPILE.rst
new file mode 100644
index 0000000..fe30570
--- /dev/null
+++ b/Help/release/dev/FILE_SET-JOB_POOL_COMPILE.rst
@@ -0,0 +1,5 @@
+FILE_SET-JOB_POOL_COMPILE
+-------------------------
+
+* File sets gained the support of the :prop_fs:`JOB_POOL_COMPILE` file set
+  property.
diff --git a/Help/release/dev/FILE_SET-SKIP_LINTING.rst b/Help/release/dev/FILE_SET-SKIP_LINTING.rst
new file mode 100644
index 0000000..eeca0cd
--- /dev/null
+++ b/Help/release/dev/FILE_SET-SKIP_LINTING.rst
@@ -0,0 +1,5 @@
+FILE_SET-SKIP_LINTING
+---------------------
+
+* File sets gained the support of the :prop_fs:`SKIP_LINTING` file set
+  property.
diff --git a/Help/release/dev/MACROS-directory-property.rst b/Help/release/dev/MACROS-directory-property.rst
new file mode 100644
index 0000000..314310b
--- /dev/null
+++ b/Help/release/dev/MACROS-directory-property.rst
@@ -0,0 +1,5 @@
+MACROS-directory-property
+-------------------------
+
+* The :prop_dir:`MACROS` directory property does not exist anymore. See policy
+  :policy:`CMP0217`.
diff --git a/Help/release/dev/ctest-llvm-cov.rst b/Help/release/dev/ctest-llvm-cov.rst
new file mode 100644
index 0000000..f6ca701
--- /dev/null
+++ b/Help/release/dev/ctest-llvm-cov.rst
@@ -0,0 +1,6 @@
+ctest-llvm-cov
+--------------
+
+* :manual:`ctest(1)` gained a :ref:`CoverageTool <ctest-CoverageTool>`
+  option in :ref:`Dashboard Client` mode to configure tests for
+  coverage collection by ``llvm-cov``.
diff --git a/Help/release/dev/ctest_build_preset_option.rst b/Help/release/dev/ctest_build_preset_option.rst
new file mode 100644
index 0000000..d46e6dc
--- /dev/null
+++ b/Help/release/dev/ctest_build_preset_option.rst
@@ -0,0 +1,5 @@
+ctest_build_preset_option
+-------------------------
+
+* The :command:`ctest_build` command gained a ``PRESET`` option
+  drive the build through a build preset.
diff --git a/Help/release/dev/file-api-FILE_SET-SOURCES.rst b/Help/release/dev/file-api-FILE_SET-SOURCES.rst
new file mode 100644
index 0000000..e121c65
--- /dev/null
+++ b/Help/release/dev/file-api-FILE_SET-SOURCES.rst
@@ -0,0 +1,8 @@
+file-api-FILE_SET-SOURCES
+-------------------------
+
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 object has been
+  updated to 2.11.
+* The :manual:`cmake-file-api(7)` "codemodel" version 2 "installers" object's
+  optional ``fileSetType`` field gained a new ``SOURCES`` value to incorporate
+  the new :ref:`file set <file sets>` ``SOURCES`` type in codemodel replies.
diff --git a/Help/release/dev/findcudatoolkit-sanitizer-api.rst b/Help/release/dev/findcudatoolkit-sanitizer-api.rst
new file mode 100644
index 0000000..94bb55b
--- /dev/null
+++ b/Help/release/dev/findcudatoolkit-sanitizer-api.rst
@@ -0,0 +1,6 @@
+findcudatoolkit-sanitizer-api
+-----------------------------
+
+* The :module:`FindCUDAToolkit` module now creates a ``CUDA::sanitizer``
+  target for the :ref:`compute-sanitizer <FindCUDAToolkit_sanitizer>`
+  library.
diff --git a/Help/release/dev/install-absolute-dest-configure-time.rst b/Help/release/dev/install-absolute-dest-configure-time.rst
new file mode 100644
index 0000000..580a46c
--- /dev/null
+++ b/Help/release/dev/install-absolute-dest-configure-time.rst
@@ -0,0 +1,12 @@
+install-absolute-dest-configure-time
+-------------------------------------
+
+* CMake gained the ability to diagnose :command:`install` commands that
+  specify an absolute ``DESTINATION`` path via the
+  :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic category.
+  This diagnostic may be controlled with the
+  :option:`-Winstall-absolute-destination <cmake -W>` command-line option,
+  the ``installAbsoluteDestination`` field in a
+  :manual:`CMake Presets <cmake-presets(7)>`
+  :preset:`configurePresets.warnings` object,
+  or the :command:`cmake_diagnostic` command.
diff --git a/Help/release/dev/list-PREDICATE.rst b/Help/release/dev/list-PREDICATE.rst
new file mode 100644
index 0000000..324bd07
--- /dev/null
+++ b/Help/release/dev/list-PREDICATE.rst
@@ -0,0 +1,10 @@
+list-PREDICATE
+--------------
+
+* The :command:`list(TRANSFORM)` command gained a new ``PREDICATE`` selector
+  that invokes a user-defined callable to decide which elements are
+  transformed.
+
+* The :command:`list(FILTER)` command gained a new ``PREDICATE`` mode
+  that invokes a user-defined callable to decide which elements are
+  included or excluded, complementing the existing ``REGEX`` mode.
diff --git a/Help/release/dev/presets-file-arg.rst b/Help/release/dev/presets-file-arg.rst
new file mode 100644
index 0000000..7cfc969
--- /dev/null
+++ b/Help/release/dev/presets-file-arg.rst
@@ -0,0 +1,11 @@
+presets-file-arg
+----------------
+
+* :manual:`cmake(1)`, :manual:`ctest(1)`, and :manual:`cpack(1)` gained
+  ``--presets-file`` arguments to support loading
+  :manual:`presets <cmake-presets(7)>` from the specified file instead of
+  ``CMakePresets.json`` and/or ``CMakeUserPresets.json``. See
+  :option:`cmake --presets-file`,
+  :option:`cmake --build --presets-file <cmake--build --presets-file>`,
+  :option:`cmake --workflow --presets-file <cmake--workflow --presets-file>`,
+  :option:`ctest --presets-file`, and :option:`cpack --presets-file` for details.
diff --git a/Help/release/dev/presets-fileDir.rst b/Help/release/dev/presets-fileDir.rst
index 45f41d2..6ee8603 100644
--- a/Help/release/dev/presets-fileDir.rst
+++ b/Help/release/dev/presets-fileDir.rst
@@ -3,5 +3,5 @@
 
 * :manual:`cmake-presets(7)` files now support schema version 12. The
   :ref:`${fileDir} <CMakePresets fileDir>` macro now always expands to the
-  directory of the preset file containing the ``${fileDir}`` macro, regardless
+  directory of the presets file containing the ``${fileDir}`` macro, regardless
   of whether it is inherited by another preset in a different directory.
diff --git a/Help/release/dev/swift-ninja-separate-swiftmodule-edge.rst b/Help/release/dev/swift-ninja-separate-swiftmodule-edge.rst
index 4f2c07c..c9eb6f8 100644
--- a/Help/release/dev/swift-ninja-separate-swiftmodule-edge.rst
+++ b/Help/release/dev/swift-ninja-separate-swiftmodule-edge.rst
@@ -6,3 +6,6 @@
   the :variable:`CMAKE_Swift_SEPARATE_MODULE_EMISSION` variable, to control
   whether importable Swift targets emit ``.swiftmodule`` from a dedicated
   build edge.  Policy :policy:`CMP0215` enables this by default.
+
+* The default :prop_tgt:`Swift_MODULE_NAME` now replaces hyphens with
+  underscores, since hyphens are not valid in Swift module identifiers.
diff --git a/Help/release/dev/swift-package-name.rst b/Help/release/dev/swift-package-name.rst
new file mode 100644
index 0000000..b35d47e
--- /dev/null
+++ b/Help/release/dev/swift-package-name.rst
@@ -0,0 +1,8 @@
+swift-package-name
+------------------
+
+* Swift targets now have a package name by default.
+  See policy :policy:`CMP0216`.
+
+* The :prop_tgt:`Swift_PACKAGE_NAME` target property was added to
+  specify a Swift package name.
diff --git a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
index 38e9b7b..5ee7bf3 100644
--- a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
+++ b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -8,3 +8,10 @@
 file takes place.  This variable is used by CMake-generated
 ``cmake_install.cmake`` scripts.  If one sets this variable to ``ON`` while
 running the script, it may get fatal error messages from the script.
+
+.. versionadded:: 4.4
+
+  The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to
+  generate errors for absolute install destinations at generate time.
+
+See also :variable:`CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
index 81c1158..ac40b3d 100644
--- a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
+++ b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -7,3 +7,10 @@
 This variable is used by CMake-generated ``cmake_install.cmake`` scripts.
 If one sets this variable to ``ON`` while running the script, it may get
 warning messages from the script.
+
+.. versionadded:: 4.4
+
+  The :diagnostic:`CMD_INSTALL_ABSOLUTE_DESTINATION` diagnostic can be used to
+  generate warnings for absolute install destinations at generate time.
+
+See also :variable:`CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst b/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst
new file mode 100644
index 0000000..bc94825
--- /dev/null
+++ b/Help/variable/CTEST_TEST_COVERAGE_TOOL.rst
@@ -0,0 +1,7 @@
+CTEST_TEST_COVERAGE_TOOL
+------------------------
+
+.. versionadded:: 4.4
+
+Specify the CTest :ref:`CoverageTool <ctest-CoverageTool>` setting
+in a :manual:`ctest(1)` :ref:`Dashboard Client` script.
diff --git a/Modules/CMakeCCompilerId.c.in b/Modules/CMakeCCompilerId.c.in
index 65d3bab..165376d 100644
--- a/Modules/CMakeCCompilerId.c.in
+++ b/Modules/CMakeCCompilerId.c.in
@@ -74,6 +74,8 @@
      defined(__TI_COMPILER_VERSION__) || defined(__RENESAS__)) &&             \
   !defined(__STRICT_ANSI__)
   "ON"
+#elif defined(__POCC__) && defined(__POCC__EXTENSIONS)
+  "ON"
 #else
   "OFF"
 #endif
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index d6d96ec..5743fa1 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -59,6 +59,7 @@
 set(CMAKE_Swift_RESPONSE_FILE_FLAG @)
 set(CMAKE_Swift_COMPILE_OPTIONS_COLOR_DIAGNOSTICS -color-diagnostics)
 set(CMAKE_Swift_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF -no-color-diagnostics)
+set(CMAKE_Swift_PACKAGE_NAME_FLAG -package-name)
 
 set(CMAKE_Swift_LINKER_PREFERENCE 50)
 set(CMAKE_Swift_LINKER_PREFERENCE_PROPAGATES 1)
diff --git a/Modules/Compiler/PellesC-C.cmake b/Modules/Compiler/PellesC-C.cmake
index c97fa64..4381e12 100644
--- a/Modules/Compiler/PellesC-C.cmake
+++ b/Modules/Compiler/PellesC-C.cmake
@@ -6,3 +6,21 @@
 
 set(CMAKE_C_OUTPUT_EXTENSION ".obj")
 set(CMAKE_C_VERBOSE_FLAG "-V2")
+
+include (Compiler/CMakeCommonCompilerMacros)
+
+set(CMAKE_C90_STANDARD_COMPILE_OPTION "") # No C90-only mode.
+set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+set(CMAKE_C99_STANDARD_COMPILE_OPTION -std=c99)
+set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=c99 -Zx)
+set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
+set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11)
+set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=c11 -Zx)
+set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
+set(CMAKE_C17_STANDARD_COMPILE_OPTION -std=c17)
+set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=c17 -Zx)
+set(CMAKE_C23_STANDARD_COMPILE_OPTION -std=c23)
+set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=c23 -Zx)
+set(CMAKE_C_STANDARD_LATEST 23)
+
+__compiler_check_default_language_standard(C 12.0 23)
diff --git a/Modules/DartConfiguration.tcl.in b/Modules/DartConfiguration.tcl.in
index 67d9f8f..409a5c6 100644
--- a/Modules/DartConfiguration.tcl.in
+++ b/Modules/DartConfiguration.tcl.in
@@ -107,3 +107,6 @@
 # specify behavior for retrying the submission
 CTestSubmitRetryDelay: @CTEST_SUBMIT_RETRY_DELAY@
 CTestSubmitRetryCount: @CTEST_SUBMIT_RETRY_COUNT@
+
+# Invoke each test with environment variables configuring tool's collection.
+CTestTestCoverageTool: @CTEST_TEST_COVERAGE_TOOL@
diff --git a/Modules/FindArmadillo.cmake b/Modules/FindArmadillo.cmake
index 901ea22..861540b 100644
--- a/Modules/FindArmadillo.cmake
+++ b/Modules/FindArmadillo.cmake
@@ -133,7 +133,7 @@
   set(Armadillo_VERSION_NAME "EARLY RELEASE")
 
   if(EXISTS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp")
-    # Read and parse armdillo version header file for version number
+    # Read and parse Armadillo version header file for version number.
     file(STRINGS "${ARMADILLO_INCLUDE_DIR}/armadillo_bits/arma_version.hpp" _ARMA_HEADER_CONTENTS REGEX "#define ARMA_VERSION_[A-Z]+ ")
     string(REGEX REPLACE ".*#define ARMA_VERSION_MAJOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MAJOR "${_ARMA_HEADER_CONTENTS}")
     string(REGEX REPLACE ".*#define ARMA_VERSION_MINOR ([0-9]+).*" "\\1" ARMADILLO_VERSION_MINOR "${_ARMA_HEADER_CONTENTS}")
@@ -176,8 +176,6 @@
     )
   mark_as_advanced(ARMADILLO_LIBRARY)
   set(_ARMA_REQUIRED_VARS ARMADILLO_LIBRARY)
-else()
-  set(ARMADILLO_LIBRARY "")
 endif()
 
 # Transitive linking with the wrapper does not work with MSVC,
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index 12037a7..73e30d2 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -537,6 +537,22 @@
 
 - ``CUDA::bin2c``
 
+.. _`FindCUDAToolkit_sanitizer`:
+
+compute-sanitizer
+"""""""""""""""""
+
+.. versionadded:: 4.4
+
+The `NVIDIA Compute Sanitizer`_ library, which allows the tracing of CUDA
+runtime and driver calls.
+
+Target Created:
+
+- ``CUDA::sanitizer``
+
+.. _`NVIDIA Compute Sanitizer`: https://docs.nvidia.com/compute-sanitizer
+
 Result Variables
 ^^^^^^^^^^^^^^^^
 
@@ -1223,7 +1239,11 @@
 if(CUDAToolkit_FOUND)
 
   function(_CUDAToolkit_find_and_add_import_lib lib_name)
-    cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS;ONLY_SEARCH_FOR" ${ARGN})
+    cmake_parse_arguments(arg "" "" "ALT;DEPS;EXTRA_PATH_SUFFIXES;EXTRA_INCLUDE_DIRS;ONLY_SEARCH_FOR;LIBRARY_SEARCH_DIRS" ${ARGN})
+
+    if(NOT arg_LIBRARY_SEARCH_DIRS)
+      set(arg_LIBRARY_SEARCH_DIRS "${CUDAToolkit_LIBRARY_SEARCH_DIRS}")
+    endif()
 
     if(arg_ONLY_SEARCH_FOR)
       set(search_names ${arg_ONLY_SEARCH_FOR})
@@ -1233,7 +1253,7 @@
 
     find_library(CUDA_${lib_name}_LIBRARY
       NAMES ${search_names}
-      HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
+      HINTS ${arg_LIBRARY_SEARCH_DIRS}
             ENV CUDA_PATH
       PATH_SUFFIXES nvidia/current lib64 ${_CUDAToolkit_win_search_dirs} lib
                     # Support NVHPC splayed math library layout
@@ -1248,7 +1268,7 @@
     if(NOT CUDA_${lib_name}_LIBRARY)
       find_library(CUDA_${lib_name}_LIBRARY
         NAMES ${search_names}
-        HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
+        HINTS ${arg_LIBRARY_SEARCH_DIRS}
               ENV CUDA_PATH
         PATH_SUFFIXES lib64/stubs ${_CUDAToolkit_win_stub_search_dirs} lib/stubs stubs
       )
@@ -1515,6 +1535,24 @@
     add_executable(CUDA::bin2c IMPORTED)
     set_property(TARGET CUDA::bin2c PROPERTY IMPORTED_LOCATION "${CUDA_bin2c_EXECUTABLE}")
   endif()
+
+  _CUDAToolkit_find_and_add_import_lib(
+    sanitizer
+    ONLY_SEARCH_FOR sanitizer-public
+    EXTRA_PATH_SUFFIXES
+      "../compute-sanitizer"
+      "../../../compute-sanitizer"
+      "../Sanitizer"
+      "../../../Sanitizer"
+      "../extras/Sanitizer"
+      "../../../extras/Sanitizer"
+    EXTRA_INCLUDE_DIRS "${CUDAToolkit_CUPTI_INCLUDE_DIR}"
+  )
+  if(TARGET CUDA::sanitizer)
+    get_property(loc TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION)
+    get_filename_component(sanitizer_dir "${loc}" DIRECTORY)
+    target_include_directories(CUDA::sanitizer INTERFACE "${sanitizer_dir}/include")
+  endif()
 endif()
 
 if(_CUDAToolkit_Pop_ROOT_PATH)
diff --git a/Modules/Platform/Windows-PellesC-ASM_POASM.cmake b/Modules/Platform/Windows-PellesC-ASM_POASM.cmake
index 2cafaef..2bff21f 100644
--- a/Modules/Platform/Windows-PellesC-ASM_POASM.cmake
+++ b/Modules/Platform/Windows-PellesC-ASM_POASM.cmake
@@ -6,3 +6,8 @@
 
 set(CMAKE_ASM_POASM_COMPILE_OBJECT
   "<CMAKE_ASM_POASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -Fo<OBJECT> <SOURCE>")
+
+string(APPEND CMAKE_ASM_POASM_FLAGS_DEBUG_INIT " -Zi")
+string(APPEND CMAKE_ASM_POASM_FLAGS_RELEASE_INIT "")
+string(APPEND CMAKE_ASM_POASM_FLAGS_RELWITHDEBINFO_INIT " -Zi")
+string(APPEND CMAKE_ASM_POASM_FLAGS_MINSIZEREL_INIT "")
diff --git a/Modules/Platform/Windows-PellesC-C.cmake b/Modules/Platform/Windows-PellesC-C.cmake
index 64fd5f6..f5a3dad 100644
--- a/Modules/Platform/Windows-PellesC-C.cmake
+++ b/Modules/Platform/Windows-PellesC-C.cmake
@@ -6,3 +6,12 @@
 
 set(CMAKE_C_COMPILE_OBJECT
   "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -Fo<OBJECT> -c <SOURCE>")
+
+# Enable Pelles C's Microsoft extensions to use Windows APIs.
+# The flag also disables standard definitions, so add them.
+string(APPEND CMAKE_C_FLAGS_INIT " -Ze -D__STDC__=1 -D__STDC_VERSION__=__POCC_STDC_VERSION__")
+
+string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " -Zi -Ob0")
+string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -Ot -Ob2 -DNDEBUG=1")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO_INIT " -Zi -Ot -Ob1 -DNDEBUG=1")
+string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -Os -Ob1 -DNDEBUG=1")
diff --git a/Modules/Platform/Windows-PellesC.cmake b/Modules/Platform/Windows-PellesC.cmake
index eb16576..c378c23 100644
--- a/Modules/Platform/Windows-PellesC.cmake
+++ b/Modules/Platform/Windows-PellesC.cmake
@@ -6,16 +6,19 @@
 set(CMAKE_LINK_DEF_FILE_FLAG "-def:")
 set(CMAKE_LINK_LIBRARY_FLAG "")
 
+set(CMAKE_BUILD_TYPE_INIT Debug)
+
 macro(__windows_compiler_pellesc lang)
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <LINK_FLAGS> -out:<TARGET> <OBJECTS>")
 
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "<CMAKE_LINKER> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> -dll -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <LINK_FLAGS> <LINK_LIBRARIES>")
+    "<CMAKE_LINKER> <OBJECTS> ${CMAKE_START_TEMP_FILE} -out:<TARGET> -implib:<TARGET_IMPLIB> -dll -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
   set(CMAKE_${lang}_CREATE_SHARED_MODULE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}")
   set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "")
 
+  set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1)
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "<CMAKE_LINKER> <OBJECTS> -out:<TARGET> -implib:<TARGET_IMPLIB> -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <LINK_FLAGS> <LINK_LIBRARIES>")
+    "<CMAKE_LINKER> <OBJECTS> ${CMAKE_START_TEMP_FILE} -out:<TARGET> -implib:<TARGET_IMPLIB> -version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
 
   set(CMAKE_${lang}_LINK_DEF_FILE_FLAG "${CMAKE_LINK_DEF_FILE_FLAG}")
 
@@ -27,3 +30,8 @@
   endif()
   enable_language(RC)
 endmacro()
+
+foreach (t IN ITEMS EXE SHARED MODULE)
+  string(APPEND CMAKE_${t}_LINKER_FLAGS_DEBUG_INIT " -debug -debugtype:both -dbg")
+  string(APPEND CMAKE_${t}_LINKER_FLAGS_RELWITHDEBINFO_INIT " -debug -debugtype:both -dbg")
+endforeach ()
diff --git a/Modules/ProcessorCount.cmake b/Modules/ProcessorCount.cmake
index 260b663..b8fa1fa 100644
--- a/Modules/ProcessorCount.cmake
+++ b/Modules/ProcessorCount.cmake
@@ -94,12 +94,13 @@
       execute_process(COMMAND ${ProcessorCount_cmd_sysctl} -n hw.ncpu
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE count)
+        OUTPUT_VARIABLE count
+        RESULT_VARIABLE _count_ok)
       #message("ProcessorCount: trying sysctl '${ProcessorCount_cmd_sysctl}'")
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT (_count_ok EQUAL 0 AND count GREATER 0))
     # Linux (systems with nproc):
     # Prefer nproc to getconf if available as getconf may return the host CPU count in Linux containers
     find_program(ProcessorCount_cmd_nproc nproc)
@@ -108,12 +109,13 @@
       execute_process(COMMAND ${ProcessorCount_cmd_nproc}
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE count)
+        OUTPUT_VARIABLE count
+        RESULT_VARIABLE _count_ok)
       #message("ProcessorCount: trying nproc '${ProcessorCount_cmd_nproc}'")
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT (_count_ok EQUAL 0 AND count GREATER 0))
     # Linux (systems with getconf):
     find_program(ProcessorCount_cmd_getconf getconf)
     mark_as_advanced(ProcessorCount_cmd_getconf)
@@ -121,12 +123,13 @@
       execute_process(COMMAND ${ProcessorCount_cmd_getconf} _NPROCESSORS_ONLN
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE count)
+        OUTPUT_VARIABLE count
+        RESULT_VARIABLE _count_ok)
       #message("ProcessorCount: trying getconf '${ProcessorCount_cmd_getconf}'")
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT (_count_ok EQUAL 0 AND count GREATER 0))
     # HPUX (systems with machinfo):
     find_program(ProcessorCount_cmd_machinfo machinfo
       PATHS /usr/contrib/bin)
@@ -150,15 +153,18 @@
         execute_process(COMMAND ${ProcessorCount_cmd_mpsched} -s
           OUTPUT_QUIET
           ERROR_STRIP_TRAILING_WHITESPACE
-          ERROR_VARIABLE mpsched_output)
-        string(REGEX MATCHALL "Processor Count *: *([0-9]+)" procs "${mpsched_output}")
-        set(count "${CMAKE_MATCH_1}")
+          ERROR_VARIABLE mpsched_output
+          RESULT_VARIABLE _mpsched_ok)
+        if(_mpsched_ok EQUAL 0)
+          string(REGEX MATCHALL "Processor Count *: *([0-9]+)" procs "${mpsched_output}")
+          set(count "${CMAKE_MATCH_1}")
+        endif()
         #message("ProcessorCount: trying mpsched -s '${ProcessorCount_cmd_mpsched}'")
       endif()
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT count GREATER 0)
     # AIX (systems with lsconf):
     find_program(ProcessorCount_cmd_lsconf lsconf
       PATHS /usr/sbin)
@@ -167,14 +173,17 @@
       execute_process(COMMAND ${ProcessorCount_cmd_lsconf}
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE lsconf_output)
-      string(REGEX MATCHALL "Number Of Processors: ([0-9]+)" procs "${lsconf_output}")
-      set(count "${CMAKE_MATCH_1}")
+        OUTPUT_VARIABLE lsconf_output
+        RESULT_VARIABLE _lsconf_ok)
+      if(_lsconf_ok EQUAL 0)
+        string(REGEX MATCHALL "Number Of Processors: ([0-9]+)" procs "${lsconf_output}")
+        set(count "${CMAKE_MATCH_1}")
+      endif()
       #message("ProcessorCount: trying lsconf '${ProcessorCount_cmd_lsconf}'")
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT count GREATER 0)
     # QNX (systems with pidin):
     find_program(ProcessorCount_cmd_pidin pidin)
     mark_as_advanced(ProcessorCount_cmd_pidin)
@@ -182,14 +191,17 @@
       execute_process(COMMAND ${ProcessorCount_cmd_pidin} info
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE pidin_output)
-      string(REGEX MATCHALL "Processor[0-9]+: " procs "${pidin_output}")
-      list(LENGTH procs count)
+        OUTPUT_VARIABLE pidin_output
+        RESULT_VARIABLE _pidin_ok)
+      if(_pidin_ok EQUAL 0)
+        string(REGEX MATCHALL "Processor[0-9]+: " procs "${pidin_output}")
+        list(LENGTH procs count)
+      endif()
       #message("ProcessorCount: trying pidin '${ProcessorCount_cmd_pidin}'")
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT count GREATER 0)
     # Sun (systems where psrinfo tool is available)
     find_program(ProcessorCount_cmd_psrinfo psrinfo PATHS /usr/sbin /sbin)
     mark_as_advanced(ProcessorCount_cmd_psrinfo)
@@ -197,13 +209,16 @@
       execute_process(COMMAND ${ProcessorCount_cmd_psrinfo} -p -v
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE psrinfo_output)
-      string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}")
-      set(count "")
-      foreach(proc ${procs})
-        string(REGEX MATCH "has ([0-9]+) virtual" res ${proc})
-        math(EXPR count "${count} + ${CMAKE_MATCH_1}")
-      endforeach()
+        OUTPUT_VARIABLE psrinfo_output
+        RESULT_VARIABLE _psrinfo_ok)
+      if(_psrinfo_ok EQUAL 0)
+        string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}")
+        set(count "")
+        foreach(proc IN LISTS procs)
+          string(REGEX MATCH "has ([0-9]+) virtual" res ${proc})
+          math(EXPR count "${count} + ${CMAKE_MATCH_1}")
+        endforeach()
+      endif()
       #message("ProcessorCount: trying '${ProcessorCount_cmd_psrinfo}' -p -v")
     else()
       # Sun (systems where uname -X emits "NumCPU" in its output):
@@ -213,9 +228,12 @@
         execute_process(COMMAND ${ProcessorCount_cmd_uname} -X
           ERROR_QUIET
           OUTPUT_STRIP_TRAILING_WHITESPACE
-          OUTPUT_VARIABLE uname_X_output)
-        string(REGEX MATCHALL "NumCPU = ([0-9]+)" procs "${uname_X_output}")
-        set(count "${CMAKE_MATCH_1}")
+          OUTPUT_VARIABLE uname_X_output
+          RESULT_VARIABLE _uname_X_ok)
+        if(_uname_X_ok EQUAL 0)
+          string(REGEX MATCHALL "NumCPU = ([0-9]+)" procs "${uname_X_output}")
+          set(count "${CMAKE_MATCH_1}")
+        endif()
         #message("ProcessorCount: trying uname -X '${ProcessorCount_cmd_uname}'")
       endif()
     endif()
@@ -224,7 +242,7 @@
   # Execute this code when all previously attempted methods return empty
   # output:
   #
-  if(NOT count)
+  if(NOT count GREATER 0)
     # Systems with /proc/cpuinfo:
     set(cpuinfo_file /proc/cpuinfo)
     if(EXISTS "${cpuinfo_file}")
@@ -237,7 +255,7 @@
     endif()
   endif()
 
-  if(NOT count)
+  if(NOT count GREATER 0)
     # Haiku
     find_program(ProcessorCount_cmd_sysinfo sysinfo)
     mark_as_advanced(ProcessorCount_cmd_sysinfo)
@@ -245,9 +263,12 @@
       execute_process(COMMAND ${ProcessorCount_cmd_sysinfo}
         ERROR_QUIET
         OUTPUT_STRIP_TRAILING_WHITESPACE
-        OUTPUT_VARIABLE sysinfo_X_output)
-      string(REGEX MATCHALL "\nCPU #[0-9]+:" procs "\n${sysinfo_X_output}")
-      list(LENGTH procs count)
+        OUTPUT_VARIABLE sysinfo_X_output
+        RESULT_VARIABLE _sysinfo_ok)
+      if(_sysinfo_ok EQUAL 0)
+        string(REGEX MATCHALL "\nCPU #[0-9]+:" procs "\n${sysinfo_X_output}")
+        list(LENGTH procs count)
+      endif()
       #message("ProcessorCount: trying sysinfo '${ProcessorCount_cmd_sysinfo}'")
     endif()
   endif()
@@ -255,7 +276,7 @@
   # Since cygwin builds of CMake do not define WIN32 anymore, but they still
   # run on Windows, and will still have this env var defined:
   #
-  if(NOT count)
+  if(NOT count GREATER 0)
     set(count "$ENV{NUMBER_OF_PROCESSORS}")
     #message("ProcessorCount: last fallback, trying environment variable")
   endif()
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index e7a0aef..a5910ef 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -125,6 +125,7 @@
   cmCLocaleEnvironmentScope.cxx
   cmCMakePath.h
   cmCMakePath.cxx
+  cmCMakePresetsArgs.h
   cmCMakePresetsErrors.cxx
   cmCMakePresetsErrors.h
   cmCMakePresetsGraph.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 321380e..d931fa3 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 4)
 set(CMake_VERSION_MINOR 3)
-set(CMake_VERSION_PATCH 20260428)
+set(CMake_VERSION_PATCH 20260506)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index a7ed74b..c595378 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -19,6 +19,7 @@
 
 #include "cmsys/Encoding.hxx"
 
+#include "cmCMakePresetsArgs.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackGeneratorFactory.h"
@@ -46,7 +47,7 @@
 
 cmDocumentationEntry const cmDocumentationUsage = { {}, "  cpack [options]" };
 
-cmDocumentationEntry const cmDocumentationOptions[14] = {
+cmDocumentationEntry const cmDocumentationOptions[] = {
   { "-G <generators>", "Override/define CPACK_GENERATOR" },
   { "-C <Configurations>", "Specify the project configuration(s)" },
   { "-D <var>=<value>", "Set a CPack variable." },
@@ -60,6 +61,7 @@
   { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" },
   { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" },
   { "--preset", "Read arguments from a package preset" },
+  { "--presets-file", "Load package presets from the given file" },
   { "--list-presets", "List available package presets" }
 };
 
@@ -130,8 +132,7 @@
   std::string cpackProjectVendor;
   std::string cpackConfigFile;
 
-  std::string preset;
-  bool listPresets = false;
+  cmCMakePresetsArgs presetsArgs;
 
   std::map<std::string, std::string> definitions;
 
@@ -162,6 +163,12 @@
     return true;
   };
 
+  auto const presetFileLambda = [&presetsArgs](std::string const& value,
+                                               cmake*, cmMakefile*) -> bool {
+    presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value);
+    return true;
+  };
+
   using CommandArgument =
     cmCommandLineArgument<bool(std::string const&, cmake*, cmMakefile*)>;
 
@@ -201,9 +208,11 @@
                      CommandArgument::setToValue(cpackProjectVendor) },
     CommandArgument{ "--preset", "No preset specified for --preset",
                      CommandArgument::Values::One,
-                     CommandArgument::setToValue(preset) },
+                     CommandArgument::setToValue(presetsArgs.PresetName) },
     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(listPresets) },
+                     CommandArgument::setToTrue(presetsArgs.ListPresets) },
+    CommandArgument{ "--presets-file", "No file specified for --presets-file",
+                     CommandArgument::Values::One, presetFileLambda },
     CommandArgument{ "-D", CommandArgument::Values::One,
                      CommandArgument::RequiresSeparator::No,
                      [&log, &definitions](std::string const& arg, cmake*,
@@ -249,7 +258,7 @@
   generators.SetLogger(&log);
 
   // Set up presets
-  if (!preset.empty() || listPresets) {
+  if (presetsArgs.HasPresetsArg()) {
     auto const workingDirectory = cmSystemTools::GetLogicalWorkingDirectory();
 
     auto const presetGeneratorsPresent =
@@ -262,7 +271,8 @@
       };
 
     cmCMakePresetsGraph presetsGraph;
-    auto result = presetsGraph.ReadProjectPresets(workingDirectory);
+    auto result = presetsGraph.ReadProjectPresets(workingDirectory,
+                                                  presetsArgs.PresetsFile);
     if (result != true) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "Could not read presets from "
@@ -271,13 +281,13 @@
       return 1;
     }
 
-    if (listPresets) {
+    if (presetsArgs.ListPresets) {
       presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
       return 0;
     }
 
-    auto resolveResult =
-      presetsGraph.ResolvePreset(preset, presetsGraph.PackagePresets);
+    auto resolveResult = presetsGraph.ResolvePreset(
+      presetsArgs.PresetName, presetsGraph.PackagePresets);
     auto resolveError = cmCMakePresetsGraph::FormatPresetError<
       cmCMakePresetsGraph::PackagePreset>(resolveResult.StatusCode,
                                           resolveResult.ErrorPresetName,
diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx
index d74ab55..6546088 100644
--- a/Source/CTest/cmCTestBuildCommand.cxx
+++ b/Source/CTest/cmCTestBuildCommand.cxx
@@ -6,14 +6,17 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmCMakePresetsGraph.h"
 #include "cmCTest.h"
 #include "cmCTestBuildHandler.h"
 #include "cmCTestGenericHandler.h"
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
+#include "cmJSONState.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
@@ -21,6 +24,8 @@
 #include "cmValue.h"
 #include "cmake.h"
 
+using BuildPreset = cmCMakePresetsGraph::BuildPreset;
+
 bool cmCTestBuildCommand::InitialPass(std::vector<std::string> const& args,
                                       cmExecutionStatus& status) const
 {
@@ -32,7 +37,8 @@
       .Bind("CONFIGURATION"_s, &BuildArguments::Configuration)
       .Bind("FLAGS"_s, &BuildArguments::Flags)
       .Bind("PROJECT_NAME"_s, &BuildArguments::ProjectName)
-      .Bind("PARALLEL_LEVEL"_s, &BuildArguments::ParallelLevel);
+      .Bind("PARALLEL_LEVEL"_s, &BuildArguments::ParallelLevel)
+      .Bind("PRESET"_s, &BuildArguments::Preset);
 
   return this->Invoke(parser, args, status, [&](BuildArguments& a) {
     return this->ExecuteHandlerCommand(a, status);
@@ -46,32 +52,87 @@
   auto const& args = static_cast<BuildArguments&>(arguments);
   auto handler = cm::make_unique<cmCTestBuildHandler>(this->CTest);
 
+  // Build configuration is set according to the following priority order:
+  // 1) The CONFIGURATION option to ctest_build()
+  // 2) CTEST_BUILD_CONFIGURATION script variable
+  // 3) CTEST_CONFIGURATION_TYPE script variable
+  // 4) The ctest -C command line argument
+  // 5) The configuration entry from the build preset
+  cmValue ctestBuildConfiguration =
+    mf.GetDefinition("CTEST_BUILD_CONFIGURATION");
+  std::string cmakeBuildConfiguration = cmNonempty(args.Configuration)
+    ? args.Configuration
+    : cmNonempty(ctestBuildConfiguration) ? *ctestBuildConfiguration
+                                          : this->CTest->GetConfigType();
+
+  std::string const& cmakeBuildAdditionalFlags = cmNonempty(args.Flags)
+    ? args.Flags
+    : mf.GetSafeDefinition("CTEST_BUILD_FLAGS");
+  std::string const& cmakeBuildTarget = cmNonempty(args.Target)
+    ? args.Target
+    : mf.GetSafeDefinition("CTEST_BUILD_TARGET");
+
   cmValue ctestBuildCommand = mf.GetDefinition("CTEST_BUILD_COMMAND");
   if (cmNonempty(ctestBuildCommand)) {
     this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand,
                                        args.Quiet);
+  } else if (!args.Preset.empty()) {
+    std::string const sourceDirectory =
+      mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
+
+    cmCMakePresetsGraph presetsGraph;
+    if (!presetsGraph.ReadProjectPresets(sourceDirectory, "")) {
+      status.SetError(
+        cmStrCat("Could not read presets from \"", sourceDirectory,
+                 "\": ", presetsGraph.parseState.GetErrorMessage()));
+      return nullptr;
+    }
+
+    auto resolveResult =
+      presetsGraph.ResolvePreset(args.Preset, presetsGraph.BuildPresets);
+    auto resolveError = cmCMakePresetsGraph::FormatPresetError<BuildPreset>(
+      resolveResult.StatusCode, resolveResult.ErrorPresetName,
+      sourceDirectory);
+    if (resolveError) {
+      status.SetError(*resolveError);
+      return nullptr;
+    }
+
+    std::string buildCommand =
+      cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"');
+    buildCommand += " --build . --preset \"";
+    buildCommand += args.Preset;
+    buildCommand += "\"";
+
+    if (!cmakeBuildConfiguration.empty()) {
+      buildCommand += " --config \"";
+      buildCommand += cmakeBuildConfiguration;
+      buildCommand += "\"";
+    }
+
+    if (!cmakeBuildTarget.empty()) {
+      buildCommand += " --target \"";
+      buildCommand += cmakeBuildTarget;
+      buildCommand += "\"";
+    }
+
+    if (!args.ParallelLevel.empty()) {
+      buildCommand += " --parallel ";
+      buildCommand += args.ParallelLevel;
+    }
+
+    if (!cmakeBuildAdditionalFlags.empty()) {
+      buildCommand += " -- ";
+      buildCommand += cmakeBuildAdditionalFlags;
+    }
+
+    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                       "SetMakeCommand:" << buildCommand << "\n", args.Quiet);
+    this->CTest->SetCTestConfiguration("MakeCommand", buildCommand,
+                                       args.Quiet);
   } else {
     cmValue cmakeGeneratorName = mf.GetDefinition("CTEST_CMAKE_GENERATOR");
 
-    // Build configuration is determined by: CONFIGURATION argument,
-    // or CTEST_BUILD_CONFIGURATION script variable, or
-    // CTEST_CONFIGURATION_TYPE script variable, or ctest -C command
-    // line argument... in that order.
-    //
-    cmValue ctestBuildConfiguration =
-      mf.GetDefinition("CTEST_BUILD_CONFIGURATION");
-    std::string cmakeBuildConfiguration = cmNonempty(args.Configuration)
-      ? args.Configuration
-      : cmNonempty(ctestBuildConfiguration) ? *ctestBuildConfiguration
-                                            : this->CTest->GetConfigType();
-
-    std::string const& cmakeBuildAdditionalFlags = cmNonempty(args.Flags)
-      ? args.Flags
-      : mf.GetSafeDefinition("CTEST_BUILD_FLAGS");
-    std::string const& cmakeBuildTarget = cmNonempty(args.Target)
-      ? args.Target
-      : mf.GetSafeDefinition("CTEST_BUILD_TARGET");
-
     if (cmNonempty(cmakeGeneratorName)) {
       if (cmakeBuildConfiguration.empty()) {
         cmakeBuildConfiguration = "Release";
diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h
index b83f5db..04a7068 100644
--- a/Source/CTest/cmCTestBuildCommand.h
+++ b/Source/CTest/cmCTestBuildCommand.h
@@ -23,6 +23,7 @@
     std::string Flags;
     std::string ProjectName;
     std::string ParallelLevel;
+    std::string Preset;
   };
 
 private:
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx
index bede3b1..3e9e1dc 100644
--- a/Source/CTest/cmCTestConfigureCommand.cxx
+++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -64,7 +64,7 @@
 
   if (!presetName.empty()) {
     cmCMakePresetsGraph presetsGraph;
-    if (!presetsGraph.ReadProjectPresets(sourceDirectory)) {
+    if (!presetsGraph.ReadProjectPresets(sourceDirectory, "")) {
       status.SetError(
         cmStrCat("Could not read presets from \"", sourceDirectory,
                  "\": ", presetsGraph.parseState.GetErrorMessage()));
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index c5e7ce1..eaf5831 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -14,7 +14,10 @@
 
 #include <cm/memory>
 #include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
 
+#include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTest.h"
@@ -874,6 +877,35 @@
     diff.ApplyTo(env);
   }
 
+  // Inject LLVM Profile name :
+  // https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program
+  if (this->CTest->GetCTestConfiguration("CTestTestCoverageTool") ==
+        "LLVM-COV"_s ||
+      this->TestHandler->TestOptions.CoverageTool == "LLVM-COV"_s) {
+
+    // Isolate the test from any ambient LLVM_PROFILE_FILE
+    env.UnPutEnv("LLVM_PROFILE_FILE");
+    // Value is <Test Dir>/<testName>_<processID>.profraw
+    std::string profileEnv =
+      cmStrCat(this->TestProperties->CTestDirectory, "/",
+               this->TestProperties->Name, "_%p.profraw"_s);
+    cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                       this->Index
+                         << ": Using environment variable LLVM_PROFILE_FILE="_s
+                         << profileEnv << " \n",
+                       this->TestHandler->GetQuiet());
+    env.PutEnv(cmStrCat("LLVM_PROFILE_FILE="_s, profileEnv));
+    // ProcessID -> * to allow for glob to find all
+    // files generated by the test
+    cmSystemTools::ReplaceString(profileEnv, "%p", "*");
+
+    cmsys::Glob glob;
+    glob.FindFiles(profileEnv);
+    for (std::string const& file : glob.GetFiles()) {
+      cmSystemTools::RemoveFile(file);
+    }
+  };
+
   if (this->UseAllocatedResources) {
     this->SetupResourcesEnvironment(env);
   } else {
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index 2d923a4..1c0d4e7 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -96,6 +96,9 @@
   if (!args.ResourceSpecFile.empty()) {
     handler->TestOptions.ResourceSpecFile = args.ResourceSpecFile;
   }
+  if (!args.CoverageTool.empty()) {
+    handler->TestOptions.CoverageTool = args.CoverageTool;
+  }
   if (!args.StopTime.empty()) {
     this->CTest->SetStopTime(args.StopTime);
   }
diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h
index 125d5a5..00297f6 100644
--- a/Source/CTest/cmCTestTestCommand.h
+++ b/Source/CTest/cmCTestTestCommand.h
@@ -46,6 +46,7 @@
     std::string TestLoad;
     std::string ResourceSpecFile;
     std::string OutputJUnit;
+    std::string CoverageTool;
     bool StopOnFailure = false;
   };
 
@@ -74,6 +75,7 @@
       .Bind("TEST_LOAD"_s, &TestArguments::TestLoad)
       .Bind("RESOURCE_SPEC_FILE"_s, &TestArguments::ResourceSpecFile)
       .Bind("STOP_ON_FAILURE"_s, &TestArguments::StopOnFailure)
+      .Bind("COVERAGE_TOOL"_s, &TestArguments::CoverageTool)
       .Bind("OUTPUT_JUNIT"_s, &TestArguments::OutputJUnit);
   }
 
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 65957af..9731a17 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -2493,7 +2493,8 @@
   cmCTestTestProperties test;
   test.Name = testname;
   test.Args = args;
-  test.Directory = cmSystemTools::GetLogicalWorkingDirectory();
+  test.CTestDirectory = cmSystemTools::GetLogicalWorkingDirectory();
+  test.Directory = test.CTestDirectory;
   cmCTestOptionalLog(this->CTest, DEBUG,
                      "Set test directory: " << test.Directory << std::endl,
                      this->Quiet);
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 4d4dbb8..c6238da 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -56,6 +56,8 @@
   std::string ResourceSpecFile;
   std::string JUnitXMLFileName;
 
+  std::string CoverageTool;
+
   std::vector<std::string> TestPassthroughArguments;
 };
 
@@ -131,7 +133,10 @@
     void AppendError(cm::string_view err);
     cm::optional<std::string> Error;
     std::string Name;
+    // working directory for test, overridden by WORKING_DIRECTORY property
     std::string Directory;
+    // Original directory of test creation
+    std::string CTestDirectory;
     std::vector<std::string> Args;
     std::vector<std::string> RequiredFiles;
     std::vector<std::string> Depends;
diff --git a/Source/LexerParser/cmDependsJavaParser.cxx b/Source/LexerParser/cmDependsJavaParser.cxx
index e85bf98..578646f 100644
--- a/Source/LexerParser/cmDependsJavaParser.cxx
+++ b/Source/LexerParser/cmDependsJavaParser.cxx
@@ -119,13 +119,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
 # endif
 #endif
 
-#line 129 "cmDependsJavaParser.cxx"
+#line 132 "cmDependsJavaParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -813,42 +816,42 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_int16 yyrline[] =
 {
-       0,   184,   184,   193,   201,   209,   217,   225,   233,   242,
-     250,   259,   267,   276,   281,   286,   291,   296,   301,   306,
-     311,   317,   325,   334,   344,   353,   362,   370,   380,   386,
-     393,   400,   406,   413,   422,   432,   442,   451,   459,   468,
-     477,   483,   492,   498,   507,   513,   522,   534,   542,   551,
-     563,   576,   584,   592,   601,   609,   618,   618,   618,   619,
-     620,   620,   620,   620,   620,   620,   621,   624,   634,   643,
-     652,   661,   671,   677,   686,   695,   704,   712,   721,   730,
-     736,   745,   753,   761,   769,   778,   786,   795,   801,   809,
-     818,   826,   835,   844,   853,   861,   870,   878,   886,   895,
-     904,   914,   921,   931,   941,   948,   955,   958,   964,   974,
-     984,   994,  1000,  1010,  1020,  1030,  1039,  1049,  1060,  1070,
-    1077,  1087,  1096,  1106,  1115,  1125,  1131,  1141,  1150,  1160,
-    1170,  1177,  1186,  1195,  1204,  1213,  1221,  1230,  1239,  1249,
-    1259,  1268,  1278,  1288,  1295,  1304,  1314,  1323,  1333,  1342,
-    1349,  1359,  1368,  1378,  1387,  1396,  1406,  1416,  1425,  1435,
-    1444,  1453,  1462,  1471,  1480,  1490,  1499,  1508,  1517,  1526,
-    1536,  1545,  1554,  1563,  1572,  1581,  1590,  1599,  1608,  1617,
-    1626,  1635,  1645,  1655,  1666,  1676,  1686,  1695,  1704,  1713,
-    1722,  1731,  1740,  1750,  1760,  1770,  1780,  1787,  1794,  1801,
-    1811,  1818,  1828,  1838,  1847,  1857,  1866,  1876,  1883,  1890,
-    1897,  1905,  1912,  1922,  1929,  1939,  1949,  1956,  1966,  1975,
-    1985,  1995,  2004,  2014,  2023,  2033,  2044,  2051,  2058,  2069,
-    2079,  2089,  2099,  2108,  2118,  2125,  2135,  2144,  2154,  2161,
-    2171,  2180,  2190,  2199,  2205,  2214,  2223,  2232,  2241,  2251,
-    2261,  2268,  2278,  2285,  2295,  2304,  2314,  2323,  2332,  2341,
-    2351,  2358,  2368,  2377,  2387,  2397,  2403,  2410,  2420,  2430,
-    2440,  2451,  2461,  2472,  2482,  2493,  2503,  2513,  2522,  2531,
-    2540,  2549,  2559,  2569,  2579,  2588,  2597,  2606,  2615,  2625,
-    2635,  2645,  2654,  2663,  2672,  2682,  2691,  2700,  2707,  2716,
-    2725,  2734,  2744,  2753,  2762,  2772,  2781,  2790,  2799,  2809,
-    2818,  2827,  2836,  2845,  2854,  2864,  2873,  2882,  2892,  2901,
-    2911,  2920,  2930,  2939,  2949,  2958,  2968,  2977,  2987,  2996,
-    3006,  3015,  3025,  3035,  3045,  3054,  3064,  3073,  3082,  3091,
-    3100,  3109,  3118,  3127,  3136,  3145,  3154,  3163,  3173,  3183,
-    3193,  3202
+       0,   187,   187,   196,   204,   212,   220,   228,   236,   245,
+     253,   262,   270,   279,   284,   289,   294,   299,   304,   309,
+     314,   320,   328,   337,   347,   356,   365,   373,   383,   389,
+     396,   403,   409,   416,   425,   435,   445,   454,   462,   471,
+     480,   486,   495,   501,   510,   516,   525,   537,   545,   554,
+     566,   579,   587,   595,   604,   612,   621,   621,   621,   622,
+     623,   623,   623,   623,   623,   623,   624,   627,   637,   646,
+     655,   664,   674,   680,   689,   698,   707,   715,   724,   733,
+     739,   748,   756,   764,   772,   781,   789,   798,   804,   812,
+     821,   829,   838,   847,   856,   864,   873,   881,   889,   898,
+     907,   917,   924,   934,   944,   951,   958,   961,   967,   977,
+     987,   997,  1003,  1013,  1023,  1033,  1042,  1052,  1063,  1073,
+    1080,  1090,  1099,  1109,  1118,  1128,  1134,  1144,  1153,  1163,
+    1173,  1180,  1189,  1198,  1207,  1216,  1224,  1233,  1242,  1252,
+    1262,  1271,  1281,  1291,  1298,  1307,  1317,  1326,  1336,  1345,
+    1352,  1362,  1371,  1381,  1390,  1399,  1409,  1419,  1428,  1438,
+    1447,  1456,  1465,  1474,  1483,  1493,  1502,  1511,  1520,  1529,
+    1539,  1548,  1557,  1566,  1575,  1584,  1593,  1602,  1611,  1620,
+    1629,  1638,  1648,  1658,  1669,  1679,  1689,  1698,  1707,  1716,
+    1725,  1734,  1743,  1753,  1763,  1773,  1783,  1790,  1797,  1804,
+    1814,  1821,  1831,  1841,  1850,  1860,  1869,  1879,  1886,  1893,
+    1900,  1908,  1915,  1925,  1932,  1942,  1952,  1959,  1969,  1978,
+    1988,  1998,  2007,  2017,  2026,  2036,  2047,  2054,  2061,  2072,
+    2082,  2092,  2102,  2111,  2121,  2128,  2138,  2147,  2157,  2164,
+    2174,  2183,  2193,  2202,  2208,  2217,  2226,  2235,  2244,  2254,
+    2264,  2271,  2281,  2288,  2298,  2307,  2317,  2326,  2335,  2344,
+    2354,  2361,  2371,  2380,  2390,  2400,  2406,  2413,  2423,  2433,
+    2443,  2454,  2464,  2475,  2485,  2496,  2506,  2516,  2525,  2534,
+    2543,  2552,  2562,  2572,  2582,  2591,  2600,  2609,  2618,  2628,
+    2638,  2648,  2657,  2666,  2675,  2685,  2694,  2703,  2710,  2719,
+    2728,  2737,  2747,  2756,  2765,  2775,  2784,  2793,  2802,  2812,
+    2821,  2830,  2839,  2848,  2857,  2867,  2876,  2885,  2895,  2904,
+    2914,  2923,  2933,  2942,  2952,  2961,  2971,  2980,  2990,  2999,
+    3009,  3018,  3028,  3038,  3048,  3057,  3067,  3076,  3085,  3094,
+    3103,  3112,  3121,  3130,  3139,  3148,  3157,  3166,  3176,  3186,
+    3196,  3205
 };
 #endif
 
@@ -2469,214 +2472,214 @@
   switch (yyn)
     {
   case 2: /* Goal: CompilationUnit  */
-#line 185 "cmDependsJavaParser.y"
+#line 188 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2480 "cmDependsJavaParser.cxx"
+#line 2483 "cmDependsJavaParser.cxx"
     break;
 
   case 3: /* Literal: IntegerLiteral  */
-#line 194 "cmDependsJavaParser.y"
+#line 197 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2491 "cmDependsJavaParser.cxx"
+#line 2494 "cmDependsJavaParser.cxx"
     break;
 
   case 4: /* Literal: jp_FLOATINGPOINTLITERAL  */
-#line 202 "cmDependsJavaParser.y"
+#line 205 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2502 "cmDependsJavaParser.cxx"
+#line 2505 "cmDependsJavaParser.cxx"
     break;
 
   case 5: /* Literal: jp_BOOLEANLITERAL  */
-#line 210 "cmDependsJavaParser.y"
+#line 213 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2513 "cmDependsJavaParser.cxx"
+#line 2516 "cmDependsJavaParser.cxx"
     break;
 
   case 6: /* Literal: jp_CHARACTERLITERAL  */
-#line 218 "cmDependsJavaParser.y"
+#line 221 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2524 "cmDependsJavaParser.cxx"
+#line 2527 "cmDependsJavaParser.cxx"
     break;
 
   case 7: /* Literal: jp_STRINGLITERAL  */
-#line 226 "cmDependsJavaParser.y"
+#line 229 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2535 "cmDependsJavaParser.cxx"
+#line 2538 "cmDependsJavaParser.cxx"
     break;
 
   case 8: /* Literal: jp_NULLLITERAL  */
-#line 234 "cmDependsJavaParser.y"
+#line 237 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2546 "cmDependsJavaParser.cxx"
+#line 2549 "cmDependsJavaParser.cxx"
     break;
 
   case 9: /* IntegerLiteral: jp_DECIMALINTEGERLITERAL  */
-#line 243 "cmDependsJavaParser.y"
+#line 246 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2557 "cmDependsJavaParser.cxx"
+#line 2560 "cmDependsJavaParser.cxx"
     break;
 
   case 10: /* IntegerLiteral: jp_HEXINTEGERLITERAL  */
-#line 251 "cmDependsJavaParser.y"
+#line 254 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2568 "cmDependsJavaParser.cxx"
+#line 2571 "cmDependsJavaParser.cxx"
     break;
 
   case 11: /* Type: PrimitiveType  */
-#line 260 "cmDependsJavaParser.y"
+#line 263 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2579 "cmDependsJavaParser.cxx"
+#line 2582 "cmDependsJavaParser.cxx"
     break;
 
   case 12: /* Type: ReferenceType  */
-#line 268 "cmDependsJavaParser.y"
+#line 271 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2590 "cmDependsJavaParser.cxx"
+#line 2593 "cmDependsJavaParser.cxx"
     break;
 
   case 13: /* PrimitiveType: jp_BYTE_TYPE  */
-#line 277 "cmDependsJavaParser.y"
+#line 280 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2598 "cmDependsJavaParser.cxx"
+#line 2601 "cmDependsJavaParser.cxx"
     break;
 
   case 14: /* PrimitiveType: jp_SHORT_TYPE  */
-#line 282 "cmDependsJavaParser.y"
+#line 285 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2606 "cmDependsJavaParser.cxx"
+#line 2609 "cmDependsJavaParser.cxx"
     break;
 
   case 15: /* PrimitiveType: jp_INT_TYPE  */
-#line 287 "cmDependsJavaParser.y"
+#line 290 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2614 "cmDependsJavaParser.cxx"
+#line 2617 "cmDependsJavaParser.cxx"
     break;
 
   case 16: /* PrimitiveType: jp_LONG_TYPE  */
-#line 292 "cmDependsJavaParser.y"
+#line 295 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2622 "cmDependsJavaParser.cxx"
+#line 2625 "cmDependsJavaParser.cxx"
     break;
 
   case 17: /* PrimitiveType: jp_CHAR_TYPE  */
-#line 297 "cmDependsJavaParser.y"
+#line 300 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2630 "cmDependsJavaParser.cxx"
+#line 2633 "cmDependsJavaParser.cxx"
     break;
 
   case 18: /* PrimitiveType: jp_FLOAT_TYPE  */
-#line 302 "cmDependsJavaParser.y"
+#line 305 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2638 "cmDependsJavaParser.cxx"
+#line 2641 "cmDependsJavaParser.cxx"
     break;
 
   case 19: /* PrimitiveType: jp_DOUBLE_TYPE  */
-#line 307 "cmDependsJavaParser.y"
+#line 310 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2646 "cmDependsJavaParser.cxx"
+#line 2649 "cmDependsJavaParser.cxx"
     break;
 
   case 20: /* PrimitiveType: jp_BOOLEAN_TYPE  */
-#line 312 "cmDependsJavaParser.y"
+#line 315 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2654 "cmDependsJavaParser.cxx"
+#line 2657 "cmDependsJavaParser.cxx"
     break;
 
   case 21: /* ReferenceType: ClassOrInterfaceType  */
-#line 318 "cmDependsJavaParser.y"
+#line 321 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2665 "cmDependsJavaParser.cxx"
+#line 2668 "cmDependsJavaParser.cxx"
     break;
 
   case 22: /* ReferenceType: ArrayType  */
-#line 326 "cmDependsJavaParser.y"
+#line 329 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2676 "cmDependsJavaParser.cxx"
+#line 2679 "cmDependsJavaParser.cxx"
     break;
 
   case 23: /* ClassOrInterfaceType: Name  */
-#line 335 "cmDependsJavaParser.y"
+#line 338 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpStoreClass((yyvsp[0].str));
@@ -2684,44 +2687,44 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2688 "cmDependsJavaParser.cxx"
+#line 2691 "cmDependsJavaParser.cxx"
     break;
 
   case 24: /* ClassType: ClassOrInterfaceType  */
-#line 345 "cmDependsJavaParser.y"
+#line 348 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2699 "cmDependsJavaParser.cxx"
+#line 2702 "cmDependsJavaParser.cxx"
     break;
 
   case 25: /* InterfaceType: ClassOrInterfaceType  */
-#line 354 "cmDependsJavaParser.y"
+#line 357 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2710 "cmDependsJavaParser.cxx"
+#line 2713 "cmDependsJavaParser.cxx"
     break;
 
   case 26: /* ArrayType: PrimitiveType Dims  */
-#line 363 "cmDependsJavaParser.y"
+#line 366 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2721 "cmDependsJavaParser.cxx"
+#line 2724 "cmDependsJavaParser.cxx"
     break;
 
   case 27: /* ArrayType: Name Dims  */
-#line 371 "cmDependsJavaParser.y"
+#line 374 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpStoreClass((yyvsp[-1].str));
@@ -2729,56 +2732,56 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2733 "cmDependsJavaParser.cxx"
+#line 2736 "cmDependsJavaParser.cxx"
     break;
 
   case 28: /* Name: SimpleName  */
-#line 381 "cmDependsJavaParser.y"
+#line 384 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2742 "cmDependsJavaParser.cxx"
+#line 2745 "cmDependsJavaParser.cxx"
     break;
 
   case 29: /* Name: QualifiedName  */
-#line 387 "cmDependsJavaParser.y"
+#line 390 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2751 "cmDependsJavaParser.cxx"
+#line 2754 "cmDependsJavaParser.cxx"
     break;
 
   case 30: /* SimpleName: Identifier  */
-#line 394 "cmDependsJavaParser.y"
+#line 397 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2760 "cmDependsJavaParser.cxx"
+#line 2763 "cmDependsJavaParser.cxx"
     break;
 
   case 31: /* Identifier: jp_NAME  */
-#line 401 "cmDependsJavaParser.y"
+#line 404 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2769 "cmDependsJavaParser.cxx"
+#line 2772 "cmDependsJavaParser.cxx"
     break;
 
   case 32: /* Identifier: jp_DOLLAR jp_NAME  */
-#line 407 "cmDependsJavaParser.y"
+#line 410 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2778 "cmDependsJavaParser.cxx"
+#line 2781 "cmDependsJavaParser.cxx"
     break;
 
   case 33: /* QualifiedName: Name jp_DOT Identifier  */
-#line 414 "cmDependsJavaParser.y"
+#line 417 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->AddClassFound((yyvsp[-2].str));
@@ -2786,11 +2789,11 @@
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
   (yyval.str) = const_cast<char*>(yyGetParser->GetCurrentCombine());
 }
-#line 2790 "cmDependsJavaParser.cxx"
+#line 2793 "cmDependsJavaParser.cxx"
     break;
 
   case 34: /* QualifiedName: Name jp_DOT jp_CLASS  */
-#line 423 "cmDependsJavaParser.y"
+#line 426 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2799,11 +2802,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2803 "cmDependsJavaParser.cxx"
+#line 2806 "cmDependsJavaParser.cxx"
     break;
 
   case 35: /* QualifiedName: Name jp_DOT jp_THIS  */
-#line 433 "cmDependsJavaParser.y"
+#line 436 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2812,118 +2815,118 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2816 "cmDependsJavaParser.cxx"
+#line 2819 "cmDependsJavaParser.cxx"
     break;
 
   case 36: /* QualifiedName: SimpleType jp_DOT jp_CLASS  */
-#line 443 "cmDependsJavaParser.y"
+#line 446 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2827 "cmDependsJavaParser.cxx"
+#line 2830 "cmDependsJavaParser.cxx"
     break;
 
   case 37: /* SimpleType: PrimitiveType  */
-#line 452 "cmDependsJavaParser.y"
+#line 455 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2838 "cmDependsJavaParser.cxx"
+#line 2841 "cmDependsJavaParser.cxx"
     break;
 
   case 38: /* SimpleType: jp_VOID  */
-#line 460 "cmDependsJavaParser.y"
+#line 463 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2849 "cmDependsJavaParser.cxx"
+#line 2852 "cmDependsJavaParser.cxx"
     break;
 
   case 39: /* CompilationUnit: PackageDeclarationopt ImportDeclarations TypeDeclarations  */
-#line 469 "cmDependsJavaParser.y"
+#line 472 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2860 "cmDependsJavaParser.cxx"
+#line 2863 "cmDependsJavaParser.cxx"
     break;
 
   case 40: /* PackageDeclarationopt: %empty  */
-#line 477 "cmDependsJavaParser.y"
+#line 480 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2870 "cmDependsJavaParser.cxx"
+#line 2873 "cmDependsJavaParser.cxx"
     break;
 
   case 41: /* PackageDeclarationopt: PackageDeclaration  */
-#line 484 "cmDependsJavaParser.y"
+#line 487 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2881 "cmDependsJavaParser.cxx"
+#line 2884 "cmDependsJavaParser.cxx"
     break;
 
   case 42: /* ImportDeclarations: %empty  */
-#line 492 "cmDependsJavaParser.y"
+#line 495 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2891 "cmDependsJavaParser.cxx"
+#line 2894 "cmDependsJavaParser.cxx"
     break;
 
   case 43: /* ImportDeclarations: ImportDeclarations ImportDeclaration  */
-#line 499 "cmDependsJavaParser.y"
+#line 502 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2902 "cmDependsJavaParser.cxx"
+#line 2905 "cmDependsJavaParser.cxx"
     break;
 
   case 44: /* TypeDeclarations: %empty  */
-#line 507 "cmDependsJavaParser.y"
+#line 510 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2912 "cmDependsJavaParser.cxx"
+#line 2915 "cmDependsJavaParser.cxx"
     break;
 
   case 45: /* TypeDeclarations: TypeDeclarations TypeDeclaration  */
-#line 514 "cmDependsJavaParser.y"
+#line 517 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2923 "cmDependsJavaParser.cxx"
+#line 2926 "cmDependsJavaParser.cxx"
     break;
 
   case 46: /* PackageDeclaration: jp_PACKAGE Name jp_SEMICOL  */
-#line 523 "cmDependsJavaParser.y"
+#line 526 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->SetCurrentPackage((yyvsp[-1].str));
@@ -2933,33 +2936,33 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2937 "cmDependsJavaParser.cxx"
+#line 2940 "cmDependsJavaParser.cxx"
     break;
 
   case 47: /* ImportDeclaration: SingleTypeImportDeclaration  */
-#line 535 "cmDependsJavaParser.y"
+#line 538 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2948 "cmDependsJavaParser.cxx"
+#line 2951 "cmDependsJavaParser.cxx"
     break;
 
   case 48: /* ImportDeclaration: TypeImportOnDemandDeclaration  */
-#line 543 "cmDependsJavaParser.y"
+#line 546 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2959 "cmDependsJavaParser.cxx"
+#line 2962 "cmDependsJavaParser.cxx"
     break;
 
   case 49: /* SingleTypeImportDeclaration: jp_IMPORT Name jp_SEMICOL  */
-#line 552 "cmDependsJavaParser.y"
+#line 555 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->AddPackagesImport((yyvsp[-1].str));
@@ -2969,11 +2972,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2973 "cmDependsJavaParser.cxx"
+#line 2976 "cmDependsJavaParser.cxx"
     break;
 
   case 50: /* TypeImportOnDemandDeclaration: jp_IMPORT Name jp_DOT jp_TIMES jp_SEMICOL  */
-#line 564 "cmDependsJavaParser.y"
+#line 567 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   std::string str = (yyvsp[-3].str);
@@ -2984,77 +2987,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2988 "cmDependsJavaParser.cxx"
+#line 2991 "cmDependsJavaParser.cxx"
     break;
 
   case 51: /* TypeDeclaration: ClassDeclaration  */
-#line 577 "cmDependsJavaParser.y"
+#line 580 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2999 "cmDependsJavaParser.cxx"
+#line 3002 "cmDependsJavaParser.cxx"
     break;
 
   case 52: /* TypeDeclaration: InterfaceDeclaration  */
-#line 585 "cmDependsJavaParser.y"
+#line 588 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3010 "cmDependsJavaParser.cxx"
+#line 3013 "cmDependsJavaParser.cxx"
     break;
 
   case 53: /* TypeDeclaration: jp_SEMICOL  */
-#line 593 "cmDependsJavaParser.y"
+#line 596 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3021 "cmDependsJavaParser.cxx"
+#line 3024 "cmDependsJavaParser.cxx"
     break;
 
   case 54: /* Modifiers: Modifier  */
-#line 602 "cmDependsJavaParser.y"
+#line 605 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3032 "cmDependsJavaParser.cxx"
+#line 3035 "cmDependsJavaParser.cxx"
     break;
 
   case 55: /* Modifiers: Modifiers Modifier  */
-#line 610 "cmDependsJavaParser.y"
+#line 613 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3043 "cmDependsJavaParser.cxx"
+#line 3046 "cmDependsJavaParser.cxx"
     break;
 
   case 67: /* ClassHeader: Modifiersopt jp_CLASS Identifier  */
-#line 625 "cmDependsJavaParser.y"
+#line 628 "cmDependsJavaParser.y"
 {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 3054 "cmDependsJavaParser.cxx"
+#line 3057 "cmDependsJavaParser.cxx"
     break;
 
   case 68: /* ClassDeclaration: ClassHeader ClassBody  */
-#line 635 "cmDependsJavaParser.y"
+#line 638 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3062,11 +3065,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3066 "cmDependsJavaParser.cxx"
+#line 3069 "cmDependsJavaParser.cxx"
     break;
 
   case 69: /* ClassDeclaration: ClassHeader Interfaces ClassBody  */
-#line 644 "cmDependsJavaParser.y"
+#line 647 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(2);
@@ -3074,11 +3077,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3078 "cmDependsJavaParser.cxx"
+#line 3081 "cmDependsJavaParser.cxx"
     break;
 
   case 70: /* ClassDeclaration: ClassHeader Super ClassBody  */
-#line 653 "cmDependsJavaParser.y"
+#line 656 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3086,11 +3089,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3090 "cmDependsJavaParser.cxx"
+#line 3093 "cmDependsJavaParser.cxx"
     break;
 
   case 71: /* ClassDeclaration: ClassHeader Super Interfaces ClassBody  */
-#line 662 "cmDependsJavaParser.y"
+#line 665 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3098,226 +3101,226 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3102 "cmDependsJavaParser.cxx"
+#line 3105 "cmDependsJavaParser.cxx"
     break;
 
   case 72: /* Modifiersopt: %empty  */
-#line 671 "cmDependsJavaParser.y"
+#line 674 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3112 "cmDependsJavaParser.cxx"
+#line 3115 "cmDependsJavaParser.cxx"
     break;
 
   case 73: /* Modifiersopt: Modifiers  */
-#line 678 "cmDependsJavaParser.y"
+#line 681 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3123 "cmDependsJavaParser.cxx"
+#line 3126 "cmDependsJavaParser.cxx"
     break;
 
   case 74: /* Super: jp_EXTENDS ClassType  */
-#line 687 "cmDependsJavaParser.y"
+#line 690 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3134 "cmDependsJavaParser.cxx"
+#line 3137 "cmDependsJavaParser.cxx"
     break;
 
   case 75: /* Interfaces: jp_IMPLEMENTS InterfaceTypeList  */
-#line 696 "cmDependsJavaParser.y"
+#line 699 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3145 "cmDependsJavaParser.cxx"
+#line 3148 "cmDependsJavaParser.cxx"
     break;
 
   case 76: /* InterfaceTypeList: InterfaceType  */
-#line 705 "cmDependsJavaParser.y"
+#line 708 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3156 "cmDependsJavaParser.cxx"
+#line 3159 "cmDependsJavaParser.cxx"
     break;
 
   case 77: /* InterfaceTypeList: InterfaceTypeList jp_COMMA InterfaceType  */
-#line 713 "cmDependsJavaParser.y"
+#line 716 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3167 "cmDependsJavaParser.cxx"
+#line 3170 "cmDependsJavaParser.cxx"
     break;
 
   case 78: /* ClassBody: jp_CURLYSTART ClassBodyDeclarations jp_CURLYEND  */
-#line 722 "cmDependsJavaParser.y"
+#line 725 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3178 "cmDependsJavaParser.cxx"
+#line 3181 "cmDependsJavaParser.cxx"
     break;
 
   case 79: /* ClassBodyDeclarations: %empty  */
-#line 730 "cmDependsJavaParser.y"
+#line 733 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3188 "cmDependsJavaParser.cxx"
+#line 3191 "cmDependsJavaParser.cxx"
     break;
 
   case 80: /* ClassBodyDeclarations: ClassBodyDeclarations ClassBodyDeclaration  */
-#line 737 "cmDependsJavaParser.y"
+#line 740 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3199 "cmDependsJavaParser.cxx"
+#line 3202 "cmDependsJavaParser.cxx"
     break;
 
   case 81: /* ClassBodyDeclaration: ClassMemberDeclaration  */
-#line 746 "cmDependsJavaParser.y"
+#line 749 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3210 "cmDependsJavaParser.cxx"
+#line 3213 "cmDependsJavaParser.cxx"
     break;
 
   case 82: /* ClassBodyDeclaration: StaticInitializer  */
-#line 754 "cmDependsJavaParser.y"
+#line 757 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3221 "cmDependsJavaParser.cxx"
+#line 3224 "cmDependsJavaParser.cxx"
     break;
 
   case 83: /* ClassBodyDeclaration: ConstructorDeclaration  */
-#line 762 "cmDependsJavaParser.y"
+#line 765 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3232 "cmDependsJavaParser.cxx"
+#line 3235 "cmDependsJavaParser.cxx"
     break;
 
   case 84: /* ClassBodyDeclaration: TypeDeclaration  */
-#line 770 "cmDependsJavaParser.y"
+#line 773 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3243 "cmDependsJavaParser.cxx"
+#line 3246 "cmDependsJavaParser.cxx"
     break;
 
   case 85: /* ClassMemberDeclaration: FieldDeclaration  */
-#line 779 "cmDependsJavaParser.y"
+#line 782 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3254 "cmDependsJavaParser.cxx"
+#line 3257 "cmDependsJavaParser.cxx"
     break;
 
   case 86: /* ClassMemberDeclaration: MethodDeclaration  */
-#line 787 "cmDependsJavaParser.y"
+#line 790 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3265 "cmDependsJavaParser.cxx"
+#line 3268 "cmDependsJavaParser.cxx"
     break;
 
   case 87: /* FieldDeclaration: Modifiersopt Type VariableDeclarators jp_SEMICOL  */
-#line 796 "cmDependsJavaParser.y"
+#line 799 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
 }
-#line 3273 "cmDependsJavaParser.cxx"
+#line 3276 "cmDependsJavaParser.cxx"
     break;
 
   case 88: /* VariableDeclarators: VariableDeclarator  */
-#line 802 "cmDependsJavaParser.y"
+#line 805 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3284 "cmDependsJavaParser.cxx"
+#line 3287 "cmDependsJavaParser.cxx"
     break;
 
   case 89: /* VariableDeclarators: VariableDeclarators jp_COMMA VariableDeclarator  */
-#line 810 "cmDependsJavaParser.y"
+#line 813 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3295 "cmDependsJavaParser.cxx"
+#line 3298 "cmDependsJavaParser.cxx"
     break;
 
   case 90: /* VariableDeclarator: VariableDeclaratorId  */
-#line 819 "cmDependsJavaParser.y"
+#line 822 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3306 "cmDependsJavaParser.cxx"
+#line 3309 "cmDependsJavaParser.cxx"
     break;
 
   case 91: /* VariableDeclarator: VariableDeclaratorId jp_EQUALS VariableInitializer  */
-#line 827 "cmDependsJavaParser.y"
+#line 830 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3317 "cmDependsJavaParser.cxx"
+#line 3320 "cmDependsJavaParser.cxx"
     break;
 
   case 92: /* VariableDeclaratorId: Identifier  */
-#line 836 "cmDependsJavaParser.y"
+#line 839 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -3325,77 +3328,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3329 "cmDependsJavaParser.cxx"
+#line 3332 "cmDependsJavaParser.cxx"
     break;
 
   case 93: /* VariableDeclaratorId: VariableDeclaratorId jp_BRACKETSTART jp_BRACKETEND  */
-#line 845 "cmDependsJavaParser.y"
+#line 848 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3340 "cmDependsJavaParser.cxx"
+#line 3343 "cmDependsJavaParser.cxx"
     break;
 
   case 94: /* VariableInitializer: Expression  */
-#line 854 "cmDependsJavaParser.y"
+#line 857 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3351 "cmDependsJavaParser.cxx"
+#line 3354 "cmDependsJavaParser.cxx"
     break;
 
   case 95: /* VariableInitializer: ArrayInitializer  */
-#line 862 "cmDependsJavaParser.y"
+#line 865 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3362 "cmDependsJavaParser.cxx"
+#line 3365 "cmDependsJavaParser.cxx"
     break;
 
   case 96: /* MethodDeclaration: MethodHeader jp_SEMICOL  */
-#line 871 "cmDependsJavaParser.y"
+#line 874 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3373 "cmDependsJavaParser.cxx"
+#line 3376 "cmDependsJavaParser.cxx"
     break;
 
   case 97: /* MethodDeclaration: MethodHeader MethodBody  */
-#line 879 "cmDependsJavaParser.y"
+#line 882 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3384 "cmDependsJavaParser.cxx"
+#line 3387 "cmDependsJavaParser.cxx"
     break;
 
   case 98: /* MethodDeclaration: MethodHeader MethodBody jp_SEMICOL  */
-#line 887 "cmDependsJavaParser.y"
+#line 890 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3395 "cmDependsJavaParser.cxx"
+#line 3398 "cmDependsJavaParser.cxx"
     break;
 
   case 99: /* MethodHeader: Modifiersopt Type MethodDeclarator Throwsopt  */
-#line 896 "cmDependsJavaParser.y"
+#line 899 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3403,11 +3406,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3407 "cmDependsJavaParser.cxx"
+#line 3410 "cmDependsJavaParser.cxx"
     break;
 
   case 100: /* MethodHeader: Modifiersopt jp_VOID MethodDeclarator Throwsopt  */
-#line 905 "cmDependsJavaParser.y"
+#line 908 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3415,22 +3418,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3419 "cmDependsJavaParser.cxx"
+#line 3422 "cmDependsJavaParser.cxx"
     break;
 
   case 101: /* Throwsopt: %empty  */
-#line 914 "cmDependsJavaParser.y"
+#line 917 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3430 "cmDependsJavaParser.cxx"
+#line 3433 "cmDependsJavaParser.cxx"
     break;
 
   case 102: /* Throwsopt: Throws  */
-#line 922 "cmDependsJavaParser.y"
+#line 925 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3438,11 +3441,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3442 "cmDependsJavaParser.cxx"
+#line 3445 "cmDependsJavaParser.cxx"
     break;
 
   case 103: /* MethodDeclarator: Identifier jp_PARESTART FormalParameterListopt jp_PAREEND  */
-#line 932 "cmDependsJavaParser.y"
+#line 935 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3451,40 +3454,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3455 "cmDependsJavaParser.cxx"
+#line 3458 "cmDependsJavaParser.cxx"
     break;
 
   case 104: /* MethodDeclarator: MethodDeclarator jp_BRACKETSTART jp_BRACKETEND  */
-#line 942 "cmDependsJavaParser.y"
+#line 945 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
 
 }
-#line 3464 "cmDependsJavaParser.cxx"
+#line 3467 "cmDependsJavaParser.cxx"
     break;
 
   case 105: /* FormalParameterListopt: %empty  */
-#line 948 "cmDependsJavaParser.y"
+#line 951 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3475 "cmDependsJavaParser.cxx"
+#line 3478 "cmDependsJavaParser.cxx"
     break;
 
   case 107: /* FormalParameterList: FormalParameter  */
-#line 959 "cmDependsJavaParser.y"
+#line 962 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 3484 "cmDependsJavaParser.cxx"
+#line 3487 "cmDependsJavaParser.cxx"
     break;
 
   case 108: /* FormalParameterList: FormalParameterList jp_COMMA FormalParameter  */
-#line 965 "cmDependsJavaParser.y"
+#line 968 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3492,11 +3495,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3496 "cmDependsJavaParser.cxx"
+#line 3499 "cmDependsJavaParser.cxx"
     break;
 
   case 109: /* FormalParameter: Modifiersopt Type VariableDeclaratorId  */
-#line 975 "cmDependsJavaParser.y"
+#line 978 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3504,11 +3507,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3508 "cmDependsJavaParser.cxx"
+#line 3511 "cmDependsJavaParser.cxx"
     break;
 
   case 110: /* Throws: jp_THROWS ClassTypeList  */
-#line 985 "cmDependsJavaParser.y"
+#line 988 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3516,20 +3519,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3520 "cmDependsJavaParser.cxx"
+#line 3523 "cmDependsJavaParser.cxx"
     break;
 
   case 111: /* ClassTypeList: ClassType  */
-#line 995 "cmDependsJavaParser.y"
+#line 998 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 3529 "cmDependsJavaParser.cxx"
+#line 3532 "cmDependsJavaParser.cxx"
     break;
 
   case 112: /* ClassTypeList: ClassTypeList jp_COMMA ClassType  */
-#line 1001 "cmDependsJavaParser.y"
+#line 1004 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3537,11 +3540,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3541 "cmDependsJavaParser.cxx"
+#line 3544 "cmDependsJavaParser.cxx"
     break;
 
   case 113: /* MethodBody: Block  */
-#line 1011 "cmDependsJavaParser.y"
+#line 1014 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3549,11 +3552,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3553 "cmDependsJavaParser.cxx"
+#line 3556 "cmDependsJavaParser.cxx"
     break;
 
   case 114: /* StaticInitializer: jp_STATIC Block  */
-#line 1021 "cmDependsJavaParser.y"
+#line 1024 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3561,11 +3564,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3565 "cmDependsJavaParser.cxx"
+#line 3568 "cmDependsJavaParser.cxx"
     break;
 
   case 115: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody  */
-#line 1031 "cmDependsJavaParser.y"
+#line 1034 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3573,11 +3576,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3577 "cmDependsJavaParser.cxx"
+#line 3580 "cmDependsJavaParser.cxx"
     break;
 
   case 116: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody jp_SEMICOL  */
-#line 1040 "cmDependsJavaParser.y"
+#line 1043 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3585,11 +3588,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3589 "cmDependsJavaParser.cxx"
+#line 3592 "cmDependsJavaParser.cxx"
     break;
 
   case 117: /* ConstructorDeclarator: SimpleName jp_PARESTART FormalParameterListopt jp_PAREEND  */
-#line 1050 "cmDependsJavaParser.y"
+#line 1053 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3598,11 +3601,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3602 "cmDependsJavaParser.cxx"
+#line 3605 "cmDependsJavaParser.cxx"
     break;
 
   case 118: /* ConstructorBody: jp_CURLYSTART ExplicitConstructorInvocationopt BlockStatementsopt jp_CURLYEND  */
-#line 1061 "cmDependsJavaParser.y"
+#line 1064 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3610,22 +3613,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3614 "cmDependsJavaParser.cxx"
+#line 3617 "cmDependsJavaParser.cxx"
     break;
 
   case 119: /* ExplicitConstructorInvocationopt: %empty  */
-#line 1070 "cmDependsJavaParser.y"
+#line 1073 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3625 "cmDependsJavaParser.cxx"
+#line 3628 "cmDependsJavaParser.cxx"
     break;
 
   case 120: /* ExplicitConstructorInvocationopt: ExplicitConstructorInvocationopt ExplicitConstructorInvocation  */
-#line 1078 "cmDependsJavaParser.y"
+#line 1081 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3633,11 +3636,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3637 "cmDependsJavaParser.cxx"
+#line 3640 "cmDependsJavaParser.cxx"
     break;
 
   case 121: /* ExplicitConstructorInvocation: jp_THIS jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL  */
-#line 1088 "cmDependsJavaParser.y"
+#line 1091 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3645,11 +3648,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3649 "cmDependsJavaParser.cxx"
+#line 3652 "cmDependsJavaParser.cxx"
     break;
 
   case 122: /* ExplicitConstructorInvocation: jp_SUPER jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL  */
-#line 1097 "cmDependsJavaParser.y"
+#line 1100 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3657,22 +3660,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3661 "cmDependsJavaParser.cxx"
+#line 3664 "cmDependsJavaParser.cxx"
     break;
 
   case 123: /* InterfaceHeader: Modifiersopt jp_INTERFACE Identifier  */
-#line 1107 "cmDependsJavaParser.y"
+#line 1110 "cmDependsJavaParser.y"
 {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 3672 "cmDependsJavaParser.cxx"
+#line 3675 "cmDependsJavaParser.cxx"
     break;
 
   case 124: /* InterfaceDeclaration: InterfaceHeader ExtendsInterfacesopt InterfaceBody  */
-#line 1116 "cmDependsJavaParser.y"
+#line 1119 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3680,21 +3683,21 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3684 "cmDependsJavaParser.cxx"
+#line 3687 "cmDependsJavaParser.cxx"
     break;
 
   case 125: /* ExtendsInterfacesopt: %empty  */
-#line 1125 "cmDependsJavaParser.y"
+#line 1128 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3694 "cmDependsJavaParser.cxx"
+#line 3697 "cmDependsJavaParser.cxx"
     break;
 
   case 126: /* ExtendsInterfacesopt: ExtendsInterfaces  */
-#line 1132 "cmDependsJavaParser.y"
+#line 1135 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3702,11 +3705,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3706 "cmDependsJavaParser.cxx"
+#line 3709 "cmDependsJavaParser.cxx"
     break;
 
   case 127: /* ExtendsInterfaces: jp_EXTENDS InterfaceType  */
-#line 1142 "cmDependsJavaParser.y"
+#line 1145 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3714,11 +3717,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3718 "cmDependsJavaParser.cxx"
+#line 3721 "cmDependsJavaParser.cxx"
     break;
 
   case 128: /* ExtendsInterfaces: ExtendsInterfaces jp_COMMA InterfaceType  */
-#line 1151 "cmDependsJavaParser.y"
+#line 1154 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3726,11 +3729,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3730 "cmDependsJavaParser.cxx"
+#line 3733 "cmDependsJavaParser.cxx"
     break;
 
   case 129: /* InterfaceBody: jp_CURLYSTART InterfaceMemberDeclarations jp_CURLYEND  */
-#line 1161 "cmDependsJavaParser.y"
+#line 1164 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3738,33 +3741,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3742 "cmDependsJavaParser.cxx"
+#line 3745 "cmDependsJavaParser.cxx"
     break;
 
   case 130: /* InterfaceMemberDeclarations: %empty  */
-#line 1170 "cmDependsJavaParser.y"
+#line 1173 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3753 "cmDependsJavaParser.cxx"
+#line 3756 "cmDependsJavaParser.cxx"
     break;
 
   case 131: /* InterfaceMemberDeclarations: InterfaceMemberDeclarations InterfaceMemberDeclaration  */
-#line 1178 "cmDependsJavaParser.y"
+#line 1181 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3764 "cmDependsJavaParser.cxx"
+#line 3767 "cmDependsJavaParser.cxx"
     break;
 
   case 132: /* InterfaceMemberDeclaration: ConstantDeclaration  */
-#line 1187 "cmDependsJavaParser.y"
+#line 1190 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3772,11 +3775,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3776 "cmDependsJavaParser.cxx"
+#line 3779 "cmDependsJavaParser.cxx"
     break;
 
   case 133: /* InterfaceMemberDeclaration: AbstractMethodDeclaration  */
-#line 1196 "cmDependsJavaParser.y"
+#line 1199 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3784,11 +3787,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3788 "cmDependsJavaParser.cxx"
+#line 3791 "cmDependsJavaParser.cxx"
     break;
 
   case 134: /* InterfaceMemberDeclaration: ClassDeclaration  */
-#line 1205 "cmDependsJavaParser.y"
+#line 1208 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3796,22 +3799,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3800 "cmDependsJavaParser.cxx"
+#line 3803 "cmDependsJavaParser.cxx"
     break;
 
   case 135: /* InterfaceMemberDeclaration: ClassDeclaration jp_SEMICOL  */
-#line 1214 "cmDependsJavaParser.y"
+#line 1217 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3811 "cmDependsJavaParser.cxx"
+#line 3814 "cmDependsJavaParser.cxx"
     break;
 
   case 136: /* InterfaceMemberDeclaration: InterfaceDeclaration  */
-#line 1222 "cmDependsJavaParser.y"
+#line 1225 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3819,22 +3822,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3823 "cmDependsJavaParser.cxx"
+#line 3826 "cmDependsJavaParser.cxx"
     break;
 
   case 137: /* InterfaceMemberDeclaration: InterfaceDeclaration jp_SEMICOL  */
-#line 1231 "cmDependsJavaParser.y"
+#line 1234 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3834 "cmDependsJavaParser.cxx"
+#line 3837 "cmDependsJavaParser.cxx"
     break;
 
   case 138: /* ConstantDeclaration: FieldDeclaration  */
-#line 1240 "cmDependsJavaParser.y"
+#line 1243 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3842,11 +3845,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3846 "cmDependsJavaParser.cxx"
+#line 3849 "cmDependsJavaParser.cxx"
     break;
 
   case 139: /* AbstractMethodDeclaration: MethodHeader Semicols  */
-#line 1250 "cmDependsJavaParser.y"
+#line 1253 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3854,11 +3857,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3858 "cmDependsJavaParser.cxx"
+#line 3861 "cmDependsJavaParser.cxx"
     break;
 
   case 140: /* Semicols: jp_SEMICOL  */
-#line 1260 "cmDependsJavaParser.y"
+#line 1263 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3866,11 +3869,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3870 "cmDependsJavaParser.cxx"
+#line 3873 "cmDependsJavaParser.cxx"
     break;
 
   case 141: /* Semicols: Semicols jp_SEMICOL  */
-#line 1269 "cmDependsJavaParser.y"
+#line 1272 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3878,11 +3881,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3882 "cmDependsJavaParser.cxx"
+#line 3885 "cmDependsJavaParser.cxx"
     break;
 
   case 142: /* ArrayInitializer: jp_CURLYSTART VariableInitializersOptional jp_CURLYEND  */
-#line 1279 "cmDependsJavaParser.y"
+#line 1282 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3890,22 +3893,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3894 "cmDependsJavaParser.cxx"
+#line 3897 "cmDependsJavaParser.cxx"
     break;
 
   case 143: /* VariableInitializersOptional: %empty  */
-#line 1288 "cmDependsJavaParser.y"
+#line 1291 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3905 "cmDependsJavaParser.cxx"
+#line 3908 "cmDependsJavaParser.cxx"
     break;
 
   case 144: /* VariableInitializersOptional: VariableInitializers  */
-#line 1296 "cmDependsJavaParser.y"
+#line 1299 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3913,11 +3916,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3917 "cmDependsJavaParser.cxx"
+#line 3920 "cmDependsJavaParser.cxx"
     break;
 
   case 145: /* VariableInitializersOptional: VariableInitializers jp_COMMA  */
-#line 1305 "cmDependsJavaParser.y"
+#line 1308 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3925,11 +3928,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3929 "cmDependsJavaParser.cxx"
+#line 3932 "cmDependsJavaParser.cxx"
     break;
 
   case 146: /* VariableInitializers: VariableInitializer  */
-#line 1315 "cmDependsJavaParser.y"
+#line 1318 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3937,11 +3940,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3941 "cmDependsJavaParser.cxx"
+#line 3944 "cmDependsJavaParser.cxx"
     break;
 
   case 147: /* VariableInitializers: VariableInitializers jp_COMMA VariableInitializer  */
-#line 1324 "cmDependsJavaParser.y"
+#line 1327 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3949,33 +3952,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3953 "cmDependsJavaParser.cxx"
+#line 3956 "cmDependsJavaParser.cxx"
     break;
 
   case 148: /* Block: jp_CURLYSTART BlockStatementsopt jp_CURLYEND  */
-#line 1334 "cmDependsJavaParser.y"
+#line 1337 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3964 "cmDependsJavaParser.cxx"
+#line 3967 "cmDependsJavaParser.cxx"
     break;
 
   case 149: /* BlockStatementsopt: %empty  */
-#line 1342 "cmDependsJavaParser.y"
+#line 1345 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3975 "cmDependsJavaParser.cxx"
+#line 3978 "cmDependsJavaParser.cxx"
     break;
 
   case 150: /* BlockStatementsopt: BlockStatements  */
-#line 1350 "cmDependsJavaParser.y"
+#line 1353 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3983,11 +3986,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3987 "cmDependsJavaParser.cxx"
+#line 3990 "cmDependsJavaParser.cxx"
     break;
 
   case 151: /* BlockStatements: BlockStatement  */
-#line 1360 "cmDependsJavaParser.y"
+#line 1363 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3995,11 +3998,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3999 "cmDependsJavaParser.cxx"
+#line 4002 "cmDependsJavaParser.cxx"
     break;
 
   case 152: /* BlockStatements: BlockStatements BlockStatement  */
-#line 1369 "cmDependsJavaParser.y"
+#line 1372 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4007,11 +4010,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4011 "cmDependsJavaParser.cxx"
+#line 4014 "cmDependsJavaParser.cxx"
     break;
 
   case 153: /* BlockStatement: LocalVariableDeclarationStatement  */
-#line 1379 "cmDependsJavaParser.y"
+#line 1382 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4019,11 +4022,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4023 "cmDependsJavaParser.cxx"
+#line 4026 "cmDependsJavaParser.cxx"
     break;
 
   case 154: /* BlockStatement: Statement  */
-#line 1388 "cmDependsJavaParser.y"
+#line 1391 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4031,11 +4034,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4035 "cmDependsJavaParser.cxx"
+#line 4038 "cmDependsJavaParser.cxx"
     break;
 
   case 155: /* BlockStatement: ClassDeclaration  */
-#line 1397 "cmDependsJavaParser.y"
+#line 1400 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4043,11 +4046,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4047 "cmDependsJavaParser.cxx"
+#line 4050 "cmDependsJavaParser.cxx"
     break;
 
   case 156: /* LocalVariableDeclarationStatement: LocalVariableDeclaration jp_SEMICOL  */
-#line 1407 "cmDependsJavaParser.y"
+#line 1410 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4055,11 +4058,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4059 "cmDependsJavaParser.cxx"
+#line 4062 "cmDependsJavaParser.cxx"
     break;
 
   case 157: /* LocalVariableDeclaration: Modifiers Type VariableDeclarators  */
-#line 1417 "cmDependsJavaParser.y"
+#line 1420 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(3);
@@ -4067,11 +4070,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4071 "cmDependsJavaParser.cxx"
+#line 4074 "cmDependsJavaParser.cxx"
     break;
 
   case 158: /* LocalVariableDeclaration: Type VariableDeclarators  */
-#line 1426 "cmDependsJavaParser.y"
+#line 1429 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4079,11 +4082,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4083 "cmDependsJavaParser.cxx"
+#line 4086 "cmDependsJavaParser.cxx"
     break;
 
   case 159: /* Statement: StatementWithoutTrailingSubstatement  */
-#line 1436 "cmDependsJavaParser.y"
+#line 1439 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4091,11 +4094,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4095 "cmDependsJavaParser.cxx"
+#line 4098 "cmDependsJavaParser.cxx"
     break;
 
   case 160: /* Statement: LabeledStatement  */
-#line 1445 "cmDependsJavaParser.y"
+#line 1448 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4103,11 +4106,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4107 "cmDependsJavaParser.cxx"
+#line 4110 "cmDependsJavaParser.cxx"
     break;
 
   case 161: /* Statement: IfThenStatement  */
-#line 1454 "cmDependsJavaParser.y"
+#line 1457 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4115,11 +4118,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4119 "cmDependsJavaParser.cxx"
+#line 4122 "cmDependsJavaParser.cxx"
     break;
 
   case 162: /* Statement: IfThenElseStatement  */
-#line 1463 "cmDependsJavaParser.y"
+#line 1466 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4127,11 +4130,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4131 "cmDependsJavaParser.cxx"
+#line 4134 "cmDependsJavaParser.cxx"
     break;
 
   case 163: /* Statement: WhileStatement  */
-#line 1472 "cmDependsJavaParser.y"
+#line 1475 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4139,11 +4142,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4143 "cmDependsJavaParser.cxx"
+#line 4146 "cmDependsJavaParser.cxx"
     break;
 
   case 164: /* Statement: ForStatement  */
-#line 1481 "cmDependsJavaParser.y"
+#line 1484 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4151,11 +4154,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4155 "cmDependsJavaParser.cxx"
+#line 4158 "cmDependsJavaParser.cxx"
     break;
 
   case 165: /* StatementNoShortIf: StatementWithoutTrailingSubstatement  */
-#line 1491 "cmDependsJavaParser.y"
+#line 1494 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4163,11 +4166,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4167 "cmDependsJavaParser.cxx"
+#line 4170 "cmDependsJavaParser.cxx"
     break;
 
   case 166: /* StatementNoShortIf: LabeledStatementNoShortIf  */
-#line 1500 "cmDependsJavaParser.y"
+#line 1503 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4175,11 +4178,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4179 "cmDependsJavaParser.cxx"
+#line 4182 "cmDependsJavaParser.cxx"
     break;
 
   case 167: /* StatementNoShortIf: IfThenElseStatementNoShortIf  */
-#line 1509 "cmDependsJavaParser.y"
+#line 1512 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4187,11 +4190,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4191 "cmDependsJavaParser.cxx"
+#line 4194 "cmDependsJavaParser.cxx"
     break;
 
   case 168: /* StatementNoShortIf: WhileStatementNoShortIf  */
-#line 1518 "cmDependsJavaParser.y"
+#line 1521 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4199,11 +4202,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4203 "cmDependsJavaParser.cxx"
+#line 4206 "cmDependsJavaParser.cxx"
     break;
 
   case 169: /* StatementNoShortIf: ForStatementNoShortIf  */
-#line 1527 "cmDependsJavaParser.y"
+#line 1530 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4211,11 +4214,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4215 "cmDependsJavaParser.cxx"
+#line 4218 "cmDependsJavaParser.cxx"
     break;
 
   case 170: /* StatementWithoutTrailingSubstatement: Block  */
-#line 1537 "cmDependsJavaParser.y"
+#line 1540 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4223,11 +4226,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4227 "cmDependsJavaParser.cxx"
+#line 4230 "cmDependsJavaParser.cxx"
     break;
 
   case 171: /* StatementWithoutTrailingSubstatement: EmptyStatement  */
-#line 1546 "cmDependsJavaParser.y"
+#line 1549 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4235,11 +4238,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4239 "cmDependsJavaParser.cxx"
+#line 4242 "cmDependsJavaParser.cxx"
     break;
 
   case 172: /* StatementWithoutTrailingSubstatement: ExpressionStatement  */
-#line 1555 "cmDependsJavaParser.y"
+#line 1558 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4247,11 +4250,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4251 "cmDependsJavaParser.cxx"
+#line 4254 "cmDependsJavaParser.cxx"
     break;
 
   case 173: /* StatementWithoutTrailingSubstatement: SwitchStatement  */
-#line 1564 "cmDependsJavaParser.y"
+#line 1567 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4259,11 +4262,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4263 "cmDependsJavaParser.cxx"
+#line 4266 "cmDependsJavaParser.cxx"
     break;
 
   case 174: /* StatementWithoutTrailingSubstatement: DoStatement  */
-#line 1573 "cmDependsJavaParser.y"
+#line 1576 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4271,11 +4274,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4275 "cmDependsJavaParser.cxx"
+#line 4278 "cmDependsJavaParser.cxx"
     break;
 
   case 175: /* StatementWithoutTrailingSubstatement: BreakStatement  */
-#line 1582 "cmDependsJavaParser.y"
+#line 1585 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4283,11 +4286,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4287 "cmDependsJavaParser.cxx"
+#line 4290 "cmDependsJavaParser.cxx"
     break;
 
   case 176: /* StatementWithoutTrailingSubstatement: ContinueStatement  */
-#line 1591 "cmDependsJavaParser.y"
+#line 1594 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4295,11 +4298,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4299 "cmDependsJavaParser.cxx"
+#line 4302 "cmDependsJavaParser.cxx"
     break;
 
   case 177: /* StatementWithoutTrailingSubstatement: ReturnStatement  */
-#line 1600 "cmDependsJavaParser.y"
+#line 1603 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4307,11 +4310,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4311 "cmDependsJavaParser.cxx"
+#line 4314 "cmDependsJavaParser.cxx"
     break;
 
   case 178: /* StatementWithoutTrailingSubstatement: SynchronizedStatement  */
-#line 1609 "cmDependsJavaParser.y"
+#line 1612 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4319,11 +4322,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4323 "cmDependsJavaParser.cxx"
+#line 4326 "cmDependsJavaParser.cxx"
     break;
 
   case 179: /* StatementWithoutTrailingSubstatement: ThrowStatement  */
-#line 1618 "cmDependsJavaParser.y"
+#line 1621 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4331,11 +4334,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4335 "cmDependsJavaParser.cxx"
+#line 4338 "cmDependsJavaParser.cxx"
     break;
 
   case 180: /* StatementWithoutTrailingSubstatement: TryStatement  */
-#line 1627 "cmDependsJavaParser.y"
+#line 1630 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4343,11 +4346,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4347 "cmDependsJavaParser.cxx"
+#line 4350 "cmDependsJavaParser.cxx"
     break;
 
   case 181: /* StatementWithoutTrailingSubstatement: AssertStatement  */
-#line 1636 "cmDependsJavaParser.y"
+#line 1639 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4355,11 +4358,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4359 "cmDependsJavaParser.cxx"
+#line 4362 "cmDependsJavaParser.cxx"
     break;
 
   case 182: /* EmptyStatement: jp_SEMICOL  */
-#line 1646 "cmDependsJavaParser.y"
+#line 1649 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4367,11 +4370,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4371 "cmDependsJavaParser.cxx"
+#line 4374 "cmDependsJavaParser.cxx"
     break;
 
   case 183: /* LabeledStatement: Identifier jp_COLON Statement  */
-#line 1656 "cmDependsJavaParser.y"
+#line 1659 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
@@ -4380,11 +4383,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4384 "cmDependsJavaParser.cxx"
+#line 4387 "cmDependsJavaParser.cxx"
     break;
 
   case 184: /* LabeledStatementNoShortIf: Identifier jp_COLON StatementNoShortIf  */
-#line 1667 "cmDependsJavaParser.y"
+#line 1670 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4392,11 +4395,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4396 "cmDependsJavaParser.cxx"
+#line 4399 "cmDependsJavaParser.cxx"
     break;
 
   case 185: /* ExpressionStatement: StatementExpression jp_SEMICOL  */
-#line 1677 "cmDependsJavaParser.y"
+#line 1680 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4404,11 +4407,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4408 "cmDependsJavaParser.cxx"
+#line 4411 "cmDependsJavaParser.cxx"
     break;
 
   case 186: /* StatementExpression: Assignment  */
-#line 1687 "cmDependsJavaParser.y"
+#line 1690 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4416,11 +4419,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4420 "cmDependsJavaParser.cxx"
+#line 4423 "cmDependsJavaParser.cxx"
     break;
 
   case 187: /* StatementExpression: PreIncrementExpression  */
-#line 1696 "cmDependsJavaParser.y"
+#line 1699 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4428,11 +4431,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4432 "cmDependsJavaParser.cxx"
+#line 4435 "cmDependsJavaParser.cxx"
     break;
 
   case 188: /* StatementExpression: PreDecrementExpression  */
-#line 1705 "cmDependsJavaParser.y"
+#line 1708 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4440,11 +4443,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4444 "cmDependsJavaParser.cxx"
+#line 4447 "cmDependsJavaParser.cxx"
     break;
 
   case 189: /* StatementExpression: PostIncrementExpression  */
-#line 1714 "cmDependsJavaParser.y"
+#line 1717 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4452,11 +4455,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4456 "cmDependsJavaParser.cxx"
+#line 4459 "cmDependsJavaParser.cxx"
     break;
 
   case 190: /* StatementExpression: PostDecrementExpression  */
-#line 1723 "cmDependsJavaParser.y"
+#line 1726 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4464,11 +4467,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4468 "cmDependsJavaParser.cxx"
+#line 4471 "cmDependsJavaParser.cxx"
     break;
 
   case 191: /* StatementExpression: MethodInvocation  */
-#line 1732 "cmDependsJavaParser.y"
+#line 1735 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4476,11 +4479,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4480 "cmDependsJavaParser.cxx"
+#line 4483 "cmDependsJavaParser.cxx"
     break;
 
   case 192: /* StatementExpression: ClassInstanceCreationExpression  */
-#line 1741 "cmDependsJavaParser.y"
+#line 1744 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4488,11 +4491,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4492 "cmDependsJavaParser.cxx"
+#line 4495 "cmDependsJavaParser.cxx"
     break;
 
   case 193: /* IfThenStatement: jp_IF jp_PARESTART Expression jp_PAREEND Statement  */
-#line 1751 "cmDependsJavaParser.y"
+#line 1754 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4500,11 +4503,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4504 "cmDependsJavaParser.cxx"
+#line 4507 "cmDependsJavaParser.cxx"
     break;
 
   case 194: /* IfThenElseStatement: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE Statement  */
-#line 1761 "cmDependsJavaParser.y"
+#line 1764 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4512,11 +4515,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4516 "cmDependsJavaParser.cxx"
+#line 4519 "cmDependsJavaParser.cxx"
     break;
 
   case 195: /* IfThenElseStatementNoShortIf: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE StatementNoShortIf  */
-#line 1771 "cmDependsJavaParser.y"
+#line 1774 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4524,40 +4527,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4528 "cmDependsJavaParser.cxx"
+#line 4531 "cmDependsJavaParser.cxx"
     break;
 
   case 196: /* SwitchStatement: jp_SWITCH jp_PARESTART Expression jp_PAREEND SwitchBlock  */
-#line 1781 "cmDependsJavaParser.y"
+#line 1784 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4537 "cmDependsJavaParser.cxx"
+#line 4540 "cmDependsJavaParser.cxx"
     break;
 
   case 197: /* SwitchBlock: jp_CURLYSTART SwitchBlockStatementGroups SwitchLabelsopt jp_CURLYEND  */
-#line 1788 "cmDependsJavaParser.y"
+#line 1791 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
 
 }
-#line 4546 "cmDependsJavaParser.cxx"
+#line 4549 "cmDependsJavaParser.cxx"
     break;
 
   case 198: /* SwitchLabelsopt: %empty  */
-#line 1794 "cmDependsJavaParser.y"
+#line 1797 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4557 "cmDependsJavaParser.cxx"
+#line 4560 "cmDependsJavaParser.cxx"
     break;
 
   case 199: /* SwitchLabelsopt: SwitchLabels  */
-#line 1802 "cmDependsJavaParser.y"
+#line 1805 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4565,22 +4568,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4569 "cmDependsJavaParser.cxx"
+#line 4572 "cmDependsJavaParser.cxx"
     break;
 
   case 200: /* SwitchBlockStatementGroups: %empty  */
-#line 1811 "cmDependsJavaParser.y"
+#line 1814 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4580 "cmDependsJavaParser.cxx"
+#line 4583 "cmDependsJavaParser.cxx"
     break;
 
   case 201: /* SwitchBlockStatementGroups: SwitchBlockStatementGroups SwitchBlockStatementGroup  */
-#line 1819 "cmDependsJavaParser.y"
+#line 1822 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4588,11 +4591,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4592 "cmDependsJavaParser.cxx"
+#line 4595 "cmDependsJavaParser.cxx"
     break;
 
   case 202: /* SwitchBlockStatementGroup: SwitchLabels BlockStatements  */
-#line 1829 "cmDependsJavaParser.y"
+#line 1832 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4600,11 +4603,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4604 "cmDependsJavaParser.cxx"
+#line 4607 "cmDependsJavaParser.cxx"
     break;
 
   case 203: /* SwitchLabels: SwitchLabel  */
-#line 1839 "cmDependsJavaParser.y"
+#line 1842 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4612,11 +4615,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4616 "cmDependsJavaParser.cxx"
+#line 4619 "cmDependsJavaParser.cxx"
     break;
 
   case 204: /* SwitchLabels: SwitchLabels SwitchLabel  */
-#line 1848 "cmDependsJavaParser.y"
+#line 1851 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4624,11 +4627,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4628 "cmDependsJavaParser.cxx"
+#line 4631 "cmDependsJavaParser.cxx"
     break;
 
   case 205: /* SwitchLabel: jp_CASE ConstantExpression jp_COLON  */
-#line 1858 "cmDependsJavaParser.y"
+#line 1861 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4636,11 +4639,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4640 "cmDependsJavaParser.cxx"
+#line 4643 "cmDependsJavaParser.cxx"
     break;
 
   case 206: /* SwitchLabel: jp_DEFAULT jp_COLON  */
-#line 1867 "cmDependsJavaParser.y"
+#line 1870 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4648,58 +4651,58 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4652 "cmDependsJavaParser.cxx"
+#line 4655 "cmDependsJavaParser.cxx"
     break;
 
   case 207: /* WhileStatement: jp_WHILE jp_PARESTART Expression jp_PAREEND Statement  */
-#line 1877 "cmDependsJavaParser.y"
+#line 1880 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4661 "cmDependsJavaParser.cxx"
+#line 4664 "cmDependsJavaParser.cxx"
     break;
 
   case 208: /* WhileStatementNoShortIf: jp_WHILE jp_PARESTART Expression jp_PAREEND StatementNoShortIf  */
-#line 1884 "cmDependsJavaParser.y"
+#line 1887 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4670 "cmDependsJavaParser.cxx"
+#line 4673 "cmDependsJavaParser.cxx"
     break;
 
   case 209: /* DoStatement: jp_DO Statement jp_WHILE jp_PARESTART Expression jp_PAREEND jp_SEMICOL  */
-#line 1891 "cmDependsJavaParser.y"
+#line 1894 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
 
 }
-#line 4679 "cmDependsJavaParser.cxx"
+#line 4682 "cmDependsJavaParser.cxx"
     break;
 
   case 210: /* ForStatement: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND Statement  */
-#line 1899 "cmDependsJavaParser.y"
+#line 1902 "cmDependsJavaParser.y"
 {
   jpElementStart(9);
 
 }
-#line 4688 "cmDependsJavaParser.cxx"
+#line 4691 "cmDependsJavaParser.cxx"
     break;
 
   case 211: /* ForUpdateopt: %empty  */
-#line 1905 "cmDependsJavaParser.y"
+#line 1908 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4699 "cmDependsJavaParser.cxx"
+#line 4702 "cmDependsJavaParser.cxx"
     break;
 
   case 212: /* ForUpdateopt: ForUpdate  */
-#line 1913 "cmDependsJavaParser.y"
+#line 1916 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4707,22 +4710,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4711 "cmDependsJavaParser.cxx"
+#line 4714 "cmDependsJavaParser.cxx"
     break;
 
   case 213: /* ForInitopt: %empty  */
-#line 1922 "cmDependsJavaParser.y"
+#line 1925 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4722 "cmDependsJavaParser.cxx"
+#line 4725 "cmDependsJavaParser.cxx"
     break;
 
   case 214: /* ForInitopt: ForInit  */
-#line 1930 "cmDependsJavaParser.y"
+#line 1933 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4730,33 +4733,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4734 "cmDependsJavaParser.cxx"
+#line 4737 "cmDependsJavaParser.cxx"
     break;
 
   case 215: /* ForStatementNoShortIf: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND StatementNoShortIf  */
-#line 1941 "cmDependsJavaParser.y"
+#line 1944 "cmDependsJavaParser.y"
 {
   jpElementStart(9);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4745 "cmDependsJavaParser.cxx"
+#line 4748 "cmDependsJavaParser.cxx"
     break;
 
   case 216: /* Expressionopt: %empty  */
-#line 1949 "cmDependsJavaParser.y"
+#line 1952 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4756 "cmDependsJavaParser.cxx"
+#line 4759 "cmDependsJavaParser.cxx"
     break;
 
   case 217: /* Expressionopt: Expression  */
-#line 1957 "cmDependsJavaParser.y"
+#line 1960 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4764,11 +4767,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4768 "cmDependsJavaParser.cxx"
+#line 4771 "cmDependsJavaParser.cxx"
     break;
 
   case 218: /* ForInit: StatementExpressionList  */
-#line 1967 "cmDependsJavaParser.y"
+#line 1970 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4776,11 +4779,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4780 "cmDependsJavaParser.cxx"
+#line 4783 "cmDependsJavaParser.cxx"
     break;
 
   case 219: /* ForInit: LocalVariableDeclaration  */
-#line 1976 "cmDependsJavaParser.y"
+#line 1979 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4788,11 +4791,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4792 "cmDependsJavaParser.cxx"
+#line 4795 "cmDependsJavaParser.cxx"
     break;
 
   case 220: /* ForUpdate: StatementExpressionList  */
-#line 1986 "cmDependsJavaParser.y"
+#line 1989 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4800,11 +4803,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4804 "cmDependsJavaParser.cxx"
+#line 4807 "cmDependsJavaParser.cxx"
     break;
 
   case 221: /* StatementExpressionList: StatementExpression  */
-#line 1996 "cmDependsJavaParser.y"
+#line 1999 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4812,11 +4815,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4816 "cmDependsJavaParser.cxx"
+#line 4819 "cmDependsJavaParser.cxx"
     break;
 
   case 222: /* StatementExpressionList: StatementExpressionList jp_COMMA StatementExpression  */
-#line 2005 "cmDependsJavaParser.y"
+#line 2008 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4824,11 +4827,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4828 "cmDependsJavaParser.cxx"
+#line 4831 "cmDependsJavaParser.cxx"
     break;
 
   case 223: /* AssertStatement: jp_ASSERT Expression jp_SEMICOL  */
-#line 2015 "cmDependsJavaParser.y"
+#line 2018 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4836,11 +4839,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4840 "cmDependsJavaParser.cxx"
+#line 4843 "cmDependsJavaParser.cxx"
     break;
 
   case 224: /* AssertStatement: jp_ASSERT Expression jp_COLON Expression jp_SEMICOL  */
-#line 2024 "cmDependsJavaParser.y"
+#line 2027 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4848,11 +4851,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4852 "cmDependsJavaParser.cxx"
+#line 4855 "cmDependsJavaParser.cxx"
     break;
 
   case 225: /* BreakStatement: jp_BREAK Identifieropt jp_SEMICOL  */
-#line 2034 "cmDependsJavaParser.y"
+#line 2037 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4861,31 +4864,31 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4865 "cmDependsJavaParser.cxx"
+#line 4868 "cmDependsJavaParser.cxx"
     break;
 
   case 226: /* Identifieropt: %empty  */
-#line 2044 "cmDependsJavaParser.y"
+#line 2047 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4876 "cmDependsJavaParser.cxx"
+#line 4879 "cmDependsJavaParser.cxx"
     break;
 
   case 227: /* Identifieropt: Identifier  */
-#line 2052 "cmDependsJavaParser.y"
+#line 2055 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 4885 "cmDependsJavaParser.cxx"
+#line 4888 "cmDependsJavaParser.cxx"
     break;
 
   case 228: /* ContinueStatement: jp_CONTINUE Identifieropt jp_SEMICOL  */
-#line 2059 "cmDependsJavaParser.y"
+#line 2062 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4894,11 +4897,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4898 "cmDependsJavaParser.cxx"
+#line 4901 "cmDependsJavaParser.cxx"
     break;
 
   case 229: /* ReturnStatement: jp_RETURN Expressionopt jp_SEMICOL  */
-#line 2070 "cmDependsJavaParser.y"
+#line 2073 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4906,11 +4909,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4910 "cmDependsJavaParser.cxx"
+#line 4913 "cmDependsJavaParser.cxx"
     break;
 
   case 230: /* ThrowStatement: jp_THROW Expression jp_SEMICOL  */
-#line 2080 "cmDependsJavaParser.y"
+#line 2083 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4918,11 +4921,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4922 "cmDependsJavaParser.cxx"
+#line 4925 "cmDependsJavaParser.cxx"
     break;
 
   case 231: /* SynchronizedStatement: jp_SYNCHRONIZED jp_PARESTART Expression jp_PAREEND Block  */
-#line 2090 "cmDependsJavaParser.y"
+#line 2093 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4930,11 +4933,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4934 "cmDependsJavaParser.cxx"
+#line 4937 "cmDependsJavaParser.cxx"
     break;
 
   case 232: /* TryStatement: jp_TRY Block Catches  */
-#line 2100 "cmDependsJavaParser.y"
+#line 2103 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4942,11 +4945,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4946 "cmDependsJavaParser.cxx"
+#line 4949 "cmDependsJavaParser.cxx"
     break;
 
   case 233: /* TryStatement: jp_TRY Block Catchesopt Finally  */
-#line 2109 "cmDependsJavaParser.y"
+#line 2112 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -4954,22 +4957,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4958 "cmDependsJavaParser.cxx"
+#line 4961 "cmDependsJavaParser.cxx"
     break;
 
   case 234: /* Catchesopt: %empty  */
-#line 2118 "cmDependsJavaParser.y"
+#line 2121 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4969 "cmDependsJavaParser.cxx"
+#line 4972 "cmDependsJavaParser.cxx"
     break;
 
   case 235: /* Catchesopt: Catches  */
-#line 2126 "cmDependsJavaParser.y"
+#line 2129 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4977,11 +4980,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4981 "cmDependsJavaParser.cxx"
+#line 4984 "cmDependsJavaParser.cxx"
     break;
 
   case 236: /* Catches: CatchClause  */
-#line 2136 "cmDependsJavaParser.y"
+#line 2139 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4989,11 +4992,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4993 "cmDependsJavaParser.cxx"
+#line 4996 "cmDependsJavaParser.cxx"
     break;
 
   case 237: /* Catches: Catches CatchClause  */
-#line 2145 "cmDependsJavaParser.y"
+#line 2148 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5001,20 +5004,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5005 "cmDependsJavaParser.cxx"
+#line 5008 "cmDependsJavaParser.cxx"
     break;
 
   case 238: /* CatchClause: jp_CATCH jp_PARESTART FormalParameter jp_PAREEND Block  */
-#line 2155 "cmDependsJavaParser.y"
+#line 2158 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 5014 "cmDependsJavaParser.cxx"
+#line 5017 "cmDependsJavaParser.cxx"
     break;
 
   case 239: /* Finally: jp_FINALLY Block  */
-#line 2162 "cmDependsJavaParser.y"
+#line 2165 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5022,11 +5025,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5026 "cmDependsJavaParser.cxx"
+#line 5029 "cmDependsJavaParser.cxx"
     break;
 
   case 240: /* Primary: PrimaryNoNewArray  */
-#line 2172 "cmDependsJavaParser.y"
+#line 2175 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5034,11 +5037,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5038 "cmDependsJavaParser.cxx"
+#line 5041 "cmDependsJavaParser.cxx"
     break;
 
   case 241: /* Primary: ArrayCreationExpression  */
-#line 2181 "cmDependsJavaParser.y"
+#line 2184 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5046,11 +5049,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5050 "cmDependsJavaParser.cxx"
+#line 5053 "cmDependsJavaParser.cxx"
     break;
 
   case 242: /* PrimaryNoNewArray: Literal  */
-#line 2191 "cmDependsJavaParser.y"
+#line 2194 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5058,20 +5061,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5062 "cmDependsJavaParser.cxx"
+#line 5065 "cmDependsJavaParser.cxx"
     break;
 
   case 243: /* PrimaryNoNewArray: jp_THIS  */
-#line 2200 "cmDependsJavaParser.y"
+#line 2203 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 5071 "cmDependsJavaParser.cxx"
+#line 5074 "cmDependsJavaParser.cxx"
     break;
 
   case 244: /* PrimaryNoNewArray: jp_PARESTART Expression jp_PAREEND  */
-#line 2206 "cmDependsJavaParser.y"
+#line 2209 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5079,11 +5082,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5083 "cmDependsJavaParser.cxx"
+#line 5086 "cmDependsJavaParser.cxx"
     break;
 
   case 245: /* PrimaryNoNewArray: ClassInstanceCreationExpression  */
-#line 2215 "cmDependsJavaParser.y"
+#line 2218 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5091,11 +5094,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5095 "cmDependsJavaParser.cxx"
+#line 5098 "cmDependsJavaParser.cxx"
     break;
 
   case 246: /* PrimaryNoNewArray: FieldAccess  */
-#line 2224 "cmDependsJavaParser.y"
+#line 2227 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5103,11 +5106,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5107 "cmDependsJavaParser.cxx"
+#line 5110 "cmDependsJavaParser.cxx"
     break;
 
   case 247: /* PrimaryNoNewArray: MethodInvocation  */
-#line 2233 "cmDependsJavaParser.y"
+#line 2236 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5115,11 +5118,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5119 "cmDependsJavaParser.cxx"
+#line 5122 "cmDependsJavaParser.cxx"
     break;
 
   case 248: /* PrimaryNoNewArray: ArrayAccess  */
-#line 2242 "cmDependsJavaParser.y"
+#line 2245 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5127,11 +5130,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5131 "cmDependsJavaParser.cxx"
+#line 5134 "cmDependsJavaParser.cxx"
     break;
 
   case 249: /* ClassInstanceCreationExpression: New ClassType jp_PARESTART ArgumentListopt jp_PAREEND ClassBodyOpt  */
-#line 2252 "cmDependsJavaParser.y"
+#line 2255 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   jpCheckEmpty(6);
@@ -5139,22 +5142,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5143 "cmDependsJavaParser.cxx"
+#line 5146 "cmDependsJavaParser.cxx"
     break;
 
   case 250: /* ClassBodyOpt: %empty  */
-#line 2261 "cmDependsJavaParser.y"
+#line 2264 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5154 "cmDependsJavaParser.cxx"
+#line 5157 "cmDependsJavaParser.cxx"
     break;
 
   case 251: /* ClassBodyOpt: ClassBody  */
-#line 2269 "cmDependsJavaParser.y"
+#line 2272 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5162,22 +5165,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5166 "cmDependsJavaParser.cxx"
+#line 5169 "cmDependsJavaParser.cxx"
     break;
 
   case 252: /* ArgumentListopt: %empty  */
-#line 2278 "cmDependsJavaParser.y"
+#line 2281 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5177 "cmDependsJavaParser.cxx"
+#line 5180 "cmDependsJavaParser.cxx"
     break;
 
   case 253: /* ArgumentListopt: ArgumentList  */
-#line 2286 "cmDependsJavaParser.y"
+#line 2289 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5185,11 +5188,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5189 "cmDependsJavaParser.cxx"
+#line 5192 "cmDependsJavaParser.cxx"
     break;
 
   case 254: /* ArgumentList: Expression  */
-#line 2296 "cmDependsJavaParser.y"
+#line 2299 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5197,11 +5200,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5201 "cmDependsJavaParser.cxx"
+#line 5204 "cmDependsJavaParser.cxx"
     break;
 
   case 255: /* ArgumentList: ArgumentList jp_COMMA Expression  */
-#line 2305 "cmDependsJavaParser.y"
+#line 2308 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5209,11 +5212,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5213 "cmDependsJavaParser.cxx"
+#line 5216 "cmDependsJavaParser.cxx"
     break;
 
   case 256: /* ArrayCreationExpression: New PrimitiveType DimExprs Dimsopt  */
-#line 2315 "cmDependsJavaParser.y"
+#line 2318 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5221,11 +5224,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5225 "cmDependsJavaParser.cxx"
+#line 5228 "cmDependsJavaParser.cxx"
     break;
 
   case 257: /* ArrayCreationExpression: New ClassOrInterfaceType DimExprs Dimsopt  */
-#line 2324 "cmDependsJavaParser.y"
+#line 2327 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5233,11 +5236,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5237 "cmDependsJavaParser.cxx"
+#line 5240 "cmDependsJavaParser.cxx"
     break;
 
   case 258: /* ArrayCreationExpression: New PrimitiveType Dims ArrayInitializer  */
-#line 2333 "cmDependsJavaParser.y"
+#line 2336 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5245,11 +5248,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5249 "cmDependsJavaParser.cxx"
+#line 5252 "cmDependsJavaParser.cxx"
     break;
 
   case 259: /* ArrayCreationExpression: New ClassOrInterfaceType Dims ArrayInitializer  */
-#line 2342 "cmDependsJavaParser.y"
+#line 2345 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5257,22 +5260,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5261 "cmDependsJavaParser.cxx"
+#line 5264 "cmDependsJavaParser.cxx"
     break;
 
   case 260: /* Dimsopt: %empty  */
-#line 2351 "cmDependsJavaParser.y"
+#line 2354 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5272 "cmDependsJavaParser.cxx"
+#line 5275 "cmDependsJavaParser.cxx"
     break;
 
   case 261: /* Dimsopt: Dims  */
-#line 2359 "cmDependsJavaParser.y"
+#line 2362 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5280,11 +5283,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5284 "cmDependsJavaParser.cxx"
+#line 5287 "cmDependsJavaParser.cxx"
     break;
 
   case 262: /* DimExprs: DimExpr  */
-#line 2369 "cmDependsJavaParser.y"
+#line 2372 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5292,11 +5295,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5296 "cmDependsJavaParser.cxx"
+#line 5299 "cmDependsJavaParser.cxx"
     break;
 
   case 263: /* DimExprs: DimExprs DimExpr  */
-#line 2378 "cmDependsJavaParser.y"
+#line 2381 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5304,11 +5307,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5308 "cmDependsJavaParser.cxx"
+#line 5311 "cmDependsJavaParser.cxx"
     break;
 
   case 264: /* DimExpr: jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2388 "cmDependsJavaParser.y"
+#line 2391 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5316,29 +5319,29 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5320 "cmDependsJavaParser.cxx"
+#line 5323 "cmDependsJavaParser.cxx"
     break;
 
   case 265: /* Dims: jp_BRACKETSTART jp_BRACKETEND  */
-#line 2398 "cmDependsJavaParser.y"
+#line 2401 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
 
 }
-#line 5329 "cmDependsJavaParser.cxx"
+#line 5332 "cmDependsJavaParser.cxx"
     break;
 
   case 266: /* Dims: Dims jp_BRACKETSTART jp_BRACKETEND  */
-#line 2404 "cmDependsJavaParser.y"
+#line 2407 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
 
 }
-#line 5338 "cmDependsJavaParser.cxx"
+#line 5341 "cmDependsJavaParser.cxx"
     break;
 
   case 267: /* FieldAccess: Primary jp_DOT Identifier  */
-#line 2411 "cmDependsJavaParser.y"
+#line 2414 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5347,11 +5350,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5351 "cmDependsJavaParser.cxx"
+#line 5354 "cmDependsJavaParser.cxx"
     break;
 
   case 268: /* FieldAccess: jp_SUPER jp_DOT Identifier  */
-#line 2421 "cmDependsJavaParser.y"
+#line 2424 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5360,11 +5363,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5364 "cmDependsJavaParser.cxx"
+#line 5367 "cmDependsJavaParser.cxx"
     break;
 
   case 269: /* FieldAccess: jp_THIS jp_DOT Identifier  */
-#line 2431 "cmDependsJavaParser.y"
+#line 2434 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5373,11 +5376,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5377 "cmDependsJavaParser.cxx"
+#line 5380 "cmDependsJavaParser.cxx"
     break;
 
   case 270: /* FieldAccess: Primary jp_DOT jp_THIS  */
-#line 2441 "cmDependsJavaParser.y"
+#line 2444 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5386,11 +5389,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5390 "cmDependsJavaParser.cxx"
+#line 5393 "cmDependsJavaParser.cxx"
     break;
 
   case 271: /* MethodInvocation: Name jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2452 "cmDependsJavaParser.y"
+#line 2455 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5399,11 +5402,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5403 "cmDependsJavaParser.cxx"
+#line 5406 "cmDependsJavaParser.cxx"
     break;
 
   case 272: /* MethodInvocation: Primary jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2462 "cmDependsJavaParser.y"
+#line 2465 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-5].str)));
@@ -5413,11 +5416,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5417 "cmDependsJavaParser.cxx"
+#line 5420 "cmDependsJavaParser.cxx"
     break;
 
   case 273: /* MethodInvocation: jp_SUPER jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2473 "cmDependsJavaParser.y"
+#line 2476 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5426,11 +5429,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5430 "cmDependsJavaParser.cxx"
+#line 5433 "cmDependsJavaParser.cxx"
     break;
 
   case 274: /* MethodInvocation: jp_THIS jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2483 "cmDependsJavaParser.y"
+#line 2486 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5439,11 +5442,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5443 "cmDependsJavaParser.cxx"
+#line 5446 "cmDependsJavaParser.cxx"
     break;
 
   case 275: /* ArrayAccess: Name jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2494 "cmDependsJavaParser.y"
+#line 2497 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5452,11 +5455,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5456 "cmDependsJavaParser.cxx"
+#line 5459 "cmDependsJavaParser.cxx"
     break;
 
   case 276: /* ArrayAccess: PrimaryNoNewArray jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2504 "cmDependsJavaParser.y"
+#line 2507 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5464,11 +5467,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5468 "cmDependsJavaParser.cxx"
+#line 5471 "cmDependsJavaParser.cxx"
     break;
 
   case 277: /* PostfixExpression: Primary  */
-#line 2514 "cmDependsJavaParser.y"
+#line 2517 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5476,11 +5479,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5480 "cmDependsJavaParser.cxx"
+#line 5483 "cmDependsJavaParser.cxx"
     break;
 
   case 278: /* PostfixExpression: Name  */
-#line 2523 "cmDependsJavaParser.y"
+#line 2526 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5488,11 +5491,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5492 "cmDependsJavaParser.cxx"
+#line 5495 "cmDependsJavaParser.cxx"
     break;
 
   case 279: /* PostfixExpression: ArrayType jp_DOT jp_CLASS  */
-#line 2532 "cmDependsJavaParser.y"
+#line 2535 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5500,11 +5503,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5504 "cmDependsJavaParser.cxx"
+#line 5507 "cmDependsJavaParser.cxx"
     break;
 
   case 280: /* PostfixExpression: PostIncrementExpression  */
-#line 2541 "cmDependsJavaParser.y"
+#line 2544 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5512,11 +5515,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5516 "cmDependsJavaParser.cxx"
+#line 5519 "cmDependsJavaParser.cxx"
     break;
 
   case 281: /* PostfixExpression: PostDecrementExpression  */
-#line 2550 "cmDependsJavaParser.y"
+#line 2553 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5524,11 +5527,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5528 "cmDependsJavaParser.cxx"
+#line 5531 "cmDependsJavaParser.cxx"
     break;
 
   case 282: /* PostIncrementExpression: PostfixExpression jp_PLUSPLUS  */
-#line 2560 "cmDependsJavaParser.y"
+#line 2563 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5536,11 +5539,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5540 "cmDependsJavaParser.cxx"
+#line 5543 "cmDependsJavaParser.cxx"
     break;
 
   case 283: /* PostDecrementExpression: PostfixExpression jp_MINUSMINUS  */
-#line 2570 "cmDependsJavaParser.y"
+#line 2573 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5548,11 +5551,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5552 "cmDependsJavaParser.cxx"
+#line 5555 "cmDependsJavaParser.cxx"
     break;
 
   case 284: /* UnaryExpression: PreIncrementExpression  */
-#line 2580 "cmDependsJavaParser.y"
+#line 2583 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5560,11 +5563,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5564 "cmDependsJavaParser.cxx"
+#line 5567 "cmDependsJavaParser.cxx"
     break;
 
   case 285: /* UnaryExpression: PreDecrementExpression  */
-#line 2589 "cmDependsJavaParser.y"
+#line 2592 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5572,11 +5575,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5576 "cmDependsJavaParser.cxx"
+#line 5579 "cmDependsJavaParser.cxx"
     break;
 
   case 286: /* UnaryExpression: jp_PLUS UnaryExpression  */
-#line 2598 "cmDependsJavaParser.y"
+#line 2601 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5584,11 +5587,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5588 "cmDependsJavaParser.cxx"
+#line 5591 "cmDependsJavaParser.cxx"
     break;
 
   case 287: /* UnaryExpression: jp_MINUS UnaryExpression  */
-#line 2607 "cmDependsJavaParser.y"
+#line 2610 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5596,11 +5599,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5600 "cmDependsJavaParser.cxx"
+#line 5603 "cmDependsJavaParser.cxx"
     break;
 
   case 288: /* UnaryExpression: UnaryExpressionNotPlusMinus  */
-#line 2616 "cmDependsJavaParser.y"
+#line 2619 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5608,11 +5611,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5612 "cmDependsJavaParser.cxx"
+#line 5615 "cmDependsJavaParser.cxx"
     break;
 
   case 289: /* PreIncrementExpression: jp_PLUSPLUS UnaryExpression  */
-#line 2626 "cmDependsJavaParser.y"
+#line 2629 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5620,11 +5623,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5624 "cmDependsJavaParser.cxx"
+#line 5627 "cmDependsJavaParser.cxx"
     break;
 
   case 290: /* PreDecrementExpression: jp_MINUSMINUS UnaryExpression  */
-#line 2636 "cmDependsJavaParser.y"
+#line 2639 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5632,11 +5635,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5636 "cmDependsJavaParser.cxx"
+#line 5639 "cmDependsJavaParser.cxx"
     break;
 
   case 291: /* UnaryExpressionNotPlusMinus: PostfixExpression  */
-#line 2646 "cmDependsJavaParser.y"
+#line 2649 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5644,11 +5647,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5648 "cmDependsJavaParser.cxx"
+#line 5651 "cmDependsJavaParser.cxx"
     break;
 
   case 292: /* UnaryExpressionNotPlusMinus: jp_TILDE UnaryExpression  */
-#line 2655 "cmDependsJavaParser.y"
+#line 2658 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5656,11 +5659,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5660 "cmDependsJavaParser.cxx"
+#line 5663 "cmDependsJavaParser.cxx"
     break;
 
   case 293: /* UnaryExpressionNotPlusMinus: jp_EXCLAMATION UnaryExpression  */
-#line 2664 "cmDependsJavaParser.y"
+#line 2667 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5668,11 +5671,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5672 "cmDependsJavaParser.cxx"
+#line 5675 "cmDependsJavaParser.cxx"
     break;
 
   case 294: /* UnaryExpressionNotPlusMinus: CastExpression  */
-#line 2673 "cmDependsJavaParser.y"
+#line 2676 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5680,11 +5683,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5684 "cmDependsJavaParser.cxx"
+#line 5687 "cmDependsJavaParser.cxx"
     break;
 
   case 295: /* CastExpression: jp_PARESTART PrimitiveType Dimsopt jp_PAREEND UnaryExpression  */
-#line 2683 "cmDependsJavaParser.y"
+#line 2686 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -5692,11 +5695,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5696 "cmDependsJavaParser.cxx"
+#line 5699 "cmDependsJavaParser.cxx"
     break;
 
   case 296: /* CastExpression: jp_PARESTART Expression jp_PAREEND UnaryExpressionNotPlusMinus  */
-#line 2692 "cmDependsJavaParser.y"
+#line 2695 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5704,20 +5707,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5708 "cmDependsJavaParser.cxx"
+#line 5711 "cmDependsJavaParser.cxx"
     break;
 
   case 297: /* CastExpression: jp_PARESTART Name Dims jp_PAREEND UnaryExpressionNotPlusMinus  */
-#line 2701 "cmDependsJavaParser.y"
+#line 2704 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 5717 "cmDependsJavaParser.cxx"
+#line 5720 "cmDependsJavaParser.cxx"
     break;
 
   case 298: /* MultiplicativeExpression: UnaryExpression  */
-#line 2708 "cmDependsJavaParser.y"
+#line 2711 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5725,11 +5728,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5729 "cmDependsJavaParser.cxx"
+#line 5732 "cmDependsJavaParser.cxx"
     break;
 
   case 299: /* MultiplicativeExpression: MultiplicativeExpression jp_TIMES UnaryExpression  */
-#line 2717 "cmDependsJavaParser.y"
+#line 2720 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5737,11 +5740,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5741 "cmDependsJavaParser.cxx"
+#line 5744 "cmDependsJavaParser.cxx"
     break;
 
   case 300: /* MultiplicativeExpression: MultiplicativeExpression jp_DIVIDE UnaryExpression  */
-#line 2726 "cmDependsJavaParser.y"
+#line 2729 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5749,11 +5752,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5753 "cmDependsJavaParser.cxx"
+#line 5756 "cmDependsJavaParser.cxx"
     break;
 
   case 301: /* MultiplicativeExpression: MultiplicativeExpression jp_PERCENT UnaryExpression  */
-#line 2735 "cmDependsJavaParser.y"
+#line 2738 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5761,11 +5764,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5765 "cmDependsJavaParser.cxx"
+#line 5768 "cmDependsJavaParser.cxx"
     break;
 
   case 302: /* AdditiveExpression: MultiplicativeExpression  */
-#line 2745 "cmDependsJavaParser.y"
+#line 2748 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5773,11 +5776,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5777 "cmDependsJavaParser.cxx"
+#line 5780 "cmDependsJavaParser.cxx"
     break;
 
   case 303: /* AdditiveExpression: AdditiveExpression jp_PLUS MultiplicativeExpression  */
-#line 2754 "cmDependsJavaParser.y"
+#line 2757 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5785,11 +5788,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5789 "cmDependsJavaParser.cxx"
+#line 5792 "cmDependsJavaParser.cxx"
     break;
 
   case 304: /* AdditiveExpression: AdditiveExpression jp_MINUS MultiplicativeExpression  */
-#line 2763 "cmDependsJavaParser.y"
+#line 2766 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5797,11 +5800,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5801 "cmDependsJavaParser.cxx"
+#line 5804 "cmDependsJavaParser.cxx"
     break;
 
   case 305: /* ShiftExpression: AdditiveExpression  */
-#line 2773 "cmDependsJavaParser.y"
+#line 2776 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5809,11 +5812,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5813 "cmDependsJavaParser.cxx"
+#line 5816 "cmDependsJavaParser.cxx"
     break;
 
   case 306: /* ShiftExpression: ShiftExpression jp_LTLT AdditiveExpression  */
-#line 2782 "cmDependsJavaParser.y"
+#line 2785 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5821,11 +5824,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5825 "cmDependsJavaParser.cxx"
+#line 5828 "cmDependsJavaParser.cxx"
     break;
 
   case 307: /* ShiftExpression: ShiftExpression jp_GTGT AdditiveExpression  */
-#line 2791 "cmDependsJavaParser.y"
+#line 2794 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5833,11 +5836,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5837 "cmDependsJavaParser.cxx"
+#line 5840 "cmDependsJavaParser.cxx"
     break;
 
   case 308: /* ShiftExpression: ShiftExpression jp_GTGTGT AdditiveExpression  */
-#line 2800 "cmDependsJavaParser.y"
+#line 2803 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5845,11 +5848,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5849 "cmDependsJavaParser.cxx"
+#line 5852 "cmDependsJavaParser.cxx"
     break;
 
   case 309: /* RelationalExpression: ShiftExpression  */
-#line 2810 "cmDependsJavaParser.y"
+#line 2813 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5857,11 +5860,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5861 "cmDependsJavaParser.cxx"
+#line 5864 "cmDependsJavaParser.cxx"
     break;
 
   case 310: /* RelationalExpression: RelationalExpression jp_LESSTHAN ShiftExpression  */
-#line 2819 "cmDependsJavaParser.y"
+#line 2822 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5869,11 +5872,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5873 "cmDependsJavaParser.cxx"
+#line 5876 "cmDependsJavaParser.cxx"
     break;
 
   case 311: /* RelationalExpression: RelationalExpression jp_GREATER ShiftExpression  */
-#line 2828 "cmDependsJavaParser.y"
+#line 2831 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5881,11 +5884,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5885 "cmDependsJavaParser.cxx"
+#line 5888 "cmDependsJavaParser.cxx"
     break;
 
   case 312: /* RelationalExpression: RelationalExpression jp_LTEQUALS ShiftExpression  */
-#line 2837 "cmDependsJavaParser.y"
+#line 2840 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5893,11 +5896,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5897 "cmDependsJavaParser.cxx"
+#line 5900 "cmDependsJavaParser.cxx"
     break;
 
   case 313: /* RelationalExpression: RelationalExpression jp_GTEQUALS ShiftExpression  */
-#line 2846 "cmDependsJavaParser.y"
+#line 2849 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5905,11 +5908,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5909 "cmDependsJavaParser.cxx"
+#line 5912 "cmDependsJavaParser.cxx"
     break;
 
   case 314: /* RelationalExpression: RelationalExpression jp_INSTANCEOF ReferenceType  */
-#line 2855 "cmDependsJavaParser.y"
+#line 2858 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5917,11 +5920,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5921 "cmDependsJavaParser.cxx"
+#line 5924 "cmDependsJavaParser.cxx"
     break;
 
   case 315: /* EqualityExpression: RelationalExpression  */
-#line 2865 "cmDependsJavaParser.y"
+#line 2868 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5929,11 +5932,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5933 "cmDependsJavaParser.cxx"
+#line 5936 "cmDependsJavaParser.cxx"
     break;
 
   case 316: /* EqualityExpression: EqualityExpression jp_EQUALSEQUALS RelationalExpression  */
-#line 2874 "cmDependsJavaParser.y"
+#line 2877 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5941,11 +5944,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5945 "cmDependsJavaParser.cxx"
+#line 5948 "cmDependsJavaParser.cxx"
     break;
 
   case 317: /* EqualityExpression: EqualityExpression jp_EXCLAMATIONEQUALS RelationalExpression  */
-#line 2883 "cmDependsJavaParser.y"
+#line 2886 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5953,11 +5956,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5957 "cmDependsJavaParser.cxx"
+#line 5960 "cmDependsJavaParser.cxx"
     break;
 
   case 318: /* AndExpression: EqualityExpression  */
-#line 2893 "cmDependsJavaParser.y"
+#line 2896 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5965,11 +5968,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5969 "cmDependsJavaParser.cxx"
+#line 5972 "cmDependsJavaParser.cxx"
     break;
 
   case 319: /* AndExpression: AndExpression jp_AND EqualityExpression  */
-#line 2902 "cmDependsJavaParser.y"
+#line 2905 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5977,11 +5980,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5981 "cmDependsJavaParser.cxx"
+#line 5984 "cmDependsJavaParser.cxx"
     break;
 
   case 320: /* ExclusiveOrExpression: AndExpression  */
-#line 2912 "cmDependsJavaParser.y"
+#line 2915 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5989,11 +5992,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5993 "cmDependsJavaParser.cxx"
+#line 5996 "cmDependsJavaParser.cxx"
     break;
 
   case 321: /* ExclusiveOrExpression: ExclusiveOrExpression jp_CARROT AndExpression  */
-#line 2921 "cmDependsJavaParser.y"
+#line 2924 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6001,11 +6004,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6005 "cmDependsJavaParser.cxx"
+#line 6008 "cmDependsJavaParser.cxx"
     break;
 
   case 322: /* InclusiveOrExpression: ExclusiveOrExpression  */
-#line 2931 "cmDependsJavaParser.y"
+#line 2934 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6013,11 +6016,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6017 "cmDependsJavaParser.cxx"
+#line 6020 "cmDependsJavaParser.cxx"
     break;
 
   case 323: /* InclusiveOrExpression: InclusiveOrExpression jp_PIPE ExclusiveOrExpression  */
-#line 2940 "cmDependsJavaParser.y"
+#line 2943 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6025,11 +6028,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6029 "cmDependsJavaParser.cxx"
+#line 6032 "cmDependsJavaParser.cxx"
     break;
 
   case 324: /* ConditionalAndExpression: InclusiveOrExpression  */
-#line 2950 "cmDependsJavaParser.y"
+#line 2953 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6037,11 +6040,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6041 "cmDependsJavaParser.cxx"
+#line 6044 "cmDependsJavaParser.cxx"
     break;
 
   case 325: /* ConditionalAndExpression: ConditionalAndExpression jp_ANDAND InclusiveOrExpression  */
-#line 2959 "cmDependsJavaParser.y"
+#line 2962 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6049,11 +6052,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6053 "cmDependsJavaParser.cxx"
+#line 6056 "cmDependsJavaParser.cxx"
     break;
 
   case 326: /* ConditionalOrExpression: ConditionalAndExpression  */
-#line 2969 "cmDependsJavaParser.y"
+#line 2972 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6061,11 +6064,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6065 "cmDependsJavaParser.cxx"
+#line 6068 "cmDependsJavaParser.cxx"
     break;
 
   case 327: /* ConditionalOrExpression: ConditionalOrExpression jp_PIPEPIPE ConditionalAndExpression  */
-#line 2978 "cmDependsJavaParser.y"
+#line 2981 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6073,11 +6076,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6077 "cmDependsJavaParser.cxx"
+#line 6080 "cmDependsJavaParser.cxx"
     break;
 
   case 328: /* ConditionalExpression: ConditionalOrExpression  */
-#line 2988 "cmDependsJavaParser.y"
+#line 2991 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6085,11 +6088,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6089 "cmDependsJavaParser.cxx"
+#line 6092 "cmDependsJavaParser.cxx"
     break;
 
   case 329: /* ConditionalExpression: ConditionalOrExpression jp_QUESTION Expression jp_COLON ConditionalExpression  */
-#line 2997 "cmDependsJavaParser.y"
+#line 3000 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -6097,11 +6100,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6101 "cmDependsJavaParser.cxx"
+#line 6104 "cmDependsJavaParser.cxx"
     break;
 
   case 330: /* AssignmentExpression: ConditionalExpression  */
-#line 3007 "cmDependsJavaParser.y"
+#line 3010 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6109,11 +6112,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6113 "cmDependsJavaParser.cxx"
+#line 6116 "cmDependsJavaParser.cxx"
     break;
 
   case 331: /* AssignmentExpression: Assignment  */
-#line 3016 "cmDependsJavaParser.y"
+#line 3019 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6121,11 +6124,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6125 "cmDependsJavaParser.cxx"
+#line 6128 "cmDependsJavaParser.cxx"
     break;
 
   case 332: /* Assignment: LeftHandSide AssignmentOperator AssignmentExpression  */
-#line 3026 "cmDependsJavaParser.y"
+#line 3029 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6133,11 +6136,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6137 "cmDependsJavaParser.cxx"
+#line 6140 "cmDependsJavaParser.cxx"
     break;
 
   case 333: /* LeftHandSide: Name  */
-#line 3036 "cmDependsJavaParser.y"
+#line 3039 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -6146,11 +6149,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6150 "cmDependsJavaParser.cxx"
+#line 6153 "cmDependsJavaParser.cxx"
     break;
 
   case 334: /* LeftHandSide: FieldAccess  */
-#line 3046 "cmDependsJavaParser.y"
+#line 3049 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6158,11 +6161,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6162 "cmDependsJavaParser.cxx"
+#line 6165 "cmDependsJavaParser.cxx"
     break;
 
   case 335: /* LeftHandSide: ArrayAccess  */
-#line 3055 "cmDependsJavaParser.y"
+#line 3058 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6170,11 +6173,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6174 "cmDependsJavaParser.cxx"
+#line 6177 "cmDependsJavaParser.cxx"
     break;
 
   case 336: /* AssignmentOperator: jp_EQUALS  */
-#line 3065 "cmDependsJavaParser.y"
+#line 3068 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6182,11 +6185,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6186 "cmDependsJavaParser.cxx"
+#line 6189 "cmDependsJavaParser.cxx"
     break;
 
   case 337: /* AssignmentOperator: jp_TIMESEQUALS  */
-#line 3074 "cmDependsJavaParser.y"
+#line 3077 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6194,11 +6197,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6198 "cmDependsJavaParser.cxx"
+#line 6201 "cmDependsJavaParser.cxx"
     break;
 
   case 338: /* AssignmentOperator: jp_DIVIDEEQUALS  */
-#line 3083 "cmDependsJavaParser.y"
+#line 3086 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6206,11 +6209,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6210 "cmDependsJavaParser.cxx"
+#line 6213 "cmDependsJavaParser.cxx"
     break;
 
   case 339: /* AssignmentOperator: jp_PERCENTEQUALS  */
-#line 3092 "cmDependsJavaParser.y"
+#line 3095 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6218,11 +6221,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6222 "cmDependsJavaParser.cxx"
+#line 6225 "cmDependsJavaParser.cxx"
     break;
 
   case 340: /* AssignmentOperator: jp_PLUSEQUALS  */
-#line 3101 "cmDependsJavaParser.y"
+#line 3104 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6230,11 +6233,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6234 "cmDependsJavaParser.cxx"
+#line 6237 "cmDependsJavaParser.cxx"
     break;
 
   case 341: /* AssignmentOperator: jp_MINUSEQUALS  */
-#line 3110 "cmDependsJavaParser.y"
+#line 3113 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6242,11 +6245,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6246 "cmDependsJavaParser.cxx"
+#line 6249 "cmDependsJavaParser.cxx"
     break;
 
   case 342: /* AssignmentOperator: jp_LESLESEQUALS  */
-#line 3119 "cmDependsJavaParser.y"
+#line 3122 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6254,11 +6257,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6258 "cmDependsJavaParser.cxx"
+#line 6261 "cmDependsJavaParser.cxx"
     break;
 
   case 343: /* AssignmentOperator: jp_GTGTEQUALS  */
-#line 3128 "cmDependsJavaParser.y"
+#line 3131 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6266,11 +6269,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6270 "cmDependsJavaParser.cxx"
+#line 6273 "cmDependsJavaParser.cxx"
     break;
 
   case 344: /* AssignmentOperator: jp_GTGTGTEQUALS  */
-#line 3137 "cmDependsJavaParser.y"
+#line 3140 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6278,11 +6281,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6282 "cmDependsJavaParser.cxx"
+#line 6285 "cmDependsJavaParser.cxx"
     break;
 
   case 345: /* AssignmentOperator: jp_ANDEQUALS  */
-#line 3146 "cmDependsJavaParser.y"
+#line 3149 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6290,11 +6293,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6294 "cmDependsJavaParser.cxx"
+#line 6297 "cmDependsJavaParser.cxx"
     break;
 
   case 346: /* AssignmentOperator: jp_CARROTEQUALS  */
-#line 3155 "cmDependsJavaParser.y"
+#line 3158 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6302,11 +6305,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6306 "cmDependsJavaParser.cxx"
+#line 6309 "cmDependsJavaParser.cxx"
     break;
 
   case 347: /* AssignmentOperator: jp_PIPEEQUALS  */
-#line 3164 "cmDependsJavaParser.y"
+#line 3167 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6314,11 +6317,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6318 "cmDependsJavaParser.cxx"
+#line 6321 "cmDependsJavaParser.cxx"
     break;
 
   case 348: /* Expression: AssignmentExpression  */
-#line 3174 "cmDependsJavaParser.y"
+#line 3177 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6326,11 +6329,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6330 "cmDependsJavaParser.cxx"
+#line 6333 "cmDependsJavaParser.cxx"
     break;
 
   case 349: /* ConstantExpression: Expression  */
-#line 3184 "cmDependsJavaParser.y"
+#line 3187 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6338,11 +6341,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6342 "cmDependsJavaParser.cxx"
+#line 6345 "cmDependsJavaParser.cxx"
     break;
 
   case 350: /* New: jp_NEW  */
-#line 3194 "cmDependsJavaParser.y"
+#line 3197 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6350,11 +6353,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6354 "cmDependsJavaParser.cxx"
+#line 6357 "cmDependsJavaParser.cxx"
     break;
 
   case 351: /* New: Name jp_DOT jp_NEW  */
-#line 3203 "cmDependsJavaParser.y"
+#line 3206 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -6363,11 +6366,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6367 "cmDependsJavaParser.cxx"
+#line 6370 "cmDependsJavaParser.cxx"
     break;
 
 
-#line 6371 "cmDependsJavaParser.cxx"
+#line 6374 "cmDependsJavaParser.cxx"
 
       default: break;
     }
@@ -6591,7 +6594,7 @@
   return yyresult;
 }
 
-#line 3212 "cmDependsJavaParser.y"
+#line 3215 "cmDependsJavaParser.y"
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmDependsJavaParser.y b/Source/LexerParser/cmDependsJavaParser.y
index 01d0017..7679212 100644
--- a/Source/LexerParser/cmDependsJavaParser.y
+++ b/Source/LexerParser/cmDependsJavaParser.y
@@ -44,6 +44,9 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
diff --git a/Source/LexerParser/cmExprParser.cxx b/Source/LexerParser/cmExprParser.cxx
index d30d5cf..d4168a0 100644
--- a/Source/LexerParser/cmExprParser.cxx
+++ b/Source/LexerParser/cmExprParser.cxx
@@ -112,6 +112,9 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
@@ -125,7 +128,7 @@
 #  pragma diag_suppress 550 /* variable set but never used */
 #endif
 
-#line 129 "cmExprParser.cxx"
+#line 132 "cmExprParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -567,9 +570,9 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint8 yyrline[] =
 {
-       0,    86,    86,    91,    94,    99,   102,   107,   110,   115,
-     118,   122,   128,   131,   135,   141,   144,   148,   152,   158,
-     161,   164,   168,   173,   176
+       0,    89,    89,    94,    97,   102,   105,   110,   113,   118,
+     121,   125,   131,   134,   138,   144,   147,   151,   155,   161,
+     164,   167,   171,   176,   179
 };
 #endif
 
@@ -1430,199 +1433,199 @@
   switch (yyn)
     {
   case 2: /* start: exp  */
-#line 86 "cmExprParser.y"
+#line 89 "cmExprParser.y"
       {
     cmExpr_yyget_extra(yyscanner)->SetResult((yyvsp[0].Number));
   }
-#line 1438 "cmExprParser.cxx"
+#line 1441 "cmExprParser.cxx"
     break;
 
   case 3: /* exp: bitwiseor  */
-#line 91 "cmExprParser.y"
+#line 94 "cmExprParser.y"
             {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1446 "cmExprParser.cxx"
+#line 1449 "cmExprParser.cxx"
     break;
 
   case 4: /* exp: exp exp_OR bitwiseor  */
-#line 94 "cmExprParser.y"
+#line 97 "cmExprParser.y"
                        {
     (yyval.Number) = (yyvsp[-2].Number) | (yyvsp[0].Number);
   }
-#line 1454 "cmExprParser.cxx"
+#line 1457 "cmExprParser.cxx"
     break;
 
   case 5: /* bitwiseor: bitwisexor  */
-#line 99 "cmExprParser.y"
+#line 102 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1462 "cmExprParser.cxx"
+#line 1465 "cmExprParser.cxx"
     break;
 
   case 6: /* bitwiseor: bitwiseor exp_XOR bitwisexor  */
-#line 102 "cmExprParser.y"
+#line 105 "cmExprParser.y"
                                {
     (yyval.Number) = (yyvsp[-2].Number) ^ (yyvsp[0].Number);
   }
-#line 1470 "cmExprParser.cxx"
+#line 1473 "cmExprParser.cxx"
     break;
 
   case 7: /* bitwisexor: bitwiseand  */
-#line 107 "cmExprParser.y"
+#line 110 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1478 "cmExprParser.cxx"
+#line 1481 "cmExprParser.cxx"
     break;
 
   case 8: /* bitwisexor: bitwisexor exp_AND bitwiseand  */
-#line 110 "cmExprParser.y"
+#line 113 "cmExprParser.y"
                                 {
     (yyval.Number) = (yyvsp[-2].Number) & (yyvsp[0].Number);
   }
-#line 1486 "cmExprParser.cxx"
+#line 1489 "cmExprParser.cxx"
     break;
 
   case 9: /* bitwiseand: shift  */
-#line 115 "cmExprParser.y"
+#line 118 "cmExprParser.y"
         {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1494 "cmExprParser.cxx"
+#line 1497 "cmExprParser.cxx"
     break;
 
   case 10: /* bitwiseand: bitwiseand exp_SHIFTLEFT shift  */
-#line 118 "cmExprParser.y"
+#line 121 "cmExprParser.y"
                                  {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->ShL((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1503 "cmExprParser.cxx"
+#line 1506 "cmExprParser.cxx"
     break;
 
   case 11: /* bitwiseand: bitwiseand exp_SHIFTRIGHT shift  */
-#line 122 "cmExprParser.y"
+#line 125 "cmExprParser.y"
                                   {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->ShR((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1512 "cmExprParser.cxx"
+#line 1515 "cmExprParser.cxx"
     break;
 
   case 12: /* shift: term  */
-#line 128 "cmExprParser.y"
+#line 131 "cmExprParser.y"
        {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1520 "cmExprParser.cxx"
+#line 1523 "cmExprParser.cxx"
     break;
 
   case 13: /* shift: shift exp_PLUS term  */
-#line 131 "cmExprParser.y"
+#line 134 "cmExprParser.y"
                       {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Add((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1529 "cmExprParser.cxx"
+#line 1532 "cmExprParser.cxx"
     break;
 
   case 14: /* shift: shift exp_MINUS term  */
-#line 135 "cmExprParser.y"
+#line 138 "cmExprParser.y"
                        {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Sub((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1538 "cmExprParser.cxx"
+#line 1541 "cmExprParser.cxx"
     break;
 
   case 15: /* term: unary  */
-#line 141 "cmExprParser.y"
+#line 144 "cmExprParser.y"
         {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1546 "cmExprParser.cxx"
+#line 1549 "cmExprParser.cxx"
     break;
 
   case 16: /* term: term exp_TIMES unary  */
-#line 144 "cmExprParser.y"
+#line 147 "cmExprParser.y"
                        {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Mul((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1555 "cmExprParser.cxx"
+#line 1558 "cmExprParser.cxx"
     break;
 
   case 17: /* term: term exp_DIVIDE unary  */
-#line 148 "cmExprParser.y"
+#line 151 "cmExprParser.y"
                         {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Div((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1564 "cmExprParser.cxx"
+#line 1567 "cmExprParser.cxx"
     break;
 
   case 18: /* term: term exp_MOD unary  */
-#line 152 "cmExprParser.y"
+#line 155 "cmExprParser.y"
                      {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Mod((yyvsp[-2].Number), (yyvsp[0].Number));
   }
-#line 1573 "cmExprParser.cxx"
+#line 1576 "cmExprParser.cxx"
     break;
 
   case 19: /* unary: factor  */
-#line 158 "cmExprParser.y"
+#line 161 "cmExprParser.y"
          {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1581 "cmExprParser.cxx"
+#line 1584 "cmExprParser.cxx"
     break;
 
   case 20: /* unary: exp_PLUS unary  */
-#line 161 "cmExprParser.y"
+#line 164 "cmExprParser.y"
                  {
     (yyval.Number) = + (yyvsp[0].Number);
   }
-#line 1589 "cmExprParser.cxx"
+#line 1592 "cmExprParser.cxx"
     break;
 
   case 21: /* unary: exp_MINUS unary  */
-#line 164 "cmExprParser.y"
+#line 167 "cmExprParser.y"
                   {
     (yyval.Number) = cmExpr_yyget_extra(yyscanner)
       ->Neg((yyvsp[0].Number));
   }
-#line 1598 "cmExprParser.cxx"
+#line 1601 "cmExprParser.cxx"
     break;
 
   case 22: /* unary: exp_NOT unary  */
-#line 168 "cmExprParser.y"
+#line 171 "cmExprParser.y"
                 {
     (yyval.Number) = ~ (yyvsp[0].Number);
   }
-#line 1606 "cmExprParser.cxx"
+#line 1609 "cmExprParser.cxx"
     break;
 
   case 23: /* factor: exp_NUMBER  */
-#line 173 "cmExprParser.y"
+#line 176 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1614 "cmExprParser.cxx"
+#line 1617 "cmExprParser.cxx"
     break;
 
   case 24: /* factor: exp_OPENPARENT exp exp_CLOSEPARENT  */
-#line 176 "cmExprParser.y"
+#line 179 "cmExprParser.y"
                                      {
     (yyval.Number) = (yyvsp[-1].Number);
   }
-#line 1622 "cmExprParser.cxx"
+#line 1625 "cmExprParser.cxx"
     break;
 
 
-#line 1626 "cmExprParser.cxx"
+#line 1629 "cmExprParser.cxx"
 
       default: break;
     }
@@ -1846,7 +1849,7 @@
   return yyresult;
 }
 
-#line 181 "cmExprParser.y"
+#line 184 "cmExprParser.y"
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmExprParser.y b/Source/LexerParser/cmExprParser.y
index edd3bc5..b4f7e60 100644
--- a/Source/LexerParser/cmExprParser.y
+++ b/Source/LexerParser/cmExprParser.y
@@ -37,6 +37,9 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx
index a43f4fc..5bc20b0 100644
--- a/Source/LexerParser/cmFortranParser.cxx
+++ b/Source/LexerParser/cmFortranParser.cxx
@@ -132,13 +132,16 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
 # endif
 #endif
 
-#line 142 "cmFortranParser.cxx"
+#line 145 "cmFortranParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -610,13 +613,13 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_int16 yyrline[] =
 {
-       0,   106,   106,   106,   109,   113,   118,   123,   129,   136,
-     141,   146,   150,   155,   167,   172,   177,   182,   187,   192,
-     197,   202,   207,   211,   215,   219,   223,   224,   229,   229,
-     229,   230,   230,   231,   231,   232,   232,   233,   233,   234,
-     234,   235,   235,   236,   236,   237,   237,   238,   238,   241,
-     242,   243,   244,   245,   246,   247,   248,   249,   250,   251,
-     252,   253,   254,   255,   256,   257
+       0,   109,   109,   109,   112,   116,   121,   126,   132,   139,
+     144,   149,   153,   158,   170,   175,   180,   185,   190,   195,
+     200,   205,   210,   214,   218,   222,   226,   227,   232,   232,
+     232,   233,   233,   234,   234,   235,   235,   236,   236,   237,
+     237,   238,   238,   239,   239,   240,   240,   241,   241,   244,
+     245,   246,   247,   248,   249,   250,   251,   252,   253,   254,
+     255,   256,   257,   258,   259,   260
 };
 #endif
 
@@ -1327,21 +1330,21 @@
   switch (yykind)
     {
     case YYSYMBOL_STRING: /* STRING  */
-#line 100 "cmFortranParser.y"
+#line 103 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1333 "cmFortranParser.cxx"
+#line 1336 "cmFortranParser.cxx"
         break;
 
     case YYSYMBOL_WORD: /* WORD  */
-#line 100 "cmFortranParser.y"
+#line 103 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1339 "cmFortranParser.cxx"
+#line 1342 "cmFortranParser.cxx"
         break;
 
     case YYSYMBOL_CPP_INCLUDE_ANGLE: /* CPP_INCLUDE_ANGLE  */
-#line 100 "cmFortranParser.y"
+#line 103 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1345 "cmFortranParser.cxx"
+#line 1348 "cmFortranParser.cxx"
         break;
 
       default:
@@ -1618,47 +1621,47 @@
   switch (yyn)
     {
   case 4: /* stmt: INTERFACE EOSTMT  */
-#line 109 "cmFortranParser.y"
+#line 112 "cmFortranParser.y"
                    {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
   }
-#line 1627 "cmFortranParser.cxx"
+#line 1630 "cmFortranParser.cxx"
     break;
 
   case 5: /* stmt: USE WORD other EOSTMT  */
-#line 113 "cmFortranParser.y"
+#line 116 "cmFortranParser.y"
                         {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1637 "cmFortranParser.cxx"
+#line 1640 "cmFortranParser.cxx"
     break;
 
   case 6: /* stmt: MODULE WORD EOSTMT  */
-#line 118 "cmFortranParser.y"
+#line 121 "cmFortranParser.y"
                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleModule(parser, (yyvsp[-1].string));
     free((yyvsp[-1].string));
   }
-#line 1647 "cmFortranParser.cxx"
+#line 1650 "cmFortranParser.cxx"
     break;
 
   case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD EOSTMT  */
-#line 123 "cmFortranParser.y"
+#line 126 "cmFortranParser.y"
                                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmodule(parser, (yyvsp[-3].string), (yyvsp[-1].string));
     free((yyvsp[-3].string));
     free((yyvsp[-1].string));
   }
-#line 1658 "cmFortranParser.cxx"
+#line 1661 "cmFortranParser.cxx"
     break;
 
   case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT  */
-#line 129 "cmFortranParser.y"
+#line 132 "cmFortranParser.y"
                                                       {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-5].string), (yyvsp[-3].string), (yyvsp[-1].string));
@@ -1666,50 +1669,50 @@
     free((yyvsp[-3].string));
     free((yyvsp[-1].string));
   }
-#line 1670 "cmFortranParser.cxx"
+#line 1673 "cmFortranParser.cxx"
     break;
 
   case 9: /* stmt: INTERFACE WORD EOSTMT  */
-#line 136 "cmFortranParser.y"
+#line 139 "cmFortranParser.y"
                         {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
     free((yyvsp[-1].string));
   }
-#line 1680 "cmFortranParser.cxx"
+#line 1683 "cmFortranParser.cxx"
     break;
 
   case 10: /* stmt: END INTERFACE WORD EOSTMT  */
-#line 141 "cmFortranParser.y"
+#line 144 "cmFortranParser.y"
                             {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
     free((yyvsp[-1].string));
   }
-#line 1690 "cmFortranParser.cxx"
+#line 1693 "cmFortranParser.cxx"
     break;
 
   case 11: /* stmt: END INTERFACE EOSTMT  */
-#line 146 "cmFortranParser.y"
+#line 149 "cmFortranParser.y"
                        {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
   }
-#line 1699 "cmFortranParser.cxx"
+#line 1702 "cmFortranParser.cxx"
     break;
 
   case 12: /* stmt: USE DCOLON WORD other EOSTMT  */
-#line 150 "cmFortranParser.y"
+#line 153 "cmFortranParser.y"
                                {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1709 "cmFortranParser.cxx"
+#line 1712 "cmFortranParser.cxx"
     break;
 
   case 13: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT  */
-#line 155 "cmFortranParser.y"
+#line 158 "cmFortranParser.y"
                                           {
     if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) {
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
@@ -1722,139 +1725,139 @@
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1726 "cmFortranParser.cxx"
+#line 1729 "cmFortranParser.cxx"
     break;
 
   case 14: /* stmt: INCLUDE STRING EOSTMT  */
-#line 167 "cmFortranParser.y"
+#line 170 "cmFortranParser.y"
                         {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-1].string));
     free((yyvsp[-1].string));
   }
-#line 1736 "cmFortranParser.cxx"
+#line 1739 "cmFortranParser.cxx"
     break;
 
   case 15: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT  */
-#line 172 "cmFortranParser.y"
+#line 175 "cmFortranParser.y"
                                          {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1746 "cmFortranParser.cxx"
+#line 1749 "cmFortranParser.cxx"
     break;
 
   case 16: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT  */
-#line 177 "cmFortranParser.y"
+#line 180 "cmFortranParser.y"
                                  {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1756 "cmFortranParser.cxx"
+#line 1759 "cmFortranParser.cxx"
     break;
 
   case 17: /* stmt: include STRING other EOSTMT  */
-#line 182 "cmFortranParser.y"
+#line 185 "cmFortranParser.y"
                               {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1766 "cmFortranParser.cxx"
+#line 1769 "cmFortranParser.cxx"
     break;
 
   case 18: /* stmt: define WORD other EOSTMT  */
-#line 187 "cmFortranParser.y"
+#line 190 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1776 "cmFortranParser.cxx"
+#line 1779 "cmFortranParser.cxx"
     break;
 
   case 19: /* stmt: undef WORD other EOSTMT  */
-#line 192 "cmFortranParser.y"
+#line 195 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1786 "cmFortranParser.cxx"
+#line 1789 "cmFortranParser.cxx"
     break;
 
   case 20: /* stmt: ifdef WORD other EOSTMT  */
-#line 197 "cmFortranParser.y"
+#line 200 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1796 "cmFortranParser.cxx"
+#line 1799 "cmFortranParser.cxx"
     break;
 
   case 21: /* stmt: ifndef WORD other EOSTMT  */
-#line 202 "cmFortranParser.y"
+#line 205 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1806 "cmFortranParser.cxx"
+#line 1809 "cmFortranParser.cxx"
     break;
 
   case 22: /* stmt: if other EOSTMT  */
-#line 207 "cmFortranParser.y"
+#line 210 "cmFortranParser.y"
                   {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIf(parser);
   }
-#line 1815 "cmFortranParser.cxx"
+#line 1818 "cmFortranParser.cxx"
     break;
 
   case 23: /* stmt: elif other EOSTMT  */
-#line 211 "cmFortranParser.y"
+#line 214 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElif(parser);
   }
-#line 1824 "cmFortranParser.cxx"
+#line 1827 "cmFortranParser.cxx"
     break;
 
   case 24: /* stmt: else other EOSTMT  */
-#line 215 "cmFortranParser.y"
+#line 218 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElse(parser);
   }
-#line 1833 "cmFortranParser.cxx"
+#line 1836 "cmFortranParser.cxx"
     break;
 
   case 25: /* stmt: endif other EOSTMT  */
-#line 219 "cmFortranParser.y"
+#line 222 "cmFortranParser.y"
                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleEndif(parser);
   }
-#line 1842 "cmFortranParser.cxx"
+#line 1845 "cmFortranParser.cxx"
     break;
 
   case 49: /* misc_code: WORD  */
-#line 241 "cmFortranParser.y"
+#line 244 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1848 "cmFortranParser.cxx"
+#line 1851 "cmFortranParser.cxx"
     break;
 
   case 56: /* misc_code: STRING  */
-#line 248 "cmFortranParser.y"
+#line 251 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1854 "cmFortranParser.cxx"
+#line 1857 "cmFortranParser.cxx"
     break;
 
 
-#line 1858 "cmFortranParser.cxx"
+#line 1861 "cmFortranParser.cxx"
 
       default: break;
     }
@@ -2078,6 +2081,6 @@
   return yyresult;
 }
 
-#line 260 "cmFortranParser.y"
+#line 263 "cmFortranParser.y"
 
 /* End of grammar */
diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y
index 431d6ab..77eaf75 100644
--- a/Source/LexerParser/cmFortranParser.y
+++ b/Source/LexerParser/cmFortranParser.y
@@ -57,6 +57,9 @@
 # pragma GCC diagnostic ignored "-Wconversion"
 # pragma GCC diagnostic ignored "-Wfree-nonheap-object"
 #endif
+#if defined(__GNUC__) && __GNUC__ >= 16
+# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
 #if defined(__clang__) && defined(__has_warning)
 # if __has_warning("-Wunused-but-set-variable")
 #  pragma clang diagnostic ignored "-Wunused-but-set-variable"
diff --git a/Source/LexerParser/cmFortranParserTokens.h b/Source/LexerParser/cmFortranParserTokens.h
index cb2b999..51b6a45 100644
--- a/Source/LexerParser/cmFortranParserTokens.h
+++ b/Source/LexerParser/cmFortranParserTokens.h
@@ -100,7 +100,7 @@
 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
 union YYSTYPE
 {
-#line 76 "cmFortranParser.y"
+#line 79 "cmFortranParser.y"
 
   char* string;
 
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index 9e11197..eb743bc 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -541,7 +541,8 @@
 void QCMake::loadPresets()
 {
   auto result = this->CMakePresetsGraph.ReadProjectPresets(
-    this->SourceDirectory.toStdString(), true);
+    this->SourceDirectory.toStdString(), "",
+    cmCMakePresetsGraph::ReadOption::AllowNoFiles);
   if (!result) {
     emit this->presetLoadError(
       this->SourceDirectory,
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
index 32c964c..9be5259 100644
--- a/Source/QtDialog/QCMake.h
+++ b/Source/QtDialog/QCMake.h
@@ -111,7 +111,7 @@
                            cmDiagnostics::DiagnosticAction);
   /// check if project IDE open is possible and emit openPossible signal
   void checkOpenPossible();
-  /// Reload the preset files and tree
+  /// Reload the presets files and tree
   void loadPresets();
 
 public:
diff --git a/Source/cmCMakePresetsArgs.h b/Source/cmCMakePresetsArgs.h
new file mode 100644
index 0000000..7228c9d
--- /dev/null
+++ b/Source/cmCMakePresetsArgs.h
@@ -0,0 +1,78 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+
+class cmCMakePresetsArgsBase
+{
+public:
+  virtual ~cmCMakePresetsArgsBase() = default;
+
+  virtual bool HasPresetsArg() const { return !this->PresetName.empty(); };
+  virtual void Clear() { this->PresetName.clear(); }
+
+  std::string PresetName;
+  std::string PresetsFile;
+
+protected:
+  cmCMakePresetsArgsBase() = default;
+};
+
+class cmCMakePresetsArgs : public cmCMakePresetsArgsBase
+{
+public:
+  bool HasPresetsArg() const override
+  {
+    return this->cmCMakePresetsArgsBase::HasPresetsArg() || this->ListPresets;
+  }
+
+  void Clear() override
+  {
+    this->cmCMakePresetsArgsBase::Clear();
+    this->ListPresets = false;
+  }
+
+  bool ListPresets = false;
+};
+
+class cmCMakePresetsConfigureArgs : public cmCMakePresetsArgsBase
+{
+public:
+  enum class ListPresetsOption
+  {
+    None,
+    Configure,
+    Build,
+    Test,
+    Package,
+    Workflow,
+    All,
+  };
+
+  bool HasPresetsArg() const override
+  {
+    return this->cmCMakePresetsArgsBase::HasPresetsArg() ||
+      this->ListPresets != ListPresetsOption::None;
+  }
+
+  void Clear() override
+  {
+    this->cmCMakePresetsArgsBase::Clear();
+    this->ListPresets = ListPresetsOption::None;
+  }
+
+  ListPresetsOption ListPresets = ListPresetsOption::None;
+};
+
+class cmCMakePresetsWorkflowArgs : public cmCMakePresetsArgs
+{
+public:
+  void Clear() override
+  {
+    this->cmCMakePresetsArgs::Clear();
+    this->Fresh = false;
+  }
+
+  bool Fresh = false;
+};
diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx
index 6b454ac..be3d193 100644
--- a/Source/cmCMakePresetsErrors.cxx
+++ b/Source/cmCMakePresetsErrors.cxx
@@ -80,9 +80,16 @@
   state->AddErrorAtValue("Invalid preset", value);
 }
 
-void INVALID_PRESET_NAMED(std::string const& presetName, cmJSONState* state)
+void INVALID_PRESET_NAMED(std::string const& presetName,
+                          std::string const& kind, cmJSONState* state,
+                          std::string const& detail)
 {
-  state->AddError(cmStrCat("Invalid preset: \"", presetName, '"'));
+  std::string err_msg =
+    cmStrCat("Invalid ", kind, " preset: \"", presetName, '"');
+  if (!detail.empty()) {
+    err_msg = cmStrCat(err_msg, ": ", detail);
+  }
+  state->AddError(err_msg);
 }
 
 void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state)
@@ -97,17 +104,18 @@
 }
 
 void CYCLIC_PRESET_INHERITANCE(std::string const& presetName,
-                               cmJSONState* state)
+                               std::string const& kind, cmJSONState* state)
 
 {
-  state->AddError(
-    cmStrCat("Cyclic preset inheritance for preset \"", presetName, '"'));
+  state->AddError(cmStrCat("Cyclic preset inheritance for ", kind,
+                           " preset \"", presetName, '"'));
 }
 
 void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName,
+                                            std::string const& kind,
                                             cmJSONState* state)
 {
-  state->AddError(cmStrCat("Inherited preset \"", presetName,
+  state->AddError(cmStrCat("Inherited ", kind, " preset \"", presetName,
                            "\" is unreachable from preset's file"));
 }
 
@@ -197,7 +205,7 @@
 
 void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state)
 {
-  state->AddError(cmStrCat("Cyclic include among preset files: ", file));
+  state->AddError(cmStrCat("Cyclic include among presets files: ", file));
 }
 
 void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state)
diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h
index a153d92..e8ca3e5 100644
--- a/Source/cmCMakePresetsErrors.h
+++ b/Source/cmCMakePresetsErrors.h
@@ -35,16 +35,19 @@
 
 void INVALID_PRESET(Json::Value const* value, cmJSONState* state);
 
-void INVALID_PRESET_NAMED(std::string const& presetName, cmJSONState* state);
+void INVALID_PRESET_NAMED(std::string const& presetName,
+                          std::string const& kind, cmJSONState* state,
+                          std::string const& detail);
 
 void INVALID_VARIABLE(Json::Value const* value, cmJSONState* state);
 
 void DUPLICATE_PRESETS(std::string const& presetName, cmJSONState* state);
 
 void CYCLIC_PRESET_INHERITANCE(std::string const& presetName,
-                               cmJSONState* state);
+                               std::string const& kind, cmJSONState* state);
 
 void INHERITED_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName,
+                                            std::string const& kind,
                                             cmJSONState* state);
 
 void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(std::string const& presetName,
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx
index 7e57854..98dc6c6 100644
--- a/Source/cmCMakePresetsGraph.cxx
+++ b/Source/cmCMakePresetsGraph.cxx
@@ -109,8 +109,8 @@
 {
   switch (cycleStatus[preset.Name]) {
     case CycleStatus::InProgress:
-      cmCMakePresetsErrors::CYCLIC_PRESET_INHERITANCE(preset.Name,
-                                                      &graph.parseState);
+      cmCMakePresetsErrors::CYCLIC_PRESET_INHERITANCE(
+        preset.Name, preset.kind(), &graph.parseState);
       return false;
     case CycleStatus::Verified:
       return true;
@@ -121,28 +121,32 @@
   cycleStatus[preset.Name] = CycleStatus::InProgress;
 
   if (preset.Environment.count("") != 0) {
-    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+      preset.Name, preset.kind(), &graph.parseState,
+      "Empty environment variable names are not allowed");
     return false;
   }
 
   bool result = preset.VisitPresetBeforeInherit();
   if (!result) {
-    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+      preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail);
     return false;
   }
 
   for (auto const& i : preset.Inherits) {
     auto parent = presets.find(i);
     if (parent == presets.end()) {
-      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                 &graph.parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+        preset.Name, preset.kind(), &graph.parseState,
+        cmStrCat("Could not find inherited preset \"", i, "\""));
       return false;
     }
 
     auto& parentPreset = parent->second.Unexpanded;
     if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) {
       cmCMakePresetsErrors::INHERITED_PRESET_UNREACHABLE_FROM_FILE(
-        preset.Name, &graph.parseState);
+        preset.Name, preset.kind(), &graph.parseState);
       return false;
     }
 
@@ -152,8 +156,8 @@
 
     result = preset.VisitPresetInherit(parentPreset);
     if (!result) {
-      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                 &graph.parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+        preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail);
       return false;
     }
 
@@ -171,7 +175,8 @@
   result = preset.VisitPresetAfterInherit(graph.GetVersion(preset),
                                           &graph.parseState);
   if (!result) {
-    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+      preset.Name, preset.kind(), &graph.parseState, preset.ErrorDetail);
     return false;
   }
 
@@ -453,8 +458,9 @@
       switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
                        graph->GetVersion(preset))) {
         case ExpandMacroResult::Error:
-          cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                     &graph->parseState);
+          cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+            preset.Name, preset.kind(), &graph->parseState,
+            "Invalid macro expansion");
           return false;
         case ExpandMacroResult::Ignore:
           out.reset();
@@ -471,8 +477,8 @@
     cm::optional<bool> result;
     if (!preset.ConditionEvaluator->Evaluate(
           macroExpanders, graph->GetVersion(preset), result)) {
-      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                 &graph->parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(
+        preset.Name, preset.kind(), &graph->parseState, "Invalid condition");
       return false;
     }
     if (!result) {
@@ -911,6 +917,8 @@
 {
   auto& preset = *this;
   if (preset.Environment.count("") != 0) {
+    this->ErrorDetail =
+      "Empty environment variable names are not allowed in configure presets";
     return false;
   }
 
@@ -945,6 +953,7 @@
     }
 
     if (preset.CacheVariables.count("") != 0) {
+      this->ErrorDetail = "Empty cache variable names are not allowed";
       return false;
     }
   }
@@ -977,7 +986,12 @@
 bool cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit(
   int /* version */, cmJSONState* /*stat*/)
 {
-  return this->Hidden || !this->ConfigurePreset.empty();
+  if (!this->Hidden && this->ConfigurePreset.empty()) {
+    this->ErrorDetail = "Build presets must either be hidden or have an "
+                        "associated configure preset";
+    return false;
+  }
+  return true;
 }
 
 bool cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
@@ -1087,7 +1101,12 @@
 bool cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(
   int /* version */, cmJSONState* /*state*/)
 {
-  return this->Hidden || !this->ConfigurePreset.empty();
+  if (!this->Hidden && this->ConfigurePreset.empty()) {
+    this->ErrorDetail = "Test presets must either be hidden or have an "
+                        "associated configure preset";
+    return false;
+  }
+  return true;
 }
 
 bool cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
@@ -1116,7 +1135,12 @@
 bool cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(
   int /* version */, cmJSONState* /*state*/)
 {
-  return this->Hidden || !this->ConfigurePreset.empty();
+  if (!this->Hidden && this->ConfigurePreset.empty()) {
+    this->ErrorDetail = "Package presets must either be hidden or have an "
+                        "associated configure preset";
+    return false;
+  }
+  return true;
 }
 
 bool cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit(
@@ -1142,12 +1166,13 @@
 }
 
 bool cmCMakePresetsGraph::ReadProjectPresets(std::string const& sourceDir,
-                                             bool allowNoFiles)
+                                             std::string const& presetsFile,
+                                             ReadOption readFilesOption)
 {
   this->SourceDir = cmSystemTools::CollapseFullPath(sourceDir);
   this->ClearPresets();
 
-  if (!this->ReadProjectPresetsInternal(allowNoFiles)) {
+  if (!this->ReadProjectPresetsInternal(presetsFile, readFilesOption)) {
     this->ClearPresets();
     return false;
   }
@@ -1183,33 +1208,46 @@
   return {};
 }
 
-bool cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
+bool cmCMakePresetsGraph::ReadProjectPresetsInternal(
+  std::string const& presetsFile, ReadOption readFilesOption)
 {
   bool haveOneFile = false;
 
   File* file;
-  std::string filename = GetUserFilename(this->SourceDir);
+  std::string filename;
   std::vector<File*> inProgressFiles;
-  if (cmSystemTools::FileExists(filename)) {
-    if (!this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
-                            inProgressFiles, file, this->errors)) {
-      return false;
-    }
-    haveOneFile = true;
-  } else {
-    filename = GetFilename(this->SourceDir);
+  if (!presetsFile.empty()) {
+    filename = presetsFile;
     if (cmSystemTools::FileExists(filename)) {
-      if (!this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
+      if (!this->ReadJSONFile(filename, RootType::Any, ReadReason::Root,
                               inProgressFiles, file, this->errors)) {
         return false;
       }
       haveOneFile = true;
     }
+  } else {
+    filename = GetUserFilename(this->SourceDir);
+    if (cmSystemTools::FileExists(filename)) {
+      if (!this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
+                              inProgressFiles, file, this->errors)) {
+        return false;
+      }
+      haveOneFile = true;
+    } else {
+      filename = GetFilename(this->SourceDir);
+      if (cmSystemTools::FileExists(filename)) {
+        if (!this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
+                                inProgressFiles, file, this->errors)) {
+          return false;
+        }
+        haveOneFile = true;
+      }
+    }
   }
   assert(inProgressFiles.empty());
 
   if (!haveOneFile) {
-    if (allowNoFiles) {
+    if (readFilesOption == ReadOption::AllowNoFiles) {
       return true;
     }
     cmCMakePresetsErrors::FILE_NOT_FOUND(filename, &this->parseState);
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h
index 209fd64..f04dfdd 100644
--- a/Source/cmCMakePresetsGraph.h
+++ b/Source/cmCMakePresetsGraph.h
@@ -84,6 +84,8 @@
 
     std::map<std::string, cm::optional<std::string>> Environment;
 
+    std::string ErrorDetail;
+
     virtual bool VisitPresetInherit(Preset const& parent) = 0;
     virtual bool VisitPresetBeforeInherit() { return true; }
 
@@ -453,8 +455,15 @@
     return preset.OriginFile->Version;
   }
 
-  bool ReadProjectPresets(std::string const& sourceDir,
-                          bool allowNoFiles = false);
+  enum class ReadOption
+  {
+    RequireFiles,
+    AllowNoFiles,
+  };
+
+  bool ReadProjectPresets(
+    std::string const& sourceDir, std::string const& presetsFile,
+    ReadOption readFilesOption = ReadOption::RequireFiles);
 
   std::string GetGeneratorForPreset(std::string const& presetName) const;
 
@@ -472,6 +481,7 @@
 private:
   enum class RootType
   {
+    Any,
     Project,
     User,
   };
@@ -482,7 +492,8 @@
     Included,
   };
 
-  bool ReadProjectPresetsInternal(bool allowNoFiles);
+  bool ReadProjectPresetsInternal(std::string const& presetsFile,
+                                  ReadOption readFilesOption);
   bool ReadJSONFile(std::string const& filename, RootType rootType,
                     ReadReason readReason, std::vector<File*>& inProgressFiles,
                     File*& file, std::string& errMsg);
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index e57bd9d..caf450e 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -42,6 +42,7 @@
 
 #include "cm_parse_date.h"
 
+#include "cmCMakePresetsArgs.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCTestBuildAndTest.h"
 #include "cmCTestScriptHandler.h"
@@ -1501,13 +1502,13 @@
   return false;
 }
 
-bool cmCTest::SetArgsFromPreset(std::string const& presetName,
-                                bool listPresets)
+bool cmCTest::SetArgsFromPreset(cmCMakePresetsArgs const& args)
 {
   auto const workingDirectory = cmSystemTools::GetLogicalWorkingDirectory();
 
   cmCMakePresetsGraph settingsFile;
-  auto result = settingsFile.ReadProjectPresets(workingDirectory);
+  auto result =
+    settingsFile.ReadProjectPresets(workingDirectory, args.PresetsFile);
   if (result != true) {
     cmSystemTools::Error(cmStrCat("Could not read presets from ",
                                   workingDirectory, ":\n",
@@ -1515,13 +1516,13 @@
     return false;
   }
 
-  if (listPresets) {
+  if (args.ListPresets) {
     settingsFile.PrintTestPresetList();
     return true;
   }
 
   auto resolveResult =
-    settingsFile.ResolvePreset(presetName, settingsFile.TestPresets);
+    settingsFile.ResolvePreset(args.PresetName, settingsFile.TestPresets);
   auto resolveError =
     cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::TestPreset>(
       resolveResult.StatusCode, resolveResult.ErrorPresetName,
@@ -1801,8 +1802,7 @@
   bool processSteps = false;
   bool SRArgumentSpecified = false;
   std::vector<std::pair<std::string, bool>> runScripts;
-  bool listPresets = false;
-  std::string presetName;
+  cmCMakePresetsArgs presetsArgs;
 
   // copy the command line
   cm::append(this->Impl->InitialCommandLineArguments, args);
@@ -1990,14 +1990,21 @@
 
   auto const presetArguments = std::vector<CommandArgument>{
     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
-                     [&listPresets](std::string const&) -> bool {
-                       listPresets = true;
+                     [&presetsArgs](std::string const&) -> bool {
+                       presetsArgs.ListPresets = true;
                        return true;
                      } },
     CommandArgument{ "--preset", "'--preset' requires an argument",
                      CommandArgument::Values::One,
-                     [&presetName](std::string const& presetArg) -> bool {
-                       presetName = presetArg;
+                     [&presetsArgs](std::string const& presetArg) -> bool {
+                       presetsArgs.PresetName = presetArg;
+                       return true;
+                     } },
+    CommandArgument{ "--presets-file", "'--presets-file' requires an argument",
+                     CommandArgument::Values::One,
+                     [&presetsArgs](std::string const& presetFileArg) -> bool {
+                       presetsArgs.PresetsFile =
+                         cmSystemTools::ToNormalizedPathOnDisk(presetFileArg);
                        return true;
                      } }
   };
@@ -2490,9 +2497,9 @@
     }
   }
 
-  if (listPresets || !presetName.empty()) {
-    bool success = this->SetArgsFromPreset(presetName, listPresets);
-    if (listPresets) {
+  if (presetsArgs.HasPresetsArg()) {
+    bool success = this->SetArgsFromPreset(presetsArgs);
+    if (presetsArgs.ListPresets) {
       return static_cast<int>(!success);
     }
     if (!success) {
@@ -2607,8 +2614,8 @@
     }
   }
 
-  // TestProgressOutput only supported if console supports it and not logging
-  // to a file
+  // TestProgressOutput only supported if console supports it and not
+  // logging to a file
   this->Impl->TestProgressOutput = this->Impl->TestProgressOutput &&
     !this->Impl->OutputLogFile && this->ProgressOutputSupportedByConsole();
 #ifdef _WIN32
@@ -3260,6 +3267,7 @@
 
   // CTest Test Step
   set("CTEST_TEST_TIMEOUT", "TimeOut");
+  set("CTEST_TEST_COVERAGE_TOOL", "CTestTestCoverageTool");
 
   // CTest Coverage Step
   set("CTEST_COVERAGE_COMMAND", "CoverageCommand");
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index 46c88b7..8dce347 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -20,6 +20,7 @@
 #include "cmProcessOutput.h"
 
 class cmake;
+class cmCMakePresetsArgs;
 class cmGeneratedFileStream;
 class cmInstrumentation;
 class cmMakefile;
@@ -452,7 +453,7 @@
   bool AddVariableDefinition(std::string const& arg);
 
   /** set command line arguments read from a test preset */
-  bool SetArgsFromPreset(std::string const& presetName, bool listPresets);
+  bool SetArgsFromPreset(cmCMakePresetsArgs const& args);
 
   /** returns true iff the console supports progress output */
   static bool ProgressOutputSupportedByConsole();
diff --git a/Source/cmDiagnostics.cxx b/Source/cmDiagnostics.cxx
index 60d3ec4..50d3c02 100644
--- a/Source/cmDiagnostics.cxx
+++ b/Source/cmDiagnostics.cxx
@@ -12,6 +12,28 @@
 #include "cmStringAlgorithms.h"
 
 namespace {
+
+#if __cplusplus >= 201703L
+constexpr unsigned validateDiagnosticsSubtree(unsigned parent, unsigned index)
+{
+  // Ensure that all diagnostics, starting from the specified index, have the
+  // specified parent as an ancestor. Return the first index that violates
+  // this condition.
+  while (index < cmDiagnostics::CategoryCount &&
+         cmDiagnostics::CategoryInfo[index].Parent == parent) {
+    unsigned const child = index;
+    // For each diagnostic, 'consume' its children (if any).
+    index = validateDiagnosticsSubtree(child, ++index);
+  }
+  return index;
+}
+
+static_assert(validateDiagnosticsSubtree(cmDiagnostics::CMD_NONE, 1) ==
+                cmDiagnostics::CategoryCount,
+              "Diagnostics are not properly ordered"
+              " (hint: LHS is the index of the first misordered diagnostic)");
+#endif
+
 cm::optional<cmDiagnosticCategory> stringToCategory(cm::string_view input)
 {
   using Map = std::map<cm::string_view, cmDiagnosticCategory>;
diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h
index 3ab2239..84fe098 100644
--- a/Source/cmDiagnostics.h
+++ b/Source/cmDiagnostics.h
@@ -28,6 +28,7 @@
 #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT)                          \
   SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12)                              \
   SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1)                         \
+  SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_INSTALL_ABSOLUTE_DESTINATION, 12)    \
   SELECT(ACTION, Ignore, CMD_NONE, CMD_UNINITIALIZED, 1)                      \
   SELECT(ACTION, Warn, CMD_NONE, CMD_UNUSED_CLI, 1)
 
@@ -35,7 +36,7 @@
 #define CM_FOR_EACH_DIAGNOSTIC_CATEGORY(ACTION)                               \
   CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, CM_SELECT_CATEGORY)
 
-/** \class cmDiagnostic
+/** \class cmDiagnostics
  * \brief Handles CMake diagnostic (warning) behavior
  *
  * See the cmake-diagnostics(7) manual for an overview of this class's purpose.
diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx
index 66e3730..0cc2b64 100644
--- a/Source/cmDocumentation.cxx
+++ b/Source/cmDocumentation.cxx
@@ -26,7 +26,7 @@
 #include "cmVersion.h"
 
 namespace {
-cmDocumentationEntry const cmDocumentationStandardOptions[21] = {
+cmDocumentationEntry const cmDocumentationStandardOptions[] = {
   { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." },
   { "--version[=json-v1],-version[=json-v1],/V[=json-v1],/version[=json-v1] "
     "[<file>]",
@@ -39,6 +39,12 @@
   { "--help-command-list [<file>]",
     "List commands with help available and exit." },
   { "--help-commands [<file>]", "Print cmake-commands manual and exit." },
+  { "--help-diagnostic <diag> [<file>]",
+    "Print help for one diagnostic and exit." },
+  { "--help-diagnostic-list [<file>]",
+    "List diagnostics with help available and exit." },
+  { "--help-diagnostics [<file>]",
+    "Print cmake-diagnostics manual and exit." },
   { "--help-module <mod> [<file>]", "Print help for one module and exit." },
   { "--help-module-list [<file>]",
     "List modules with help available and exit." },
@@ -164,6 +170,8 @@
       return this->PrintHelpOneManual(os);
     case cmDocumentation::OneCommand:
       return this->PrintHelpOneCommand(os);
+    case cmDocumentation::OneDiagnostic:
+      return this->PrintHelpOneDiagnostic(os);
     case cmDocumentation::OneModule:
       return this->PrintHelpOneModule(os);
     case cmDocumentation::OnePolicy:
@@ -176,6 +184,8 @@
       return this->PrintHelpListManuals(os);
     case cmDocumentation::ListCommands:
       return this->PrintHelpListCommands(os);
+    case cmDocumentation::ListDiagnostics:
+      return this->PrintHelpListDiagnostics(os);
     case cmDocumentation::ListModules:
       return this->PrintHelpListModules(os);
     case cmDocumentation::ListProperties:
@@ -377,6 +387,11 @@
       cmSystemTools::Message(
         "Warning: --help-compatcommands no longer supported");
       return true;
+    } else if (strcmp(argv[i], "--help-diagnostics") == 0) {
+      help.HelpType = cmDocumentation::OneManual;
+      help.Argument = "cmake-diagnostics.7";
+      i += int(get_opt_argument(i + 1, help.Filename));
+      this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-full") == 0) {
       help.HelpType = cmDocumentation::Full;
       i += int(get_opt_argument(i + 1, help.Filename));
@@ -393,6 +408,12 @@
       i += int(get_opt_argument(i + 1, help.Filename));
       help.Argument = cmSystemTools::LowerCase(help.Argument);
       this->WarnFormFromFilename(help, result);
+    } else if (strcmp(argv[i], "--help-diagnostic") == 0) {
+      help.HelpType = cmDocumentation::OneDiagnostic;
+      i += int(get_opt_argument(i + 1, help.Argument));
+      i += int(get_opt_argument(i + 1, help.Filename));
+      help.Argument = cmSystemTools::UpperCase(help.Argument);
+      this->WarnFormFromFilename(help, result);
     } else if (strcmp(argv[i], "--help-module") == 0) {
       help.HelpType = cmDocumentation::OneModule;
       i += int(get_opt_argument(i + 1, help.Argument));
@@ -421,6 +442,9 @@
     } else if (strcmp(argv[i], "--help-command-list") == 0) {
       help.HelpType = cmDocumentation::ListCommands;
       i += int(get_opt_argument(i + 1, help.Filename));
+    } else if (strcmp(argv[i], "--help-diagnostic-list") == 0) {
+      help.HelpType = cmDocumentation::ListDiagnostics;
+      i += int(get_opt_argument(i + 1, help.Filename));
     } else if (strcmp(argv[i], "--help-module-list") == 0) {
       help.HelpType = cmDocumentation::ListModules;
       i += int(get_opt_argument(i + 1, help.Filename));
@@ -622,6 +646,25 @@
   return true;
 }
 
+bool cmDocumentation::PrintHelpOneDiagnostic(std::ostream& os)
+{
+  std::string dname = cmSystemTools::UpperCase(this->CurrentArgument);
+  if (this->PrintFiles(os, cmStrCat("diagnostic/", dname))) {
+    return true;
+  }
+  // Argument was not a command.  Complain.
+  os << "Argument \"" << this->CurrentArgument
+     << "\" to --help-command is not a CMake diagnostic.  "
+        "Use --help-diagnostic-list to see all diagnostics.\n";
+  return false;
+}
+
+bool cmDocumentation::PrintHelpListDiagnostics(std::ostream& os)
+{
+  this->PrintNames(os, "diagnostic/*");
+  return true;
+}
+
 bool cmDocumentation::PrintHelpOneModule(std::ostream& os)
 {
   std::string mname = this->CurrentArgument;
diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h
index dd0fadb..e23cee9 100644
--- a/Source/cmDocumentation.h
+++ b/Source/cmDocumentation.h
@@ -30,6 +30,7 @@
     Full,
     ListManuals,
     ListCommands,
+    ListDiagnostics,
     ListModules,
     ListProperties,
     ListVariables,
@@ -38,6 +39,7 @@
     OneArbitrary,
     OneManual,
     OneCommand,
+    OneDiagnostic,
     OneModule,
     OneProperty,
     OneVariable,
@@ -124,12 +126,14 @@
   bool PrintHelpOneArbitrary(std::ostream& os);
   bool PrintHelpOneManual(std::ostream& os);
   bool PrintHelpOneCommand(std::ostream& os);
+  bool PrintHelpOneDiagnostic(std::ostream& os);
   bool PrintHelpOneModule(std::ostream& os);
   bool PrintHelpOnePolicy(std::ostream& os);
   bool PrintHelpOneProperty(std::ostream& os);
   bool PrintHelpOneVariable(std::ostream& os);
   bool PrintHelpListManuals(std::ostream& os);
   bool PrintHelpListCommands(std::ostream& os);
+  bool PrintHelpListDiagnostics(std::ostream& os);
   bool PrintHelpListModules(std::ostream& os);
   bool PrintHelpListProperties(std::ostream& os);
   bool PrintHelpListVariables(std::ostream& os);
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index aeb68a3..9d8311d 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -63,8 +63,8 @@
                                                openmodeApp);
   } else {
     // Generate atomically and with copy-if-different.
-    std::unique_ptr<cmGeneratedFileStream> ap(
-      new cmGeneratedFileStream(this->MainImportFile, true));
+    auto ap =
+      cm::make_unique<cmGeneratedFileStream>(this->MainImportFile, true);
     ap->SetCopyIfDifferent(true);
     foutPtr = std::move(ap);
   }
diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx
index eae42b6..2f87099 100644
--- a/Source/cmExportInstallCMakeConfigGenerator.cxx
+++ b/Source/cmExportInstallCMakeConfigGenerator.cxx
@@ -284,22 +284,21 @@
   auto configs =
     gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
 
-  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
-  auto cge =
-    ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
-
   for (auto const& config : configs) {
-    auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
-    auto dest = cmOutputConverter::EscapeForCMake(
-      unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
-    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+    cmInstallFileSetGenerator::DestinationContext result =
+      te->FileSetGenerators.at(fileSet->GetName())
+        ->GetDestination(gte, config);
+
+    std::string dest = cmOutputConverter::EscapeForCMake(
+      result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
+    if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
       dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
     }
 
     auto const& type = fileSet->GetType();
     // C++ modules do not support interface file sets which are dependent upon
     // the configuration.
-    if (cge->GetHadContextSensitiveCondition() &&
+    if (result.HadContextSensitiveCondition &&
         type == cm::FileSetMetadata::CXX_MODULES) {
       auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
       std::ostringstream e;
@@ -311,7 +310,7 @@
       return std::string{};
     }
 
-    if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
+    if (result.HadContextSensitiveCondition && configs.size() != 1) {
       resultVector.push_back(
         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
     } else {
diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx
index 78eb146..202594a 100644
--- a/Source/cmExportInstallPackageInfoGenerator.cxx
+++ b/Source/cmExportInstallPackageInfoGenerator.cxx
@@ -224,18 +224,14 @@
   cmGeneratorTarget* gte, cmTargetExport const* te,
   cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config)
 {
-  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
-  auto cge =
-    ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
+  cmInstallFileSetGenerator::DestinationContext result =
+    te->FileSetGenerators.at(fileSet->GetName())
+      ->GetDestination(gte, config.value_or(""));
 
-  std::string const unescapedDest =
-    cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
-  bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
-
-  if (config && !isConfigDependent) {
+  if (config && !result.HadContextSensitiveCondition) {
     return {};
   }
-  if (!config && isConfigDependent) {
+  if (!config && result.HadContextSensitiveCondition) {
     this->RequiresConfigFiles = true;
     return {};
   }
@@ -255,9 +251,9 @@
   }
 
   cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake(
-    unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+    result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
 
-  if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+  if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
     dest = cmStrCat("@prefix@/"_s, *dest);
   }
 
diff --git a/Source/cmExportInstallSbomGenerator.cxx b/Source/cmExportInstallSbomGenerator.cxx
index d944383..f9641a1 100644
--- a/Source/cmExportInstallSbomGenerator.cxx
+++ b/Source/cmExportInstallSbomGenerator.cxx
@@ -216,15 +216,11 @@
   cmGeneratorTarget* gte, cmTargetExport const* te,
   cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config)
 {
-  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
-  auto cge =
-    ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
+  cmInstallFileSetGenerator::DestinationContext result =
+    te->FileSetGenerators.at(fileSet->GetName())
+      ->GetDestination(gte, config.value_or(""));
 
-  std::string const unescapedDest =
-    cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte);
-  bool const isConfigDependent = cge->GetHadContextSensitiveCondition();
-
-  if (config && !isConfigDependent) {
+  if (config && !result.HadContextSensitiveCondition) {
     return {};
   }
 
@@ -241,9 +237,9 @@
   }
 
   cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake(
-    unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+    result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
 
-  if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+  if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
     dest = cmStrCat("@prefix@/"_s, *dest);
   }
 
diff --git a/Source/cmExprParserHelper.cxx b/Source/cmExprParserHelper.cxx
index 7c946f2..71c55d5 100644
--- a/Source/cmExprParserHelper.cxx
+++ b/Source/cmExprParserHelper.cxx
@@ -190,6 +190,9 @@
   if (r == 0) {
     throw std::overflow_error("divide by zero");
   }
+  if (l == INT64_MIN && r == -1) {
+    throw std::overflow_error("signed integer overflow in division");
+  }
   return l / r;
 }
 
@@ -198,6 +201,9 @@
   if (r == 0) {
     throw std::overflow_error("modulo by zero");
   }
+  if (l == INT64_MIN && r == -1) {
+    throw std::overflow_error("signed integer overflow in modulo");
+  }
   return l % r;
 }
 
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx
index 9a5e109..7427422 100644
--- a/Source/cmExtraEclipseCDT4Generator.cxx
+++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -463,7 +463,8 @@
 }
 
 void cmExtraEclipseCDT4Generator::WriteGroups(
-  SourceGroupVector const& sourceGroups, std::string& linkName,
+  SourceGroupVector const& sourceGroups,
+  cmSourceGroupFiles const& sourceGroupFiles, std::string& linkName,
   cmXMLWriter& xml)
 {
   for (auto const& sg : sourceGroups) {
@@ -475,10 +476,11 @@
       xml, linkName3, "virtual:/virtual", VirtualFolder);
     SourceGroupVector const& children = sg->GetGroupChildren();
     if (!children.empty()) {
-      this->WriteGroups(children, linkName, xml);
+      this->WriteGroups(children, sourceGroupFiles, linkName, xml);
     }
-    std::vector<cmSourceFile const*> sFiles = sg->GetSourceFiles();
-    for (cmSourceFile const* file : sFiles) {
+    std::vector<cmSourceFile const*> const& sourceFiles =
+      sourceGroupFiles.GetSourceFiles(sg.get());
+    for (cmSourceFile const* file : sourceFiles) {
       std::string const& fullPath = file->GetFullPath();
 
       if (!cmSystemTools::FileIsDirectory(fullPath)) {
@@ -521,17 +523,18 @@
             break; // skip generating the linked resources to the source files
           }
           // get the files from the source lists then add them to the groups
+          cmSourceGroupFiles sourceGroupFiles;
           std::vector<cmSourceFile*> files;
           target->GetSourceFiles(
             files, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
           for (cmSourceFile* sf : files) {
             // Add the file to the list of sources.
-            std::string const& source = sf->ResolveFullPath();
-            cmSourceGroup* sourceGroup = lg->FindSourceGroup(source);
-            sourceGroup->AssignSource(sf);
+            sourceGroupFiles.Add(lg->FindSourceGroup(sf->ResolveFullPath()),
+                                 sf);
           }
 
-          this->WriteGroups(makefile->GetSourceGroups(), linkName2, xml);
+          this->WriteGroups(makefile->GetSourceGroups(), sourceGroupFiles,
+                            linkName2, xml);
         } break;
         // ignore all others:
         default:
diff --git a/Source/cmExtraEclipseCDT4Generator.h b/Source/cmExtraEclipseCDT4Generator.h
index aa7cc25..fa92fb4 100644
--- a/Source/cmExtraEclipseCDT4Generator.h
+++ b/Source/cmExtraEclipseCDT4Generator.h
@@ -88,6 +88,7 @@
                         cmLocalGenerator& lg);
 
   void WriteGroups(SourceGroupVector const& sourceGroups,
+                   cmSourceGroupFiles const& sourceGroupFiles,
                    std::string& linkName, cmXMLWriter& xml);
   void CreateLinksToSubprojects(cmXMLWriter& xml, std::string const& baseDir);
   void CreateLinksForTargets(cmXMLWriter& xml);
diff --git a/Source/cmFastbuildNormalTargetGenerator.cxx b/Source/cmFastbuildNormalTargetGenerator.cxx
index 6aa2ee4..f450823 100644
--- a/Source/cmFastbuildNormalTargetGenerator.cxx
+++ b/Source/cmFastbuildNormalTargetGenerator.cxx
@@ -1141,10 +1141,16 @@
 std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions(
   cmSourceFile const& srcFile)
 {
+  cmGeneratorFileSet const* fileSet =
+    this->GeneratorTarget->GetFileSetForSource(Config, &srcFile);
+  cmValue const fsSkipCodeCheckVal =
+    fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr;
   cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING");
-  bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet()
-    ? srcSkipCodeCheckVal.IsOn()
-    : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING");
+  bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet()
+    ? fsSkipCodeCheckVal.IsOn()
+    : (srcSkipCodeCheckVal.IsSet()
+         ? srcSkipCodeCheckVal.IsOn()
+         : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"));
 
   if (skipCodeCheck) {
     return {};
@@ -1413,8 +1419,8 @@
     useUnity = false;
   }
 
-  // List of sources isolated from the unity build if enabled.
-  std::set<std::string> isolatedFromUnity;
+  // List of sources excluded from the unity build if enabled.
+  std::set<std::string> excludedFromUnity;
 
   // Mapping from unity group (if any) to sources belonging to that group.
   std::map<std::string, std::vector<std::string>> sourcesWithGroups;
@@ -1425,10 +1431,10 @@
     std::string const pathToFile = srcFile.GetFullPath();
     bool fileUsesUnity = useUnity;
     if (useUnity) {
-      // Check if the source should be added to "UnityInputIsolatedFiles".
+      // Check if the source should be added to "UnityInputExcludedFiles".
       if (srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION)) {
         fileUsesUnity = false;
-        isolatedFromUnity.emplace(pathToFile);
+        excludedFromUnity.emplace(pathToFile);
       }
       std::string const perFileUnityGroup =
         srcFile.GetSafeProperty(UNITY_GROUP);
@@ -1567,27 +1573,27 @@
   }
   if (useUnity) {
     target.UnityNodes =
-      GenerateUnity(objects, isolatedFromUnity, sourcesWithGroups);
+      GenerateUnity(objects, excludedFromUnity, sourcesWithGroups);
   }
 }
 
 FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity(
-  std::set<std::string> const& isolatedFiles, std::vector<std::string>& files,
+  std::set<std::string> const& excludedFiles, std::vector<std::string>& files,
   int unitySize) const
 {
   FastbuildUnityNode result;
   for (auto iter = files.begin(); iter != files.end();) {
     std::string pathToFile = std::move(*iter);
     iter = files.erase(iter);
-    // This source must be isolated
-    if (isolatedFiles.find(pathToFile) != isolatedFiles.end()) {
+    // This source must be excluded from the generated unity file.
+    if (excludedFiles.find(pathToFile) != excludedFiles.end()) {
       result.UnityInputFiles.emplace_back(pathToFile);
-      result.UnityInputIsolatedFiles.emplace_back(std::move(pathToFile));
+      result.UnityInputExcludedFiles.emplace_back(std::move(pathToFile));
     } else {
       result.UnityInputFiles.emplace_back(std::move(pathToFile));
     }
     if (int(result.UnityInputFiles.size() -
-            result.UnityInputIsolatedFiles.size()) == unitySize) {
+            result.UnityInputExcludedFiles.size()) == unitySize) {
       break;
     }
   }
@@ -1617,7 +1623,7 @@
 std::vector<FastbuildUnityNode>
 cmFastbuildNormalTargetGenerator::GenerateUnity(
   std::vector<FastbuildObjectListNode>& objects,
-  std::set<std::string> const& isolatedSources,
+  std::set<std::string> const& excludedSources,
   std::map<std::string, std::vector<std::string>> const& sourcesWithGroups)
 {
   int const unitySize = GetUnityBatchSize();
@@ -1650,14 +1656,14 @@
     // General unity batching of the remaining (non-grouped) sources.
     while (!obj.CompilerInputFiles.empty()) {
       FastbuildUnityNode node =
-        GetOneUnity(isolatedSources, obj.CompilerInputFiles, unitySize);
+        GetOneUnity(excludedSources, obj.CompilerInputFiles, unitySize);
       node.Name = cmStrCat(this->GetName(), "_Unity_", ++unityNumber);
       node.UnityOutputPath = obj.CompilerOutputPath;
       node.UnityOutputPattern = cmStrCat(node.Name, ext);
 
-      // Unity group of size 1 doesn't make sense - just isolate the source.
+      // Unity group of size 1 doesn't make sense - just exclude the source.
       if (groupedNode.UnityInputFiles.size() == 1) {
-        node.UnityInputIsolatedFiles.emplace_back(
+        node.UnityInputExcludedFiles.emplace_back(
           groupedNode.UnityInputFiles[0]);
         node.UnityInputFiles.emplace_back(
           std::move(groupedNode.UnityInputFiles[0]));
diff --git a/Source/cmFastbuildNormalTargetGenerator.h b/Source/cmFastbuildNormalTargetGenerator.h
index 8a01f77..bc630b4 100644
--- a/Source/cmFastbuildNormalTargetGenerator.h
+++ b/Source/cmFastbuildNormalTargetGenerator.h
@@ -76,14 +76,14 @@
                                       std::string& args) const;
   void GenerateCudaDeviceLink(FastbuildTarget& target) const;
   void GenerateObjects(FastbuildTarget& target);
-  FastbuildUnityNode GetOneUnity(std::set<std::string> const& isolatedFiles,
+  FastbuildUnityNode GetOneUnity(std::set<std::string> const& excludedFiles,
                                  std::vector<std::string>& files,
                                  int unitySize) const;
 
   int GetUnityBatchSize() const;
   std::vector<FastbuildUnityNode> GenerateUnity(
     std::vector<FastbuildObjectListNode>& objects,
-    std::set<std::string> const& isolatedSources,
+    std::set<std::string> const& excludedSources,
     std::map<std::string, std::vector<std::string>> const& sourcesWithGroups);
   FastbuildUnityNode GenerateGroupedUnityNode(
     std::vector<std::string>& inputFiles,
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index be816d5..ff31855 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -823,7 +823,7 @@
 // Update the following files as well when updating this constant:
 //   Help/manual/cmake-file-api.7.rst
 //   Tests/RunCMake/FileAPI/codemodel-v2-check.py (check_objects())
-static unsigned int const CodeModelV2Minor = 10;
+static unsigned int const CodeModelV2Minor = 11;
 
 void cmFileAPI::BuildClientRequestCodeModel(
   ClientRequest& r, std::vector<RequestVersion> const& versions)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 3d398b2..3627cb9 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -486,7 +486,7 @@
   void ProcessLanguages();
   void ProcessLanguage(std::string const& lang);
 
-  Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg);
+  Json::ArrayIndex AddSourceGroup(cmSourceGroup const* sg);
   CompileData BuildCompileData(cmSourceFile* sf);
   CompileData MergeCompileData(CompileData const& fd);
   Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf,
@@ -1469,7 +1469,7 @@
   }
 }
 
-Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg)
+Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup const* sg)
 {
   auto i = this->SourceGroupsMap.find(sg);
   if (i == this->SourceGroupsMap.end()) {
@@ -1495,6 +1495,9 @@
   cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT,
                                                     fd.Language);
 
+  cmGeneratorFileSet const* fileSet =
+    GT->GetFileSetForSource(this->Config, sf);
+
   std::string const COMPILE_FLAGS("COMPILE_FLAGS");
   if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) {
     std::string flags = genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
@@ -1511,6 +1514,19 @@
     BT<std::string> opt(tmp, tmpOpt.Backtrace);
     fd.Flags.emplace_back(this->ToJBT(opt));
   }
+  // File set compile options, if any
+  if (fileSet) {
+    for (BT<std::string> const& tmpOpt : fileSet->BelongsTo(this->GT)
+           ? fileSet->GetCompileOptions(this->Config, fd.Language)
+           : fileSet->GetInterfaceCompileOptions(this->Config, fd.Language)) {
+      // We need to use the AppendCompileOptions method so we handle situations
+      // where backtrace entries have list and properly escape flags.
+      std::string tmp;
+      lg->AppendCompileOptions(tmp, tmpOpt.Value);
+      BT<std::string> opt(tmp, tmpOpt.Backtrace);
+      fd.Flags.emplace_back(this->ToJBT(opt));
+    }
+  }
 
   // Add precompile headers compile options.
   std::vector<std::string> pchArchs =
@@ -1548,9 +1564,31 @@
     fd.Flags.emplace_back(this->ToJBT(opt));
   }
 
+  std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
+  // Add include directories from file set properties.
+  if (fileSet) {
+    for (BT<std::string> const& tmpInclude : fileSet->BelongsTo(this->GT)
+           ? fileSet->GetIncludeDirectories(this->Config, fd.Language)
+           : fileSet->GetInterfaceIncludeDirectories(this->Config,
+                                                     fd.Language)) {
+      // We need to use the AppendIncludeDirectories method so we handle
+      // situations where backtrace entries have lists.
+      std::vector<std::string> tmp;
+      lg->AppendIncludeDirectories(tmp, tmpInclude.Value, *sf);
+      for (std::string& i : tmp) {
+        bool const isSystemInclude =
+          this->GT->IsSystemIncludeDirectory(i, this->Config, fd.Language);
+        BT<std::string> include(i, tmpInclude.Backtrace);
+        if (this->GT->IsApple() && cmSystemTools::IsPathToFramework(i)) {
+          fd.Frameworks.emplace_back(this->ToJBT(include), isSystemInclude);
+        } else {
+          fd.Includes.emplace_back(this->ToJBT(include), isSystemInclude);
+        }
+      }
+    }
+  }
   // Add include directories from source file properties.
   {
-    std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
     for (BT<std::string> tmpInclude : sf->GetIncludeDirectories()) {
       tmpInclude.Value =
         genexInterpreter.Evaluate(tmpInclude.Value, INCLUDE_DIRECTORIES);
@@ -1597,7 +1635,25 @@
       genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS));
   }
 
-  fd.Defines.reserve(fileDefines.size() + configFileDefines.size());
+  std::set<BT<std::string>> fileSetDefines;
+  if (fileSet) {
+    for (BT<std::string> const& tmpDef : fileSet->BelongsTo(this->GT)
+           ? fileSet->GetCompileDefinitions(this->Config, fd.Language)
+           : fileSet->GetInterfaceCompileDefinitions(this->Config,
+                                                     fd.Language)) {
+      // We need to use the AppendDefines method so we handle situations where
+      // backtrace entries have lists.
+      std::set<std::string> tmp;
+      lg->AppendDefines(tmp, tmpDef.Value);
+      for (std::string const& i : tmp) {
+        BT<std::string> def(i, tmpDef.Backtrace);
+        fileSetDefines.insert(def);
+      }
+    }
+  }
+
+  fd.Defines.reserve(fileDefines.size() + configFileDefines.size() +
+                     fileSetDefines.size());
 
   for (BT<std::string> const& def : fileDefines) {
     fd.Defines.emplace_back(this->ToJBT(def));
@@ -1607,6 +1663,10 @@
     fd.Defines.emplace_back(d, JBTIndex());
   }
 
+  for (BT<std::string> const& def : fileSetDefines) {
+    fd.Defines.emplace_back(this->ToJBT(def));
+  }
+
   return fd;
 }
 
@@ -1802,7 +1862,8 @@
     source["fileSetIndex"] = fsit->second;
   }
 
-  if (cmSourceGroup* sg = this->GT->LocalGenerator->FindSourceGroup(path)) {
+  if (cmSourceGroup const* sg =
+        this->GT->LocalGenerator->FindSourceGroup(path)) {
     Json::ArrayIndex const groupIndex = this->AddSourceGroup(sg);
     source["sourceGroupIndex"] = groupIndex;
     this->SourceGroups[groupIndex].SourceIndexes.append(si);
@@ -1888,7 +1949,8 @@
     source["fileSetIndex"] = fsit->second;
   }
 
-  if (cmSourceGroup* sg = this->GT->LocalGenerator->FindSourceGroup(path)) {
+  if (cmSourceGroup const* sg =
+        this->GT->LocalGenerator->FindSourceGroup(path)) {
     Json::ArrayIndex const groupIndex = this->AddSourceGroup(sg);
     source["sourceGroupIndex"] = groupIndex;
     this->SourceGroups[groupIndex].InterfaceSourceIndexes.append(si);
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 3e98e3f..e9e8dfd 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -184,18 +184,16 @@
       }
       // ensure a macro is not specified as validator
       auto const& validatorName = args[j];
-      cmList macros{ this->Makefile->GetProperty("MACROS") };
-      if (std::find_if(macros.begin(), macros.end(),
-                       [&validatorName](std::string const& item) {
-                         return cmSystemTools::Strucmp(validatorName.c_str(),
-                                                       item.c_str()) == 0;
-                       }) != macros.end()) {
+      if (this->Makefile->GetState()
+            ->GetCommandType(validatorName)
+            .value_or(cmStateEnums::CommandType::Macro) !=
+          cmStateEnums::CommandType::Function) {
         this->SetError(cmStrCat(
           "command specified for VALIDATOR is not a function: ", args[j],
           '.'));
         return false;
       }
-      this->ValidatorName = args[j];
+      this->ValidatorName = validatorName;
     } else if (this->CheckCommonArgument(args[j])) {
       doing = DoingNone;
     } else {
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 60c3b90..456cb72 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -19,6 +19,7 @@
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
@@ -175,7 +176,7 @@
   mf.RecordPolicies(f.Policies);
   mf.RecordDiagnostics(f.Diagnostics);
   return mf.GetState()->AddScriptedCommand(
-    this->Args.front(),
+    this->Args.front(), cmStateEnums::CommandType::Function,
     BT<cmState::Command>(std::move(f),
                          mf.GetBacktrace().Push(this->GetStartingContext())),
     mf);
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 11ae1ca..53b63b1 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -2810,6 +2810,34 @@
   }
 }
 
+void cmGeneratorTarget::AddSwiftTargetFlags(std::string& flags) const
+{
+  if (cmValue version = GetProperty("Swift_LANGUAGE_VERSION")) {
+    if (cmSystemTools::VersionCompare(
+          cmSystemTools::OP_GREATER_EQUAL,
+          this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
+          "4.2")) {
+      flags += " -swift-version " + *version;
+    }
+  }
+
+  if (!this->GetGlobalGenerator()->IsXcode() &&
+      cmSystemTools::VersionCompare(
+        cmSystemTools::OP_GREATER_EQUAL,
+        this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
+        "5.8")) {
+    // Note: The Xcode generator sets the `SWIFT_PACKAGE_NAME` BuildSettings
+    //       attribute
+    std::string const packageName = this->GetSwiftPackageName();
+    if (!packageName.empty()) {
+      std::string const packageFlag =
+        this->Makefile->GetSafeDefinition("CMAKE_Swift_PACKAGE_NAME_FLAG");
+      // Add the package name to the flags
+      flags += " " + packageFlag + " " + packageName;
+    }
+  }
+}
+
 void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const
 {
   std::string const& compiler =
@@ -6171,9 +6199,26 @@
                   "_build_database.json");
 }
 
+std::string cmGeneratorTarget::GetSwiftPackageName() const
+{
+  std::string packageName;
+  if (cmValue projectName = this->GetProperty("Swift_PACKAGE_NAME")) {
+    packageName = *projectName;
+  } else if (this->GetPolicyStatusCMP0216() == cmPolicies::NEW) {
+    packageName = this->Makefile->GetSafeDefinition("PROJECT_NAME");
+  }
+  return packageName;
+}
+
 std::string cmGeneratorTarget::GetSwiftModuleName() const
 {
-  return this->GetPropertyOrDefault("Swift_MODULE_NAME", this->GetName());
+  if (cmValue name = this->GetProperty("Swift_MODULE_NAME")) {
+    return *name;
+  }
+  // Hyphens are not valid in Swift module identifiers.
+  std::string name = this->GetName();
+  std::replace(name.begin(), name.end(), '-', '_');
+  return name;
 }
 
 std::string cmGeneratorTarget::GetSwiftModuleFileName() const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index bed66b8..23f59bc 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -628,6 +628,8 @@
 
   void AddRustTargetFlags(std::string& flags) const;
 
+  void AddSwiftTargetFlags(std::string& flags) const;
+
   std::string GetFeatureSpecificLinkRuleVariable(
     std::string const& var, std::string const& lang,
     std::string const& config) const;
@@ -638,6 +640,9 @@
 
   std::string GetClangTidyExportFixesDirectory(std::string const& lang) const;
 
+  /** Return the Swift package name for this target. */
+  std::string GetSwiftPackageName() const;
+
   /** Return the swift module name for this target. */
   std::string GetSwiftModuleName() const;
 
diff --git a/Source/cmGeneratorTarget_HeaderSetVerification.cxx b/Source/cmGeneratorTarget_HeaderSetVerification.cxx
index 57d82b2..a84ad97 100644
--- a/Source/cmGeneratorTarget_HeaderSetVerification.cxx
+++ b/Source/cmGeneratorTarget_HeaderSetVerification.cxx
@@ -89,8 +89,11 @@
     std::set<cmGeneratorFileSet const*> fileSets;
     for (auto const& fileSet : fileSetEntries) {
       if (all || verifySet.count(fileSet->GetName())) {
-        fileSets.insert(fileSet);
         verifySet.erase(fileSet->GetName());
+        if (fileSet->GetProperty("SKIP_LINTING").IsOn()) {
+          continue;
+        }
+        fileSets.insert(fileSet);
       }
     }
 
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index 7497afa..db8f8ec 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -8,6 +8,7 @@
 #include "cmGlobalGenerator.h"
 #include "cmList.h"
 #include "cmMakefile.h"
+#include "cmPolicies.h"
 #include "cmState.h"
 #include "cmValue.h"
 
@@ -27,7 +28,9 @@
     if (cmValue varsProp = status.GetMakefile().GetProperty("VARIABLES")) {
       output = *varsProp;
     }
-  } else if (args[1] == "MACROS") {
+  } else if (args[1] == "MACROS" &&
+             status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) !=
+               cmPolicies::NEW) {
     output.clear();
     if (cmValue macrosProp = status.GetMakefile().GetProperty("MACROS")) {
       output = *macrosProp;
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 9c08298..5c1d1c4 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -545,7 +545,7 @@
   std::map<std::string, std::vector<cmSourceFile*>> groupFiles;
   std::set<std::string> groupNames;
   for (cmSourceFile* sf : sources) {
-    cmSourceGroup* sourceGroup =
+    cmSourceGroup const* sourceGroup =
       this->LocalGenerator->FindSourceGroup(sf->ResolveFullPath());
     std::string gn = sourceGroup->GetFullName();
     groupFiles[gn].push_back(sf);
diff --git a/Source/cmGlobalFastbuildGenerator.cxx b/Source/cmGlobalFastbuildGenerator.cxx
index 9013c5e..e906e6f 100644
--- a/Source/cmGlobalFastbuildGenerator.cxx
+++ b/Source/cmGlobalFastbuildGenerator.cxx
@@ -1201,9 +1201,9 @@
     WriteVariable("UnityOutputPath", Quote(Unity.UnityOutputPath), 2);
     WriteVariable("UnityOutputPattern", Quote(Unity.UnityOutputPattern), 2);
     WriteArray("UnityInputFiles", Wrap(Unity.UnityInputFiles), 2);
-    if (!Unity.UnityInputIsolatedFiles.empty()) {
-      WriteArray("UnityInputIsolatedFiles",
-                 Wrap(Unity.UnityInputIsolatedFiles), 2);
+    if (!Unity.UnityInputExcludedFiles.empty()) {
+      WriteArray("UnityInputExcludedFiles",
+                 Wrap(Unity.UnityInputExcludedFiles), 2);
     }
     if (UsingRelativePaths) {
       WriteVariable("UseRelativePaths_Experimental", "true", 2);
diff --git a/Source/cmGlobalFastbuildGenerator.h b/Source/cmGlobalFastbuildGenerator.h
index f2aa938..07e8772 100644
--- a/Source/cmGlobalFastbuildGenerator.h
+++ b/Source/cmGlobalFastbuildGenerator.h
@@ -233,7 +233,7 @@
   std::string UnityOutputPath;
   std::vector<std::string> UnityInputFiles;
   std::string UnityOutputPattern;
-  std::vector<std::string> UnityInputIsolatedFiles;
+  std::vector<std::string> UnityInputExcludedFiles;
   FastbuildUnityNode()
     : FastbuildTargetBase(FastbuildTargetType::UNITY)
   {
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index c273c87..bfbb24f 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -1063,7 +1063,7 @@
       item =
         cmSystemTools::CollapseFullPath(item, mf->GetCurrentSourceDirectory());
     }
-    cmSourceGroup* sg =
+    cmSourceGroup const* sg =
       cmSourceGroup::FindSourceGroup(item, mf->GetSourceGroups());
     std::string folderName = sg->GetFullName();
     if (folderName.empty()) {
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index de19966..1e467e7 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -2723,6 +2723,15 @@
           break;
       }
     }
+
+    // Add SWIFT_PACKAGE_NAME
+    if (this->XcodeVersion >= 150) {
+      std::string const packageName = gtgt->GetSwiftPackageName();
+      if (!packageName.empty()) {
+        buildSettings->AddAttribute("SWIFT_PACKAGE_NAME",
+                                    this->CreateString(packageName));
+      }
+    }
   }
 
   std::string extraLinkOptions;
@@ -4554,7 +4563,7 @@
 
       auto addSourceToGroup = [this, &gtgt,
                                &generator](std::string const& source) {
-        cmSourceGroup* sourceGroup = generator->FindSourceGroup(source);
+        cmSourceGroup const* sourceGroup = generator->FindSourceGroup(source);
         cmXCodeObject* pbxgroup =
           this->CreateOrGetPBXGroup(gtgt.get(), sourceGroup);
         std::string key = GetGroupMapKeyFromPath(gtgt.get(), source);
@@ -4630,7 +4639,7 @@
 }
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateOrGetPBXGroup(
-  cmGeneratorTarget* gtgt, cmSourceGroup* sg)
+  cmGeneratorTarget* gtgt, cmSourceGroup const* sg)
 {
   std::string s;
   std::string target;
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 82bb9d5..114caec 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -161,7 +161,7 @@
                             cm::string_view attributeValue);
 
   cmXCodeObject* CreateOrGetPBXGroup(cmGeneratorTarget* gtgt,
-                                     cmSourceGroup* sg);
+                                     cmSourceGroup const* sg);
   cmXCodeObject* CreatePBXGroup(cmXCodeObject* parent,
                                 std::string const& name);
   bool CreateGroups(std::vector<cmLocalGenerator*>& generators);
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 1fb9d4b..8137d69 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -1912,6 +1912,15 @@
   cmInstallGenerator::MessageLevel message =
     cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never);
 
+  // Check for an absolute destination.
+  if (cmGeneratorExpression::Find(*destination) == std::string::npos &&
+      cmSystemTools::FileIsFullPath(*destination)) {
+    helper.Makefile->IssueDiagnostic(
+      cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
+      cmStrCat("INSTALL command given absolute DESTINATION path (",
+               *destination, ").\n"));
+  }
+
   // Create the directory install generator.
   helper.Makefile->AddInstallGenerator(
     cm::make_unique<cmInstallDirectoryGenerator>(
diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h
index 7bf861e..c8b1ad9 100644
--- a/Source/cmInstallCommandArguments.h
+++ b/Source/cmInstallCommandArguments.h
@@ -22,7 +22,7 @@
     this->GenericArguments = args;
   }
 
-  // Compute destination path.and check permissions
+  // Compute destination path and check permissions.
   bool Finalize();
 
   std::string const& GetDestination() const;
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx
index cf1fac9..bd62dd3 100644
--- a/Source/cmInstallDirectoryGenerator.cxx
+++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -118,6 +118,9 @@
 std::string cmInstallDirectoryGenerator::GetDestination(
   std::string const& config) const
 {
-  return cmGeneratorExpression::Evaluate(this->Destination,
-                                         this->LocalGenerator, config);
+  std::string dest = cmGeneratorExpression::Evaluate(
+    this->Destination, this->LocalGenerator, config);
+  cmInstallGenerator::CheckAbsoluteDestination(dest, this->LocalGenerator,
+                                               this->Backtrace);
+  return dest;
 }
diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx
index 85b8275..e6e9f03 100644
--- a/Source/cmInstallFileSetGenerator.cxx
+++ b/Source/cmInstallFileSetGenerator.cxx
@@ -4,6 +4,7 @@
 
 #include <algorithm>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -21,6 +22,7 @@
 #include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
+#include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
@@ -91,11 +93,32 @@
   return true;
 }
 
+std::string cmInstallFileSetGenerator::GetDestination() const
+{
+  cmInstallGenerator::CheckAbsoluteDestination(
+    this->Destination, this->LocalGenerator, this->Backtrace);
+  return this->Destination;
+}
+
 std::string cmInstallFileSetGenerator::GetDestination(
   std::string const& config) const
 {
-  return cmGeneratorExpression::Evaluate(this->Destination,
-                                         this->LocalGenerator, config);
+  return this->GetDestination(this->Target, config).UnescapedDestination;
+}
+
+cmInstallFileSetGenerator::DestinationContext
+cmInstallFileSetGenerator::GetDestination(cmGeneratorTarget* gte,
+                                          std::string const& config) const
+{
+  cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance());
+  std::unique_ptr<cmCompiledGeneratorExpression> cge =
+    ge.Parse(this->Destination);
+
+  std::string const dest = cge->Evaluate(gte->LocalGenerator, config, gte);
+  cmInstallGenerator::CheckAbsoluteDestination(dest, gte->LocalGenerator,
+                                               this->Backtrace);
+
+  return { dest, cge->GetHadContextSensitiveCondition() };
 }
 
 void cmInstallFileSetGenerator::GenerateScriptForConfig(
diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h
index ff56201..b0488fb 100644
--- a/Source/cmInstallFileSetGenerator.h
+++ b/Source/cmInstallFileSetGenerator.h
@@ -9,8 +9,9 @@
 
 #include "cmInstallGenerator.h"
 
-class cmGeneratorTarget;
+class cmExportInstallCMakeConfigGenerator;
 class cmGeneratorFileSet;
+class cmGeneratorTarget;
 class cmListFileBacktrace;
 class cmLocalGenerator;
 
@@ -28,14 +29,23 @@
 
   bool Compute(cmLocalGenerator* lg) override;
 
+  struct DestinationContext
+  {
+    std::string UnescapedDestination;
+    bool HadContextSensitiveCondition;
+  };
   std::string GetDestination(std::string const& config) const;
-  std::string GetDestination() const { return this->Destination; }
+  DestinationContext GetDestination(cmGeneratorTarget* gt,
+                                    std::string const& config) const;
   bool GetOptional() const { return this->Optional; }
   std::string GetFileSetName() const { return this->FileSetName; }
   cmGeneratorFileSet const* GetFileSet() const { return this->FileSet; };
   cmGeneratorTarget* GetTarget() const { return this->Target; }
 
 protected:
+  friend cmExportInstallCMakeConfigGenerator;
+  std::string GetDestination() const;
+
   void GenerateScriptForConfig(std::ostream& os, std::string const& config,
                                Indent indent) override;
 
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx
index 6da1862..f5613fa 100644
--- a/Source/cmInstallFilesGenerator.cxx
+++ b/Source/cmInstallFilesGenerator.cxx
@@ -54,8 +54,11 @@
 std::string cmInstallFilesGenerator::GetDestination(
   std::string const& config) const
 {
-  return cmGeneratorExpression::Evaluate(this->Destination,
-                                         this->LocalGenerator, config);
+  std::string dest = cmGeneratorExpression::Evaluate(
+    this->Destination, this->LocalGenerator, config);
+  cmInstallGenerator::CheckAbsoluteDestination(dest, this->LocalGenerator,
+                                               this->Backtrace);
+  return dest;
 }
 
 std::string cmInstallFilesGenerator::GetRename(std::string const& config) const
diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx
index dd9ade7..9317f94 100644
--- a/Source/cmInstallGenerator.cxx
+++ b/Source/cmInstallGenerator.cxx
@@ -7,6 +7,9 @@
 
 #include <cm/string_view>
 
+#include "cmDiagnostics.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -229,6 +232,19 @@
   return result;
 }
 
+void cmInstallGenerator::CheckAbsoluteDestination(
+  std::string const& dest, cmLocalGenerator* lg, cmListFileBacktrace const& bt)
+{
+  if (!cmSystemTools::FileIsFullPath(dest)) {
+    return;
+  }
+  lg->IssueDiagnostic(
+    cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
+    cmStrCat("INSTALL command given absolute DESTINATION path (", dest,
+             ").\n"),
+    bt);
+}
+
 cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel(
   cmMakefile* mf, bool never)
 {
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h
index 572d192..d35a4b6 100644
--- a/Source/cmInstallGenerator.h
+++ b/Source/cmInstallGenerator.h
@@ -56,6 +56,9 @@
   /** Get the install destination as it should appear in the
       installation script.  */
   static std::string ConvertToAbsoluteDestination(std::string const& dest);
+  static void CheckAbsoluteDestination(std::string const& dest,
+                                       cmLocalGenerator* lg,
+                                       cmListFileBacktrace const& bt);
 
   /** Test if this generator installs something for a given configuration.  */
   bool InstallsForConfig(std::string const& config);
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 6b89f86..e2e7924 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -479,8 +479,11 @@
 std::string cmInstallTargetGenerator::GetDestination(
   std::string const& config) const
 {
-  return cmGeneratorExpression::Evaluate(
-    this->Destination, this->Target->GetLocalGenerator(), config);
+  cmLocalGenerator* lg = this->Target->GetLocalGenerator();
+  std::string dest =
+    cmGeneratorExpression::Evaluate(this->Destination, lg, config);
+  cmInstallGenerator::CheckAbsoluteDestination(dest, lg, this->Backtrace);
+  return dest;
 }
 
 std::string cmInstallTargetGenerator::GetInstallFilename(
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
index 1c76664..4dfeaa2 100644
--- a/Source/cmList.cxx
+++ b/Source/cmList.cxx
@@ -81,6 +81,92 @@
   cmsys::RegularExpression& Regex;
   bool const IncludeMatches;
 };
+
+// Hash of call site (FilePath:Line) for unique variable names across recursive
+// calls.
+std::string OutputVarFor(cm::string_view prefix, cmMakefile& makefile)
+{
+  cmListFileContext context = makefile.GetBacktrace().Top();
+  std::size_t hash =
+    std::hash<std::string>{}(cmStrCat(context.FilePath, ":", context.Line));
+  return cmStrCat(prefix, hash, "_");
+}
+
+class PredicateEvaluator
+{
+public:
+  PredicateEvaluator(std::string functionName, cmMakefile& makefile,
+                     std::string errorPrefix = "sub-command TRANSFORM, "
+                                               "selector PREDICATE")
+    : FunctionName(std::move(functionName))
+    , Makefile(&makefile)
+    , ErrorPrefix(std::move(errorPrefix))
+    , OutputVar(OutputVarFor("_cmake_predicate_out_", makefile))
+  {
+    if (!makefile.GetState()->GetCommand(this->FunctionName)) {
+      throw cmList::transform_error(cmStrCat(this->ErrorPrefix,
+                                             ": unknown function \"",
+                                             this->FunctionName, "\"."));
+    }
+  }
+
+  bool operator()(std::string const& value)
+  {
+    this->Makefile->RemoveDefinition(this->OutputVar);
+
+    cmListFileContext context = this->Makefile->GetBacktrace().Top();
+    std::vector<cmListFileArgument> funcArgs;
+    funcArgs.emplace_back(value, cmListFileArgument::Quoted, context.Line);
+    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
+                          context.Line);
+    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
+                             std::move(funcArgs) };
+
+    cmExecutionStatus status(*this->Makefile);
+    if (!this->Makefile->ExecuteCommand(func, status) ||
+        status.GetNestedError()) {
+      throw cmList::transform_error(
+        cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
+                 "\" failed during execution."));
+    }
+
+    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
+    if (!result) {
+      throw cmList::transform_error(
+        cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
+                 "\" did not set the output variable."));
+    }
+
+    bool boolResult = cmIsOn(*result);
+    this->Makefile->RemoveDefinition(this->OutputVar);
+    return boolResult;
+  }
+
+private:
+  std::string FunctionName;
+  cmMakefile* Makefile = nullptr;
+  std::string ErrorPrefix;
+  std::string OutputVar;
+};
+
+class MatchesPredicate
+{
+public:
+  MatchesPredicate(PredicateEvaluator& evaluator, cmList::FilterMode mode)
+    : Evaluator(evaluator)
+    , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
+  {
+  }
+
+  bool operator()(std::string const& target)
+  {
+    return this->Evaluator(target) ^ this->IncludeMatches;
+  }
+
+private:
+  PredicateEvaluator& Evaluator;
+  bool IncludeMatches;
+};
 }
 
 cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
@@ -99,6 +185,23 @@
   return *this;
 }
 
+cmList& cmList::filter(std::string const& functionName, FilterMode mode,
+                       cmMakefile& makefile)
+{
+  try {
+    PredicateEvaluator evaluator(functionName, makefile,
+                                 "sub-command FILTER, mode PREDICATE");
+
+    auto it = std::remove_if(this->Values.begin(), this->Values.end(),
+                             MatchesPredicate{ evaluator, mode });
+    this->Values.erase(it, this->Values.end());
+  } catch (transform_error& e) {
+    throw std::invalid_argument(e.what());
+  }
+
+  return *this;
+}
+
 namespace {
 class StringSorter
 {
@@ -270,6 +373,26 @@
 
   cmsys::RegularExpression Regex;
 };
+class TransformSelectorPredicate : public TransformSelector
+{
+public:
+  TransformSelectorPredicate(std::string const& functionName,
+                             cmMakefile& makefile)
+    : TransformSelector("PREDICATE")
+    , Evaluator(functionName, makefile)
+  {
+  }
+
+  bool Validate(std::size_t) override { return true; }
+
+  bool InSelection(std::string const& value) override
+  {
+    return this->Evaluator(value);
+  }
+
+private:
+  PredicateEvaluator Evaluator;
+};
 class TransformSelectorIndexes : public TransformSelector
 {
 public:
@@ -580,6 +703,7 @@
     TransformAction::Initialize(selector);
     this->FunctionName = functionName;
     this->Makefile = &makefile;
+    this->OutputVar = OutputVarFor("_cmake_transform_apply_out_", makefile);
 
     // Validate: command must exist
     if (!makefile.GetState()->GetCommand(this->FunctionName)) {
@@ -603,17 +727,15 @@
       return s;
     }
 
-    // Use a unique output variable name to avoid collisions
-    std::string const outputVar = "_list_transform_apply_out_";
-
     // Unset the output variable before calling
-    this->Makefile->RemoveDefinition(outputVar);
+    this->Makefile->RemoveDefinition(this->OutputVar);
 
     // Build the function call: functionName(s, outputVar)
     cmListFileContext context = this->Makefile->GetBacktrace().Top();
     std::vector<cmListFileArgument> funcArgs;
     funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
-    funcArgs.emplace_back(outputVar, cmListFileArgument::Quoted, context.Line);
+    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
+                          context.Line);
     cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
                              std::move(funcArgs) };
 
@@ -626,7 +748,7 @@
     }
 
     // Read back the output variable
-    cmValue result = this->Makefile->GetDefinition(outputVar);
+    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
     if (!result) {
       throw transform_error(
         cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
@@ -638,7 +760,7 @@
     std::string output = *result;
 
     // Clean up
-    this->Makefile->RemoveDefinition(outputVar);
+    this->Makefile->RemoveDefinition(this->OutputVar);
 
     return output;
   }
@@ -646,6 +768,7 @@
 private:
   std::string FunctionName;
   cmMakefile* Makefile = nullptr;
+  std::string OutputVar;
 };
 
 // Descriptor of action
@@ -827,6 +950,14 @@
   return std::unique_ptr<cmList::TransformSelector>(selector.release());
 }
 
+std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::NewPREDICATE(std::string const& functionName,
+                                        cmMakefile& makefile)
+{
+  return std::unique_ptr<cmList::TransformSelector>(
+    new TransformSelectorPredicate(functionName, makefile));
+}
+
 cmList& cmList::transform(TransformAction action,
                           std::unique_ptr<TransformSelector> selector)
 {
@@ -897,16 +1028,19 @@
                           cmMakefile& makefile,
                           std::unique_ptr<TransformSelector> selector)
 {
-  auto descriptor = TransformConfigure(action, selector, 1);
+  // Validate action and arity via the static registry.
+  TransformConfigure(action, selector, 1);
 
-  auto* applyAction =
-    static_cast<TransformActionApply*>(descriptor->Transform.get());
-  applyAction->Initialize(static_cast<::TransformSelector*>(selector.get()),
-                          arg, makefile);
+  // Create a local instance rather than reusing the singleton from
+  // Descriptors.  A user function invoked by APPLY may itself call
+  // list(TRANSFORM ... APPLY ...), which would clobber a shared instance.
+  TransformActionApply applyAction;
+  applyAction.Initialize(static_cast<::TransformSelector*>(selector.get()),
+                         arg, makefile);
 
   static_cast<::TransformSelector&>(*selector).Transform(
-    this->Values, [&descriptor](std::string const& s) -> std::string {
-      return (*descriptor->Transform)(s);
+    this->Values, [&applyAction](std::string const& s) -> std::string {
+      return applyAction(s);
     });
 
   return *this;
diff --git a/Source/cmList.h b/Source/cmList.h
index febc2c5f..b34a6ca 100644
--- a/Source/cmList.h
+++ b/Source/cmList.h
@@ -821,8 +821,11 @@
     EXCLUDE
   };
   // Includes or removes items from the list
-  // Throw std::invalid_argument if regular expression is invalid
+  // Throw std::invalid_argument if regular expression is invalid or predicate
+  // function is unknown / does not set its output variable
   cmList& filter(cm::string_view regex, FilterMode mode);
+  cmList& filter(std::string const& functionName, FilterMode mode,
+                 cmMakefile& makefile);
 
   cmList& reverse()
   {
@@ -886,6 +889,7 @@
     struct AT;
     struct FOR;
     struct REGEX;
+    struct PREDICATE;
 
     virtual ~TransformSelector() = default;
 
@@ -911,6 +915,12 @@
     template <typename Type>
     static std::unique_ptr<TransformSelector> New(std::string&&);
 
+    // NewPREDICATE is public (unlike NewAT/NewFOR/NewREGEX) because it takes
+    // a cmMakefile& parameter that cannot be dispatched through the existing
+    // New<Type>() templates.
+    static std::unique_ptr<TransformSelector> NewPREDICATE(
+      std::string const& functionName, cmMakefile& makefile);
+
     cmMakefile* Makefile = nullptr;
 
   private:
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 06eba11..e5338d2 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -520,6 +520,7 @@
   std::string const REGEX{ "REGEX" };
   std::string const AT{ "AT" };
   std::string const FOR{ "FOR" };
+  std::string const PREDICATE{ "PREDICATE" };
   std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
   std::unique_ptr<cmList::TransformSelector> selector;
   std::string outputName = listName;
@@ -527,7 +528,8 @@
   try {
     // handle optional arguments
     while (args.size() > index) {
-      if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
+      if ((args[index] == REGEX || args[index] == AT || args[index] == FOR ||
+           args[index] == PREDICATE) &&
           selector) {
         status.SetError(
           cmStrCat("sub-command TRANSFORM, selector already specified (",
@@ -653,6 +655,21 @@
         continue;
       }
 
+      // PREDICATE selector
+      if (args[index] == PREDICATE) {
+        if (args.size() == ++index) {
+          status.SetError("sub-command TRANSFORM, selector PREDICATE expects "
+                          "'function name' argument.");
+          return false;
+        }
+
+        selector = cmList::TransformSelector::NewPREDICATE(
+          args[index], status.GetMakefile());
+
+        index += 1;
+        continue;
+      }
+
       // output variable
       if (args[index] == OUTPUT_VARIABLE) {
         if (args.size() == ++index) {
@@ -951,25 +968,46 @@
   }
 
   std::string const& mode = args[3];
-  if (mode != "REGEX") {
-    status.SetError("sub-command FILTER does not recognize mode " + mode);
-    return false;
-  }
-  if (args.size() != 5) {
-    status.SetError("sub-command FILTER, mode REGEX "
-                    "requires five arguments.");
-    return false;
-  }
-  std::string const& pattern = args[4];
+  if (mode == "REGEX") {
+    if (args.size() != 5) {
+      status.SetError("sub-command FILTER, mode REGEX "
+                      "requires five arguments.");
+      return false;
+    }
+    std::string const& pattern = args[4];
 
-  try {
-    status.GetMakefile().AddDefinition(
-      listName, list->filter(pattern, filterMode).to_string());
-    return true;
-  } catch (std::invalid_argument& e) {
-    status.SetError(e.what());
-    return false;
+    try {
+      status.GetMakefile().AddDefinition(
+        listName, list->filter(pattern, filterMode).to_string());
+      return true;
+    } catch (std::invalid_argument& e) {
+      status.SetError(e.what());
+      return false;
+    }
   }
+
+  if (mode == "PREDICATE") {
+    if (args.size() != 5) {
+      status.SetError("sub-command FILTER, mode PREDICATE "
+                      "requires five arguments.");
+      return false;
+    }
+    std::string const& functionName = args[4];
+
+    try {
+      status.GetMakefile().AddDefinition(
+        listName,
+        list->filter(functionName, filterMode, status.GetMakefile())
+          .to_string());
+      return true;
+    } catch (std::invalid_argument& e) {
+      status.SetError(e.what());
+      return false;
+    }
+  }
+
+  status.SetError("sub-command FILTER does not recognize mode " + mode);
+  return false;
 }
 } // namespace
 
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 023eb2d..03dceb7 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -2115,14 +2115,7 @@
     (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom");
 
   if (lang == "Swift") {
-    if (cmValue v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
-      if (cmSystemTools::VersionCompare(
-            cmSystemTools::OP_GREATER_EQUAL,
-            this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
-            "4.2")) {
-        this->AppendFlags(flags, "-swift-version " + *v);
-      }
-    }
+    target->AddSwiftTargetFlags(flags);
   } else if (lang == "CUDA") {
     target->AddCUDAArchitectureFlags(compileOrLink, config, flags);
     target->AddCUDAToolkitFlags(flags);
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 6de9c50..fa14c0a 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -1388,6 +1388,8 @@
   AllConfigSources sources;
   sources.Sources = target->GetAllConfigSources();
 
+  cmSourceGroupFiles sourceGroupFiles;
+
   // Add CMakeLists.txt file with rule to re-run CMake for user convenience.
   if (target->GetType() != cmStateEnums::GLOBAL_TARGET &&
       target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
@@ -1425,9 +1427,7 @@
       }
     }
     // Add the file to the list of sources.
-    std::string const source = sf->GetFullPath();
-    cmSourceGroup* sourceGroup = this->FindSourceGroup(source);
-    sourceGroup->AssignSource(sf);
+    sourceGroupFiles.Add(this->FindSourceGroup(sf->GetFullPath()), sf);
   }
 
   // open the project
@@ -1440,7 +1440,8 @@
   // Loop through every source group.
   SourceGroupVector const& sourceGroups = this->Makefile->GetSourceGroups();
   for (auto const& sg : sourceGroups) {
-    this->WriteGroup(sg.get(), target, fout, libName, configs, sources);
+    this->WriteGroup(sg.get(), target, fout, libName, configs, sources,
+                     sourceGroupFiles);
   }
 
   fout << "\t</Files>\n";
@@ -1688,11 +1689,12 @@
 bool cmLocalVisualStudio7Generator::WriteGroup(
   cmSourceGroup const* sg, cmGeneratorTarget* target, std::ostream& fout,
   std::string const& libName, std::vector<std::string> const& configs,
-  AllConfigSources const& sources)
+  AllConfigSources const& sources, cmSourceGroupFiles const& sourceGroupFiles)
 {
   cmGlobalVisualStudio7Generator* gg =
     static_cast<cmGlobalVisualStudio7Generator*>(this->GlobalGenerator);
-  std::vector<cmSourceFile const*> const& sourceFiles = sg->GetSourceFiles();
+  std::vector<cmSourceFile const*> const& sourceFiles =
+    sourceGroupFiles.GetSourceFiles(sg);
   SourceGroupVector const& children = sg->GetGroupChildren();
 
   // Write the children to temporary output.
@@ -1700,7 +1702,7 @@
   std::ostringstream tmpOut;
   for (auto const& child : children) {
     if (this->WriteGroup(child.get(), target, tmpOut, libName, configs,
-                         sources)) {
+                         sources, sourceGroupFiles)) {
       hasChildrenWithSources = true;
     }
   }
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index 38df53b..885d8ca 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/Source/cmLocalVisualStudio7Generator.h
@@ -23,6 +23,7 @@
 class cmMakefile;
 class cmSourceFile;
 class cmSourceGroup;
+class cmSourceGroupFiles;
 
 class cmVS7GeneratorOptions : public cmVisualStudioGeneratorOptions
 {
@@ -147,7 +148,8 @@
   bool WriteGroup(cmSourceGroup const* sg, cmGeneratorTarget* target,
                   std::ostream& fout, std::string const& libName,
                   std::vector<std::string> const& configs,
-                  AllConfigSources const& sources);
+                  AllConfigSources const& sources,
+                  cmSourceGroupFiles const& sourceGroupFiles);
 
   friend class cmLocalVisualStudio7GeneratorFCInfo;
   friend class cmLocalVisualStudio7GeneratorInternals;
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index f193bce..ae825ef 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -19,6 +19,7 @@
 #include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmState.h"
+#include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
@@ -170,7 +171,11 @@
                                     cmExecutionStatus& status)
 {
   cmMakefile& mf = status.GetMakefile();
-  mf.AppendProperty("MACROS", this->Args[0]);
+  if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) !=
+      cmPolicies::NEW) {
+
+    mf.AppendProperty("MACROS", this->Args[0]);
+  }
   // create a new command and add it to cmake
   cmMacroHelperCommand f;
   f.Args = this->Args;
@@ -179,7 +184,7 @@
   mf.RecordPolicies(f.Policies);
   mf.RecordDiagnostics(f.Diagnostics);
   return mf.GetState()->AddScriptedCommand(
-    this->Args[0],
+    this->Args[0], cmStateEnums::CommandType::Macro,
     BT<cmState::Command>(std::move(f),
                          mf.GetBacktrace().Push(this->GetStartingContext())),
     mf);
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index d755360..11b5446 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -3870,11 +3870,11 @@
                                         bool global)
 {
   // Create the target.
-  std::unique_ptr<cmTarget> target(
-    new cmTarget(name, type,
-                 global ? cmTarget::Visibility::ImportedGlobally
-                        : cmTarget::Visibility::Imported,
-                 this, cmTarget::PerConfig::Yes));
+  auto target =
+    cm::make_unique<cmTarget>(name, type,
+                              global ? cmTarget::Visibility::ImportedGlobally
+                                     : cmTarget::Visibility::Imported,
+                              this, cmTarget::PerConfig::Yes);
 
   // Add to the set of available imported targets.
   this->ImportedTargets[name] = target.get();
@@ -3890,9 +3890,9 @@
                                        std::string const& name)
 {
   auto foreign_name = cmStrCat("@foreign_", origin, "::", name);
-  std::unique_ptr<cmTarget> target(new cmTarget(
+  auto target = cm::make_unique<cmTarget>(
     foreign_name, cmStateEnums::TargetType::INTERFACE_LIBRARY,
-    cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes));
+    cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes);
 
   this->ImportedTargets[foreign_name] = target.get();
   this->GetGlobalGenerator()->IndexTarget(target.get());
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 77b1550..31300f4 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -1107,10 +1107,14 @@
       compilerLauncher = GetCompilerLauncher(lang, config);
     }
 
+    cmValue const fsSkipCodeCheckVal =
+      fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr;
     cmValue const srcSkipCodeCheckVal = source.GetProperty("SKIP_LINTING");
-    bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet()
-      ? srcSkipCodeCheckVal.IsOn()
-      : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING");
+    bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet()
+      ? fsSkipCodeCheckVal.IsOn()
+      : (srcSkipCodeCheckVal.IsSet()
+           ? srcSkipCodeCheckVal.IsOn()
+           : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"));
 
     if (!skipCodeCheck) {
       std::string const codeCheck = this->GenerateCodeCheckRules(
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index c9ce1df..556d713 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -1041,7 +1041,8 @@
                               vars["LINK_FLAGS"], frameworkPath, linkPath,
                               genTarget);
 
-  this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, nullptr, vars);
+  this->addPoolNinjaVariable("JOB_POOL_LINK", config, genTarget, nullptr,
+                             vars);
 
   vars["MANIFESTS"] = this->GetManifests(config);
 
@@ -1384,7 +1385,7 @@
                            this->TargetLinkLanguage(config), "CURRENT", false);
   }
 
-  this->addPoolNinjaVariable("JOB_POOL_LINK", gt, nullptr, vars);
+  this->addPoolNinjaVariable("JOB_POOL_LINK", config, gt, nullptr, vars);
 
   this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
     vars["LINK_FLAGS"], this->GetGeneratorTarget(),
@@ -1652,6 +1653,14 @@
     }
   }
 
+  // For split Swift builds, ensure the link edge depends on the target's own
+  // .swiftmodule so the emit-module edge runs even when no other target in
+  // the build depends on it (e.g. install-only targets).
+  std::string swiftModuleOutput = this->GetSwiftModuleOutput(config);
+  if (!swiftModuleOutput.empty()) {
+    linkBuild.ImplicitDeps.emplace_back(std::move(swiftModuleOutput));
+  }
+
   // Ninja should restat after linking if and only if there are byproducts.
   vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
 
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index bda7740..a07fe1c 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -1438,6 +1438,7 @@
   this->Configs[config].SwiftOutputMap[""] = deps;
 
   cmGeneratedFileStream output(mapFilePath);
+  output.SetCopyIfDifferent(true);
   output << this->Configs[config].SwiftOutputMap;
 
   // Add flag
@@ -1584,10 +1585,17 @@
 
   auto compilerLauncher = this->GetCompilerLauncher(language, config);
 
+  cmGeneratorFileSet const* fileSet =
+    this->GeneratorTarget->GetFileSetForSource(config, source);
+
+  cmValue const fsSkipCodeCheckVal =
+    fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr;
   cmValue const srcSkipCodeCheckVal = source->GetProperty("SKIP_LINTING");
-  bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet()
-    ? srcSkipCodeCheckVal.IsOn()
-    : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING");
+  bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet()
+    ? fsSkipCodeCheckVal.IsOn()
+    : (srcSkipCodeCheckVal.IsSet()
+         ? srcSkipCodeCheckVal.IsOn()
+         : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING"));
 
   if (!skipCodeCheck) {
     auto const cmakeCmd =
@@ -1779,8 +1787,9 @@
       scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi");
     }
 
-    this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
-                               source, ppBuild.Variables);
+    this->addPoolNinjaVariable("JOB_POOL_COMPILE", config,
+                               this->GetGeneratorTarget(), source,
+                               ppBuild.Variables);
 
     this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
                                            ppBuild, commandLineLengthLimit);
@@ -1813,13 +1822,13 @@
     this->ConvertToOutputFormatForShell(targetSupportDir);
   vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(objectFileDir);
 
-  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
-                             source, vars);
+  this->addPoolNinjaVariable("JOB_POOL_COMPILE", config,
+                             this->GetGeneratorTarget(), source, vars);
 
   if (!pchSources.empty() && !source->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
     auto pchIt = pchSources.find(source->GetFullPath());
     if (pchIt != pchSources.end()) {
-      this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER",
+      this->addPoolNinjaVariable("JOB_POOL_PRECOMPILE_HEADER", config,
                                  this->GetGeneratorTarget(), nullptr, vars);
     }
   }
@@ -2016,8 +2025,9 @@
       scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi");
     }
 
-    this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
-                               source, ppBuild.Variables);
+    this->addPoolNinjaVariable("JOB_POOL_COMPILE", config,
+                               this->GetGeneratorTarget(), source,
+                               ppBuild.Variables);
 
     this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
                                            ppBuild, commandLineLengthLimit);
@@ -2045,8 +2055,8 @@
     this->ConvertToOutputFormatForShell(targetSupportDir);
   vars["OBJECT_FILE_DIR"] = this->ConvertToOutputFormatForShell(bmiFileDir);
 
-  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
-                             source, vars);
+  this->addPoolNinjaVariable("JOB_POOL_COMPILE", config,
+                             this->GetGeneratorTarget(), source, vars);
 
   bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp");
 
@@ -2200,8 +2210,11 @@
   objBuild.RspFile = cmStrCat(targetObjectFilename, ".swift.rsp");
 
   // Importable targets keep -emit-module on compile so swiftc still emits
-  // .swiftdoc.  When splitting module emission, only the .swiftmodule output
-  // moves to the separate emit-module edge.
+  // .swiftdoc.  When splitting module emission, both the .swiftmodule output
+  // and -emit-module flags move entirely to the separate emit-module edge.
+  if (targetIsImportable) {
+    this->Configs[config].SwiftModuleOutput = moduleFilepath;
+  }
   if (targetIsImportable && !emitModuleSeparately) {
     objBuild.Outputs.push_back(moduleFilepath);
   }
@@ -2240,7 +2253,8 @@
   std::string const moduleOutputPath =
     this->LocalGenerator->ConvertToOutputFormat(moduleFilepath,
                                                 cmOutputConverter::SHELL);
-  if (targetIsImportable) {
+  if (targetIsImportable && !emitModuleSeparately &&
+      commonFlags.find("-emit-module-path") == std::string::npos) {
     std::string const emitModuleFlag = "-emit-module";
     std::string const modulePathFlag = "-emit-module-path";
     this->LocalGenerator->AppendFlags(
@@ -2275,11 +2289,6 @@
 
   objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
 
-  // Write object build
-  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
-                                         objBuild,
-                                         this->ForceResponseFile() ? -1 : 0);
-
   // Write a separate emit-module build edge that produces .swiftmodule
   // without compile outputs. This allows downstream Swift targets to start
   // compiling as soon as the module interface is ready, overlapping with
@@ -2291,10 +2300,14 @@
     // Start from common flags (shared with compile edge) and add
     // emit-module-specific flags.  The emit-module rule template already
     // contains -emit-module, so we only need -emit-module-path here.
+    // Skip if the flags already contain one (e.g. a directory-style path
+    // set by the target's compile options).
     modBuild.Variables["FLAGS"] = commonFlags;
-    this->LocalGenerator->AppendFlags(
-      modBuild.Variables["FLAGS"],
-      cmStrCat("-emit-module-path ", moduleOutputPath));
+    if (commonFlags.find("-emit-module-path") == std::string::npos) {
+      this->LocalGenerator->AppendFlags(
+        modBuild.Variables["FLAGS"],
+        cmStrCat("-emit-module-path ", moduleOutputPath));
+    }
 
     modBuild.RspFile = cmStrCat(moduleFilepath, ".rsp");
 
@@ -2306,7 +2319,16 @@
     this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
                                            modBuild,
                                            this->ForceResponseFile() ? -1 : 0);
+
+    // Both edges share the same -output-file-map; serialize the compile
+    // edge after emit-module so they do not race on the module .swiftdeps.
+    objBuild.OrderOnlyDeps.push_back(moduleFilepath);
   }
+
+  // Write object build
+  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
+                                         objBuild,
+                                         this->ForceResponseFile() ? -1 : 0);
 }
 
 void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
@@ -2668,6 +2690,16 @@
   return {};
 }
 
+std::string cmNinjaTargetGenerator::GetSwiftModuleOutput(
+  std::string const& config) const
+{
+  auto const it = this->Configs.find(config);
+  if (it != this->Configs.end()) {
+    return it->second.SwiftModuleOutput;
+  }
+  return {};
+}
+
 void cmNinjaTargetGenerator::EnsureDirectoryExists(
   std::string const& path) const
 {
@@ -2745,15 +2777,22 @@
 }
 
 void cmNinjaTargetGenerator::addPoolNinjaVariable(
-  std::string const& pool_property, cmGeneratorTarget* target,
-  cmSourceFile const* source, cmNinjaVars& vars)
+  std::string const& pool_property, std::string const& config,
+  cmGeneratorTarget* target, cmSourceFile const* source, cmNinjaVars& vars)
 {
-  // First check the current source properties, then if not found, its target
-  // ones. Allows to override a target-wide compile pool with a source-specific
-  // one.
+  // First check file set properties, then if not found the current source
+  // properties, then if not found, its target ones. Allows to override a
+  // target-wide compile pool with file set-specific or source-specific one.
   cmValue pool = {};
   if (source) {
-    pool = source->GetProperty(pool_property);
+    cmGeneratorFileSet const* fileSet =
+      target->GetFileSetForSource(config, source);
+    if (fileSet) {
+      pool = fileSet->GetProperty(pool_property);
+    }
+    if (!pool) {
+      pool = source->GetProperty(pool_property);
+    }
   }
   if (!pool) {
     pool = target->GetProperty(pool_property);
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index c706e75..a019df2 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -216,6 +216,7 @@
   void AdditionalCleanFiles(std::string const& config);
 
   cmNinjaDeps GetObjects(std::string const& config) const;
+  std::string GetSwiftModuleOutput(std::string const& config) const;
 
   void EnsureDirectoryExists(std::string const& dir) const;
   void EnsureParentDirectoryExists(std::string const& path) const;
@@ -246,6 +247,7 @@
 
   /// @param source may be nullptr.
   void addPoolNinjaVariable(std::string const& pool_property,
+                            std::string const& config,
                             cmGeneratorTarget* target,
                             cmSourceFile const* source, cmNinjaVars& vars);
 
@@ -278,6 +280,8 @@
     Json::Value SwiftOutputMap;
     cmNinjaDeps ExtraFiles;
     std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator;
+    // Path declared as the .swiftmodule output (compile or emit-module edge).
+    std::string SwiftModuleOutput;
   };
 
   std::map<std::string, ByConfig> Configs;
diff --git a/Source/cmPkgConfigResolver.cxx b/Source/cmPkgConfigResolver.cxx
index 54598c8..4d7ef27 100644
--- a/Source/cmPkgConfigResolver.cxx
+++ b/Source/cmPkgConfigResolver.cxx
@@ -227,7 +227,7 @@
 }
 
 cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolveStrict(
-  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
+  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env)
 {
   cm::optional<cmPkgConfigResult> result;
   cmPkgConfigResult config;
@@ -289,11 +289,11 @@
 }
 
 cm::optional<cmPkgConfigResult> cmPkgConfigResolver::ResolvePermissive(
-  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
+  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env)
 {
   cm::optional<cmPkgConfigResult> result;
 
-  cmPkgConfigResult config = ResolveBestEffort(entries, std::move(env));
+  cmPkgConfigResult config = ResolveBestEffort(entries, env);
   auto const& keys = config.Keywords;
 
   if (keys.find("Name") == keys.end() ||
@@ -307,7 +307,7 @@
 }
 
 cmPkgConfigResult cmPkgConfigResolver::ResolveBestEffort(
-  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env)
+  std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env)
 {
   cmPkgConfigResult result;
 
diff --git a/Source/cmPkgConfigResolver.h b/Source/cmPkgConfigResolver.h
index e1d9d0a..992429a 100644
--- a/Source/cmPkgConfigResolver.h
+++ b/Source/cmPkgConfigResolver.h
@@ -99,13 +99,13 @@
 
 public:
   static cm::optional<cmPkgConfigResult> ResolveStrict(
-    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env);
+    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
 
   static cm::optional<cmPkgConfigResult> ResolvePermissive(
-    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env);
+    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
 
   static cmPkgConfigResult ResolveBestEffort(
-    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv env);
+    std::vector<cmPkgConfigEntry> const& entries, cmPkgConfigEnv& env);
 
   static cmPkgConfigVersionReq ParseVersion(std::string const& version);
 
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 4259c62..1ef7f10 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -645,7 +645,12 @@
          0, WARN)                                                             \
   SELECT(POLICY, CMP0215,                                                     \
          "Ninja generators emit Swift modules separately from compilation.",  \
-         4, 4, 0, WARN)
+         4, 4, 0, WARN)                                                       \
+  SELECT(POLICY, CMP0216, "Swift targets have a default project name.", 4, 4, \
+         0, WARN)                                                             \
+  SELECT(POLICY, CMP0217,                                                     \
+         "The MACROS directory property does not exist anymore.", 4, 4, 0,    \
+         WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -703,7 +708,8 @@
   F(CMP0210)                                                                  \
   F(CMP0211)                                                                  \
   F(CMP0214)                                                                  \
-  F(CMP0215)
+  F(CMP0215)                                                                  \
+  F(CMP0216)
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \
@@ -735,14 +741,15 @@
 
     /** \brief Always the last entry.
      *
-     * Useful mostly to avoid adding a comma the last policy when adding a new
-     * one.
+     * Useful mostly to avoid adding a comma the last policy when adding
+     * a new one.
      */
     CMPCOUNT
   };
 
   //! convert a string policy ID into a number
-  static bool GetPolicyID(char const* id, /* out */ cmPolicies::PolicyID& pid);
+  static bool GetPolicyID(char const* id,
+                          /* out */ cmPolicies::PolicyID& pid);
 
   //! Return whether a policy has been removed.
   static bool IsRemoved(cmPolicies::PolicyID id);
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index e490370..0d3f3bc 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -20,7 +20,7 @@
   : OS(os)
   , DocRoot(std::move(docroot))
   , CMakeDirective("^.. (cmake:)?("
-                   "command|envvar|genex|signature|variable"
+                   "command|diagnostic|envvar|genex|signature|variable"
                    ")::")
   , CMakeModuleDirective("^.. cmake-module::[ \t]+([^ \t\n]+)$")
   , ParsedLiteralDirective("^.. parsed-literal::[ \t]*(.*)$")
@@ -34,7 +34,7 @@
   , ModuleRST(R"(^#\[(=*)\[\.rst:$)")
   , CMakeRole("(:cmake)?:("
               "cref|"
-              "command|cpack_gen|generator|genex|"
+              "command|cpack_gen|diagnostic|generator|genex|"
               "variable|envvar|module|policy|"
               "prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|"
               "prop_test|prop_tgt|"
diff --git a/Source/cmSetDirectoryPropertiesCommand.cxx b/Source/cmSetDirectoryPropertiesCommand.cxx
index cdfd068..024c76b 100644
--- a/Source/cmSetDirectoryPropertiesCommand.cxx
+++ b/Source/cmSetDirectoryPropertiesCommand.cxx
@@ -4,6 +4,7 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmPolicies.h"
 
 // cmSetDirectoryPropertiesCommand
 bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args,
@@ -27,7 +28,9 @@
         "Variables and cache variables should be set using SET command");
       return false;
     }
-    if (prop == "MACROS") {
+    if (prop == "MACROS" &&
+        status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) !=
+          cmPolicies::NEW) {
       status.SetError(
         "Commands and macros cannot be set using SET_CMAKE_PROPERTIES");
       return false;
diff --git a/Source/cmSourceGroup.cxx b/Source/cmSourceGroup.cxx
index 7251810..3b5feea 100644
--- a/Source/cmSourceGroup.cxx
+++ b/Source/cmSourceGroup.cxx
@@ -7,7 +7,6 @@
 #include <cm/memory>
 
 #include "cmGeneratorExpression.h"
-#include "cmSourceFile.h"
 #include "cmStringAlgorithms.h"
 
 class cmSourceGroupInternals
@@ -85,21 +84,11 @@
   return this->GroupFiles.find(name) != this->GroupFiles.cend();
 }
 
-void cmSourceGroup::AssignSource(cmSourceFile const* sf)
-{
-  this->SourceFiles.push_back(sf);
-}
-
 std::set<std::string> const& cmSourceGroup::GetGroupFiles() const
 {
   return this->GroupFiles;
 }
 
-std::vector<cmSourceFile const*> const& cmSourceGroup::GetSourceFiles() const
-{
-  return this->SourceFiles;
-}
-
 void cmSourceGroup::AddChild(std::unique_ptr<cmSourceGroup> child)
 {
   this->Internal->GroupChildren.push_back(std::move(child));
@@ -196,3 +185,19 @@
   // Shouldn't get here, but just in case, return the default group.
   return groups.data()->get();
 }
+
+void cmSourceGroupFiles::Add(cmSourceGroup const* sg, cmSourceFile const* sf)
+{
+  this->SourceFiles[sg].push_back(sf);
+}
+
+std::vector<cmSourceFile const*> const& cmSourceGroupFiles::GetSourceFiles(
+  cmSourceGroup const* sg) const
+{
+  auto i = this->SourceFiles.find(sg);
+  if (i != this->SourceFiles.end()) {
+    return i->second;
+  }
+  static std::vector<cmSourceFile const*> const empty;
+  return empty;
+}
diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h
index 4ce79ed..ff7d23f 100644
--- a/Source/cmSourceGroup.h
+++ b/Source/cmSourceGroup.h
@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
 #include <memory>
 #include <set>
 #include <string>
@@ -100,22 +101,10 @@
   cmSourceGroup* MatchChildrenRegex(std::string const& name) const;
 
   /**
-   * Assign the given source file to this group.  Used only by
-   * generators.
-   */
-  void AssignSource(cmSourceFile const* sf);
-
-  /**
    * Get the set of file names explicitly added to this source group.
    */
   std::set<std::string> const& GetGroupFiles() const;
 
-  /**
-   * Get the list of the source files that have been assigned to this
-   * source group.
-   */
-  std::vector<cmSourceFile const*> const& GetSourceFiles() const;
-
   SourceGroupVector const& GetGroupChildren() const;
 
   /**
@@ -142,11 +131,18 @@
    */
   std::set<std::string> GroupFiles;
 
-  /**
-   * Vector of all source files that have been assigned to
-   * this group.
-   */
-  std::vector<cmSourceFile const*> SourceFiles;
-
   std::unique_ptr<cmSourceGroupInternals> Internal;
 };
+
+/** \class cmSourceGroup
+ * \brief Used by generators to organize a target's sources into groups.
+ */
+class cmSourceGroupFiles
+{
+  std::map<cmSourceGroup const*, std::vector<cmSourceFile const*>> SourceFiles;
+
+public:
+  void Add(cmSourceGroup const* sg, cmSourceFile const* sf);
+  std::vector<cmSourceFile const*> const& GetSourceFiles(
+    cmSourceGroup const* sg) const;
+};
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index e458b4c..ecfc0a2 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -414,7 +414,10 @@
 {
   assert(name == cmSystemTools::LowerCase(name));
   assert(this->BuiltinCommands.find(name) == this->BuiltinCommands.end());
-  this->BuiltinCommands.emplace(name, std::move(command));
+  this->BuiltinCommands.emplace(
+    name,
+    CommandDescriptor{ cmStateEnums::CommandType::Function,
+                       std::move(command) });
 }
 
 static bool InvokeBuiltinCommand(cmState::BuiltinCommand command,
@@ -522,8 +525,31 @@
   this->AddUnexpectedCommand(name, error);
 }
 
-bool cmState::AddScriptedCommand(std::string const& name, BT<Command> command,
-                                 cmMakefile& mf)
+cmState::CommandDescriptor::CommandDescriptor(CommandType type,
+                                              Command command)
+  : Type(type)
+  , Script(std::move(command))
+{
+}
+cmState::CommandDescriptor::CommandDescriptor(
+  CommandDescriptor&& descriptor) noexcept
+  : Type(descriptor.Type)
+  , Script(std::move(descriptor.Script))
+{
+}
+
+cmState::CommandDescriptor& cmState::CommandDescriptor::operator=(
+  CommandDescriptor&& descriptor) noexcept
+{
+  this->Type = descriptor.Type;
+  this->Script = std::move(descriptor.Script);
+
+  return *this;
+}
+
+bool cmState::AddScriptedCommand(std::string const& name,
+                                 cmStateEnums::CommandType type,
+                                 BT<Command> command, cmMakefile& mf)
 {
   std::string sName = cmSystemTools::LowerCase(name);
 
@@ -538,30 +564,60 @@
   }
 
   // if the command already exists, give a new name to the old command.
-  if (Command oldCmd = this->GetCommandByExactName(sName)) {
-    this->ScriptedCommands["_" + sName] = oldCmd;
+  if (CommandDescriptor const* oldCmd =
+        this->GetCommandDescriptorByExactName(sName)) {
+    this->ScriptedCommands["_" + sName] = *oldCmd;
   }
 
-  this->ScriptedCommands[sName] = std::move(command.Value);
+  this->ScriptedCommands[sName] =
+    CommandDescriptor{ type, std::move(command.Value) };
   return true;
 }
 
+cmState::CommandDescriptor const* cmState::GetCommandDescriptorByExactName(
+  std::string const& name) const
+{
+  auto pos = this->ScriptedCommands.find(name);
+  if (pos != this->ScriptedCommands.end()) {
+    return &pos->second;
+  }
+  pos = this->BuiltinCommands.find(name);
+  if (pos != this->BuiltinCommands.end()) {
+    return &pos->second;
+  }
+  return nullptr;
+}
+
 cmState::Command cmState::GetCommand(std::string const& name) const
 {
   return this->GetCommandByExactName(cmSystemTools::LowerCase(name));
 }
+cm::optional<cmStateEnums::CommandType> cmState::GetCommandType(
+  std::string const& name) const
+{
+  return this->GetCommandTypeByExactName(cmSystemTools::LowerCase(name));
+}
 
 cmState::Command cmState::GetCommandByExactName(std::string const& name) const
 {
-  auto pos = this->ScriptedCommands.find(name);
-  if (pos != this->ScriptedCommands.end()) {
-    return pos->second;
+  CommandDescriptor const* descriptor =
+    this->GetCommandDescriptorByExactName(name);
+  if (!descriptor) {
+    return nullptr;
   }
-  pos = this->BuiltinCommands.find(name);
-  if (pos != this->BuiltinCommands.end()) {
-    return pos->second;
+
+  return descriptor->Script;
+}
+cm::optional<cmStateEnums::CommandType> cmState::GetCommandTypeByExactName(
+  std::string const& name) const
+{
+  CommandDescriptor const* descriptor =
+    this->GetCommandDescriptorByExactName(name);
+  if (!descriptor) {
+    return cm::nullopt;
   }
-  return nullptr;
+
+  return descriptor->Type;
 }
 
 std::vector<std::string> cmState::GetCommandNames() const
diff --git a/Source/cmState.h b/Source/cmState.h
index a25342f..5d8a1e7 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -172,8 +172,14 @@
 
   // Returns a command from its name, case insensitive, or nullptr
   Command GetCommand(std::string const& name) const;
+  // Returns a command type from its name, case insensitive, or cm::nullopt
+  cm::optional<cmStateEnums::CommandType> GetCommandType(
+    std::string const& name) const;
   // Returns a command from its name, or nullptr
   Command GetCommandByExactName(std::string const& name) const;
+  // Returns a command type from its name, or cm::nullopt
+  cm::optional<cmStateEnums::CommandType> GetCommandTypeByExactName(
+    std::string const& name) const;
 
   void AddBuiltinCommand(std::string const& name, Command command);
   void AddBuiltinCommand(std::string const& name, BuiltinCommand command);
@@ -186,7 +192,8 @@
   void AddUnexpectedCommand(std::string const& name, char const* error);
   void AddUnexpectedFlowControlCommand(std::string const& name,
                                        char const* error);
-  bool AddScriptedCommand(std::string const& name, BT<Command> command,
+  bool AddScriptedCommand(std::string const& name,
+                          cmStateEnums::CommandType type, BT<Command> command,
                           cmMakefile& mf);
   void RemoveBuiltinCommand(std::string const& name);
   void RemoveUserDefinedCommands();
@@ -277,8 +284,32 @@
 
   cmPropertyDefinitionMap PropertyDefinitions;
   std::vector<std::string> EnabledLanguages;
-  std::unordered_map<std::string, Command> BuiltinCommands;
-  std::unordered_map<std::string, Command> ScriptedCommands;
+
+  class CommandDescriptor
+  {
+  public:
+    using CommandType = cmStateEnums::CommandType;
+
+    CommandDescriptor()
+      : Type(CommandType::Macro)
+      , Script(nullptr)
+    {
+    }
+    CommandDescriptor(CommandType type, Command command);
+    CommandDescriptor(CommandDescriptor&& descriptor) noexcept;
+
+    CommandDescriptor& operator=(CommandDescriptor&& descriptor) noexcept;
+    CommandDescriptor& operator=(CommandDescriptor const& descriptor) =
+      default;
+
+    CommandType Type;
+    Command Script;
+  };
+  CommandDescriptor const* GetCommandDescriptorByExactName(
+    std::string const& name) const;
+
+  std::unordered_map<std::string, CommandDescriptor> BuiltinCommands;
+  std::unordered_map<std::string, CommandDescriptor> ScriptedCommands;
   std::unordered_set<std::string> FlowControlCommands;
   cmPropertyMap GlobalProperties;
   std::unique_ptr<cmCacheManager> CacheManager;
diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h
index 6e2d456..1b7043f 100644
--- a/Source/cmStateTypes.h
+++ b/Source/cmStateTypes.h
@@ -41,6 +41,12 @@
   UNWINDING
 };
 
+enum class CommandType
+{
+  Macro,
+  Function
+};
+
 // There are multiple overlapping ranges represented here. Be aware that adding
 // a value to this enumeration may cause failures in numerous places which
 // assume details about the ordering.
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 966091a..9e846d5 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -226,9 +226,9 @@
   {
   }
   cmAlphaNum(char ch)
-    : View_(this->Digits_, 1)
   {
     this->Digits_[0] = ch;
+    this->View_ = cm::string_view(this->Digits_, 1);
   }
   cmAlphaNum(int val);
   cmAlphaNum(unsigned int val);
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 8ec4cb0..6bb6935 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -2084,6 +2084,9 @@
   static Resolver<Policies::LogicalPath> const resolver(RealOS);
 #endif
   resolver.Resolve(std::move(p), p);
+#ifdef __clang_analyzer__ /* cplusplus.Move */
+  p.clear();
+#endif
   return p;
 }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 6622957..71da193 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -2007,14 +2007,16 @@
   std::set<cmSourceGroup const*> groupsUsed;
   for (cmGeneratorTarget::AllConfigSource const& si : sources) {
     std::string const& source = si.Source->GetFullPath();
-    cmSourceGroup* sourceGroup = this->LocalGenerator->FindSourceGroup(source);
+    cmSourceGroup const* sourceGroup =
+      this->LocalGenerator->FindSourceGroup(source);
     groupsUsed.insert(sourceGroup);
   }
 
   if (cmSourceFile const* srcCMakeLists =
         this->LocalGenerator->CreateVCProjBuildRule()) {
     std::string const& source = srcCMakeLists->GetFullPath();
-    cmSourceGroup* sourceGroup = this->LocalGenerator->FindSourceGroup(source);
+    cmSourceGroup const* sourceGroup =
+      this->LocalGenerator->FindSourceGroup(source);
     groupsUsed.insert(sourceGroup);
   }
 
@@ -2173,7 +2175,8 @@
   for (ToolSource const& s : sources) {
     cmSourceFile const* sf = s.SourceFile;
     std::string const& source = sf->GetFullPath();
-    cmSourceGroup* sourceGroup = this->LocalGenerator->FindSourceGroup(source);
+    cmSourceGroup const* sourceGroup =
+      this->LocalGenerator->FindSourceGroup(source);
     std::string const& filter = sourceGroup->GetFullName();
     std::string path = this->ConvertPath(source, s.RelativePath);
     ConvertToWindowsSlash(path);
@@ -6075,7 +6078,7 @@
   std::string const& fullFileName = source->GetFullPath();
   std::string const& srcDir = this->Makefile->GetCurrentSourceDirectory();
   std::string const& binDir = this->Makefile->GetCurrentBinaryDirectory();
-  cmSourceGroup* sourceGroup =
+  cmSourceGroup const* sourceGroup =
     this->LocalGenerator->FindSourceGroup(fullFileName);
   if (sourceGroup && !sourceGroup->GetFullName().empty()) {
     sourceGroupedFile =
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index a51374f..2918cd1 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -86,6 +86,7 @@
 #  include <cm3p/curl/curl.h>
 #  include <cm3p/json/writer.h>
 
+#  include "cmCMakePresetsArgs.h"
 #  include "cmConfigureLog.h"
 #  include "cmFileAPI.h"
 #  include "cmGraphVizWriter.h"
@@ -1014,9 +1015,9 @@
 #if !defined(CMAKE_BOOTSTRAP)
   std::string profilingFormat;
   std::string profilingOutput;
-  std::string presetName;
 
-  ListPresets listPresets = ListPresets::None;
+  cmCMakePresetsConfigureArgs presetsArgs;
+  using ListPresets = cmCMakePresetsConfigureArgs::ListPresetsOption;
 #endif
 
   auto EmptyStringArgLambda = [](std::string const&, cmake* state) -> bool {
@@ -1429,24 +1430,31 @@
   arguments.emplace_back("--preset", "No preset specified for --preset",
                          CommandArgument::Values::One,
                          [&](std::string const& value, cmake*) -> bool {
-                           presetName = value;
+                           presetsArgs.PresetName = value;
                            return true;
                          });
   arguments.emplace_back(
+    "--presets-file", "No file specified for --presets-file",
+    CommandArgument::Values::One,
+    [&presetsArgs](std::string const& value, cmake*) -> bool {
+      presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value);
+      return true;
+    });
+  arguments.emplace_back(
     "--list-presets", CommandArgument::Values::ZeroOrOne,
     [&](std::string const& value, cmake*) -> bool {
       if (value.empty() || value == "configure") {
-        listPresets = ListPresets::Configure;
+        presetsArgs.ListPresets = ListPresets::Configure;
       } else if (value == "build") {
-        listPresets = ListPresets::Build;
+        presetsArgs.ListPresets = ListPresets::Build;
       } else if (value == "test") {
-        listPresets = ListPresets::Test;
+        presetsArgs.ListPresets = ListPresets::Test;
       } else if (value == "package") {
-        listPresets = ListPresets::Package;
+        presetsArgs.ListPresets = ListPresets::Package;
       } else if (value == "workflow") {
-        listPresets = ListPresets::Workflow;
+        presetsArgs.ListPresets = ListPresets::Workflow;
       } else if (value == "all") {
-        listPresets = ListPresets::All;
+        presetsArgs.ListPresets = ListPresets::All;
       } else {
         cmSystemTools::Error(
           "Invalid value specified for --list-presets.\n"
@@ -1573,7 +1581,7 @@
 #ifdef CMAKE_BOOTSTRAP
     false;
 #else
-    !presetName.empty();
+    !presetsArgs.PresetName.empty();
 #endif
 
   if (this->State->GetRole() == cmState::Role::Project && !haveSourceDir &&
@@ -1593,8 +1601,8 @@
   }
 
 #if !defined(CMAKE_BOOTSTRAP)
-  if (listPresets != ListPresets::None || !presetName.empty()) {
-    this->SetArgsFromPreset(presetName, listPresets, haveBArg);
+  if (presetsArgs.HasPresetsArg()) {
+    this->SetArgsFromPreset(presetsArgs, haveBArg);
   }
 #endif
 }
@@ -2009,11 +2017,14 @@
 }
 
 #ifndef CMAKE_BOOTSTRAP
-bool cmake::SetArgsFromPreset(std::string const& presetName,
-                              ListPresets listPresets, bool haveBinaryDirArg)
+bool cmake::SetArgsFromPreset(cmCMakePresetsConfigureArgs const& args,
+                              bool haveBinaryDirArg)
 {
+  using ListPresets = cmCMakePresetsConfigureArgs::ListPresetsOption;
+
   cmCMakePresetsGraph presetsGraph;
-  auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
+  auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory(),
+                                                args.PresetsFile);
   if (result != true) {
     std::string errorMsg =
       cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":\n",
@@ -2022,19 +2033,28 @@
     return false;
   }
 
-  if (listPresets != ListPresets::None) {
-    if (listPresets == ListPresets::Configure) {
-      this->PrintPresetList(presetsGraph);
-    } else if (listPresets == ListPresets::Build) {
-      presetsGraph.PrintBuildPresetList();
-    } else if (listPresets == ListPresets::Test) {
-      presetsGraph.PrintTestPresetList();
-    } else if (listPresets == ListPresets::Package) {
-      presetsGraph.PrintPackagePresetList();
-    } else if (listPresets == ListPresets::Workflow) {
-      presetsGraph.PrintWorkflowPresetList();
-    } else if (listPresets == ListPresets::All) {
-      presetsGraph.PrintAllPresets();
+  if (args.ListPresets != ListPresets::None) {
+    switch (args.ListPresets) {
+      case ListPresets::Configure:
+        this->PrintPresetList(presetsGraph);
+        break;
+      case ListPresets::Build:
+        presetsGraph.PrintBuildPresetList();
+        break;
+      case ListPresets::Test:
+        presetsGraph.PrintTestPresetList();
+        break;
+      case ListPresets::Package:
+        presetsGraph.PrintPackagePresetList();
+        break;
+      case ListPresets::Workflow:
+        presetsGraph.PrintWorkflowPresetList();
+        break;
+      case ListPresets::All:
+        presetsGraph.PrintAllPresets();
+        break;
+      default:
+        break;
     }
 
     this->State->SetRoleToHelpForListPresets();
@@ -2042,7 +2062,7 @@
   }
 
   auto resolveResult =
-    presetsGraph.ResolvePreset(presetName, presetsGraph.ConfigurePresets);
+    presetsGraph.ResolvePreset(args.PresetName, presetsGraph.ConfigurePresets);
   using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
   using S = cmCMakePresetsGraph::PresetResolveStatus;
   auto resolveError = cmCMakePresetsGraph::FormatPresetError<ConfigurePreset>(
@@ -3897,13 +3917,14 @@
 
 int cmake::Build(cmBuildArgs buildArgs, std::vector<std::string> targets,
                  std::vector<std::string> nativeOptions,
-                 cmBuildOptions& buildOptions, std::string const& presetName,
-                 bool listPresets, std::vector<std::string> const& args)
+                 cmBuildOptions& buildOptions,
+                 cmCMakePresetsArgs const& presetsArgs,
+                 std::vector<std::string> const& args)
 {
   buildArgs.timeout = cmDuration::zero();
 
 #if !defined(CMAKE_BOOTSTRAP)
-  if (!presetName.empty() || listPresets) {
+  if (presetsArgs.HasPresetsArg()) {
     // If the binary directory was specified, use it to find
     // the source directory so we can locate the presets file.
     if (!buildArgs.binaryDir.empty() &&
@@ -3916,7 +3937,8 @@
         cmSystemTools::GetLogicalWorkingDirectory());
     }
     cmCMakePresetsGraph settingsFile;
-    auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
+    auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory(),
+                                                  presetsArgs.PresetsFile);
     if (result != true) {
       cmSystemTools::Error(
         cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
@@ -3924,13 +3946,13 @@
       return 1;
     }
 
-    if (listPresets) {
+    if (presetsArgs.ListPresets) {
       settingsFile.PrintBuildPresetList();
       return 0;
     }
 
-    auto resolveResult =
-      settingsFile.ResolvePreset(presetName, settingsFile.BuildPresets);
+    auto resolveResult = settingsFile.ResolvePreset(presetsArgs.PresetName,
+                                                    settingsFile.BuildPresets);
     auto resolveError =
       cmCMakePresetsGraph::FormatPresetError<cmCMakePresetsGraph::BuildPreset>(
         resolveResult.StatusCode, resolveResult.ErrorPresetName,
@@ -4261,8 +4283,7 @@
 }
 #endif
 
-int cmake::Workflow(std::string const& presetName,
-                    WorkflowListPresets listPresets, WorkflowFresh fresh)
+int cmake::Workflow(cmCMakePresetsWorkflowArgs const& args)
 {
   int exitStatus = 0;
 #ifndef CMAKE_BOOTSTRAP
@@ -4270,7 +4291,8 @@
   this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory());
 
   cmCMakePresetsGraph settingsFile;
-  auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
+  auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory(),
+                                                args.PresetsFile);
   if (result != true) {
     cmSystemTools::Error(cmStrCat("Could not read presets from ",
                                   this->GetHomeDirectory(), ":\n",
@@ -4278,24 +4300,24 @@
     return 1;
   }
 
-  if (listPresets == WorkflowListPresets::Yes) {
+  if (args.ListPresets) {
     settingsFile.PrintWorkflowPresetList();
     return 0;
   }
 
-  auto presetPair = settingsFile.WorkflowPresets.find(presetName);
+  auto presetPair = settingsFile.WorkflowPresets.find(args.PresetName);
   if (presetPair == settingsFile.WorkflowPresets.end()) {
     cmSystemTools::Error(cmStrCat("No such workflow preset in ",
-                                  this->GetHomeDirectory(), ": \"", presetName,
-                                  '"'));
+                                  this->GetHomeDirectory(), ": \"",
+                                  args.PresetName, '"'));
     settingsFile.PrintWorkflowPresetList();
     return 1;
   }
 
   if (presetPair->second.Unexpanded.Hidden) {
     cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ",
-                                  this->GetHomeDirectory(), ": \"", presetName,
-                                  '"'));
+                                  this->GetHomeDirectory(), ": \"",
+                                  args.PresetName, '"'));
     settingsFile.PrintWorkflowPresetList();
     return 1;
   }
@@ -4303,15 +4325,16 @@
   auto const& expandedPreset = presetPair->second.Expanded;
   if (!expandedPreset) {
     cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"",
-                                  presetName, "\": Invalid macro expansion"));
+                                  args.PresetName,
+                                  "\": Invalid macro expansion"));
     settingsFile.PrintWorkflowPresetList();
     return 1;
   }
 
   if (!expandedPreset->ConditionResult) {
     cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ",
-                                  this->GetHomeDirectory(), ": \"", presetName,
-                                  '"'));
+                                  this->GetHomeDirectory(), ": \"",
+                                  args.PresetName, '"'));
     settingsFile.PrintWorkflowPresetList();
     return 1;
   }
@@ -4347,13 +4370,14 @@
         if (!configurePreset) {
           return 1;
         }
-        std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(),
-                                       "--preset", step.PresetName };
-        if (fresh == WorkflowFresh::Yes) {
-          args.emplace_back("--fresh");
+        std::vector<std::string> configureCmdArgs{
+          cmSystemTools::GetCMakeCommand(), "--preset", step.PresetName
+        };
+        if (args.Fresh) {
+          configureCmdArgs.emplace_back("--fresh");
         }
         steps.emplace_back(stepNumber, "configure"_s, step.PresetName,
-                           buildWorkflowStep(args));
+                           buildWorkflowStep(configureCmdArgs));
       } break;
       case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: {
         auto const* buildPreset = this->FindPresetForWorkflow(
diff --git a/Source/cmake.h b/Source/cmake.h
index c2a0f05..900cbb4 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -48,6 +48,9 @@
 #endif
 
 class cmExternalMakefileProjectGeneratorFactory;
+class cmCMakePresetsArgs;
+class cmCMakePresetsConfigureArgs;
+class cmCMakePresetsWorkflowArgs;
 class cmFileAPI;
 class cmInstrumentation;
 class cmFileTimeCache;
@@ -254,19 +257,8 @@
   bool CreateAndSetGlobalGenerator(std::string const& name);
 
 #ifndef CMAKE_BOOTSTRAP
-  enum class ListPresets
-  {
-    None,
-    Configure,
-    Build,
-    Test,
-    Package,
-    Workflow,
-    All,
-  };
-
-  bool SetArgsFromPreset(std::string const& presetName,
-                         ListPresets listPresets, bool haveBinaryDirArg);
+  bool SetArgsFromPreset(cmCMakePresetsConfigureArgs const& args,
+                         bool haveBinaryDirArg);
 
   void PrintPresetList(cmCMakePresetsGraph const& graph) const;
 #endif
@@ -627,8 +619,9 @@
   //! run the --build option
   int Build(cmBuildArgs buildArgs, std::vector<std::string> targets,
             std::vector<std::string> nativeOptions,
-            cmBuildOptions& buildOptions, std::string const& presetName,
-            bool listPresets, std::vector<std::string> const& args);
+            cmBuildOptions& buildOptions,
+            cmCMakePresetsArgs const& presetsArgs,
+            std::vector<std::string> const& args);
 
   enum class DryRun
   {
@@ -640,18 +633,7 @@
   bool Open(std::string const& dir, DryRun dryRun);
 
   //! run the --workflow option
-  enum class WorkflowListPresets
-  {
-    No,
-    Yes,
-  };
-  enum class WorkflowFresh
-  {
-    No,
-    Yes,
-  };
-  int Workflow(std::string const& presetName, WorkflowListPresets listPresets,
-               WorkflowFresh fresh);
+  int Workflow(cmCMakePresetsWorkflowArgs const& args);
 
   void UnwatchUnusedCli(std::string const& var);
   void WatchUnusedCli(std::string const& var);
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 7b325ee..d7f0c88 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -44,6 +44,7 @@
 #include "cmcmd.h"
 
 #ifndef CMAKE_BOOTSTRAP
+#  include "cmCMakePresetsArgs.h"
 #  include "cmDocumentation.h"
 #endif
 
@@ -75,6 +76,8 @@
 
 cmDocumentationEntry const cmDocumentationOptions[] = {
   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
+  { "--presets-file <file>,--presets-file=<file>",
+    "Specify the path to a presets file." },
   { "--list-presets[=<type>]", "List available presets." },
   { "--workflow [<options>]", "Run a workflow preset." },
   { "-E", "CMake command mode. Run \"cmake -E\" for a summary of commands." },
@@ -474,8 +477,7 @@
   bool foundNonClean = false;
   PackageResolveMode resolveMode = PackageResolveMode::Default;
   buildArgs.verbose = cmSystemTools::HasEnv("VERBOSE");
-  std::string presetName;
-  bool listPresets = false;
+  cmCMakePresetsArgs presetsArgs;
 
   auto jLambda = extract_job_number_lambda_builder(buildArgs.binaryDir,
                                                    buildArgs.jobs, "-j");
@@ -516,6 +518,10 @@
     buildArgs.verbose = true;
     return true;
   };
+  auto presetFileLambda = [&](std::string const& value) -> bool {
+    presetsArgs.PresetsFile = cmSystemTools::ToNormalizedPathOnDisk(value);
+    return true;
+  };
 
   using CommandArgument =
     cmCommandLineArgument<bool(std::string const& value)>;
@@ -523,9 +529,11 @@
   std::vector<CommandArgument> arguments = {
     CommandArgument{ "--preset", "No preset specified for --preset",
                      CommandArgument::Values::One,
-                     CommandArgument::setToValue(presetName) },
+                     CommandArgument::setToValue(presetsArgs.PresetName) },
+    CommandArgument{ "--presets-file", "No file specified for --presets-file",
+                     CommandArgument::Values::One, presetFileLambda },
     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(listPresets) },
+                     CommandArgument::setToTrue(presetsArgs.ListPresets) },
     CommandArgument{ "-j", CommandArgument::Values::ZeroOrOne,
                      CommandArgument::RequiresSeparator::No, jLambda },
     CommandArgument{ "--parallel", CommandArgument::Values::ZeroOrOne,
@@ -622,7 +630,7 @@
     }
   }
 
-  if (buildArgs.binaryDir.empty() && presetName.empty() && !listPresets) {
+  if (buildArgs.binaryDir.empty() && !presetsArgs.HasPresetsArg()) {
     /* clang-format off */
     std::cerr <<
       "Usage: cmake --build <dir>            "
@@ -633,6 +641,8 @@
       "  <dir>          = Project binary directory to be built.\n"
       "  --preset <preset>, --preset=<preset>\n"
       "                 = Specify a build preset.\n"
+      "  --presets-file <file>, --presets-file=<file>\n"
+      "                 = Specify the path to a presets file.\n"
       "  --list-presets[=<type>]\n"
       "                 = List available build presets.\n"
       "  --parallel [<jobs>], -j [<jobs>]\n"
@@ -672,7 +682,7 @@
   std::vector<std::string> cmd;
   cm::append(cmd, av, av + ac);
   return cm.Build(buildArgs, std::move(targets), std::move(nativeOptions),
-                  buildOptions, presetName, listPresets, cmd);
+                  buildOptions, presetsArgs, cmd);
 #endif
 }
 
@@ -980,11 +990,7 @@
   std::cerr << "This cmake does not support --workflow\n";
   return -1;
 #else
-  using WorkflowListPresets = cmake::WorkflowListPresets;
-  using WorkflowFresh = cmake::WorkflowFresh;
-  std::string presetName;
-  auto listPresets = WorkflowListPresets::No;
-  auto fresh = WorkflowFresh::No;
+  cmCMakePresetsWorkflowArgs presetsArgs;
 
   using CommandArgument =
     cmCommandLineArgument<bool(std::string const& value)>;
@@ -992,17 +998,18 @@
   std::vector<CommandArgument> arguments = {
     CommandArgument{ "--preset", "No preset specified for --preset",
                      CommandArgument::Values::One,
-                     CommandArgument::setToValue(presetName) },
+                     CommandArgument::setToValue(presetsArgs.PresetName) },
+    CommandArgument{ "--presets-file", "No file specified for --presets-file",
+                     CommandArgument::Values::One,
+                     [&presetsArgs](std::string const& value) -> bool {
+                       presetsArgs.PresetsFile =
+                         cmSystemTools::ToNormalizedPathOnDisk(value);
+                       return true;
+                     } },
     CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
-                     [&listPresets](std::string const&) -> bool {
-                       listPresets = WorkflowListPresets::Yes;
-                       return true;
-                     } },
+                     CommandArgument::setToTrue(presetsArgs.ListPresets) },
     CommandArgument{ "--fresh", CommandArgument::Values::Zero,
-                     [&fresh](std::string const&) -> bool {
-                       fresh = WorkflowFresh::Yes;
-                       return true;
-                     } },
+                     CommandArgument::setToTrue(presetsArgs.Fresh) }
   };
 
   std::vector<std::string> inputArgs;
@@ -1029,23 +1036,23 @@
     }
     if (!(matched && parsed)) {
       if (!matched) {
-        presetName.clear();
-        listPresets = WorkflowListPresets::No;
+        presetsArgs.Clear();
         std::cerr << "Unknown argument " << arg << std::endl;
       }
       break;
     }
   }
 
-  if (presetName.empty() && listPresets == WorkflowListPresets::No) {
+  if (!presetsArgs.HasPresetsArg()) {
     /* clang-format off */
     std::cerr <<
       "Usage: cmake --workflow <options>\n"
       "Options:\n"
-      "  --preset <preset> = Workflow preset to execute.\n"
-      "  --list-presets    = List available workflow presets.\n"
-      "  --fresh           = Configure a fresh build tree, removing any "
-                            "existing cache file.\n"
+      "  --preset <preset>     = Workflow preset to execute.\n"
+      "  --presets-file <file>  = Path to a presets file.\n"
+      "  --list-presets        = List available workflow presets.\n"
+      "  --fresh               = Configure a fresh build tree, removing any "
+                                "existing cache file.\n"
       ;
     /* clang-format on */
     return 1;
@@ -1060,7 +1067,7 @@
     cmakemainProgressCallback(msg, prog, &cm);
   });
 
-  return cm.Workflow(presetName, listPresets, fresh);
+  return cm.Workflow(presetsArgs);
 #endif
 }
 
diff --git a/Templates/CTestScript.cmake.in b/Templates/CTestScript.cmake.in
index 54bfda4..cd79a23 100644
--- a/Templates/CTestScript.cmake.in
+++ b/Templates/CTestScript.cmake.in
@@ -48,6 +48,7 @@
 set(CTEST_RESOURCE_SPEC_FILE "@CTEST_RESOURCE_SPEC_FILE@")
 set(CTEST_TEST_LOAD "@CTEST_TEST_LOAD@")
 set(CTEST_TEST_TIMEOUT "@DART_TESTING_TIMEOUT@")
+set(CTEST_TEST_COVERAGE_TOOL "@COVERAGE_TOOL@"
 
 # CTest Coverage Step
 set(CTEST_COVERAGE_COMMAND "@COVERAGE_COMMAND@")
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 7787b08..210363e 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -430,6 +430,7 @@
   string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_CUDA "${CMake_TEST_CUDA_STANDARDS}")
   string(REPLACE ";" "$<SEMICOLON>" TEST_STDS_HIP "${CMake_TEST_HIP_STANDARDS}")
   set(CompileFeatures_BUILD_OPTIONS
+    -DCMake_TEST_CXX=${CMake_TEST_CXX}
     -DCMake_TEST_C_STANDARDS=${TEST_STDS_C}
     -DCMake_TEST_CXX_STANDARDS=${TEST_STDS_CXX}
     -DCMake_TEST_CUDA=${CMake_TEST_CUDA}
@@ -439,6 +440,7 @@
     )
   ADD_TEST_MACRO(CompileFeatures CompileFeatures)
   set_property(TEST CompileFeatures APPEND PROPERTY LABELS "CUDA" "HIP")
+  set_property(TEST CompileFeatures APPEND PROPERTY LABELS "PellesC")
 
   ADD_TEST_MACRO(CMakeCommands.target_compile_features)
 
diff --git a/Tests/CMakeTests/ProcessorCountTest.cmake.in b/Tests/CMakeTests/ProcessorCountTest.cmake.in
index f92dcc4..a2595c7 100644
--- a/Tests/CMakeTests/ProcessorCountTest.cmake.in
+++ b/Tests/CMakeTests/ProcessorCountTest.cmake.in
@@ -1,5 +1,8 @@
 include(ProcessorCount)
 
+set(CMAKE_EXECUTE_PROCESS_COMMAND_ERROR_IS_FATAL ANY)
+# ProcessorCount() must tolerate execute_process() failures
+
 ProcessorCount(processor_count)
 
 message("### 1. This line should be the first line of text in the test output.")
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt
index e3f74fd..08a31c4 100644
--- a/Tests/CompileFeatures/CMakeLists.txt
+++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -1,12 +1,15 @@
-
 cmake_minimum_required(VERSION 3.10)
+project(CompileFeatures LANGUAGES NONE)
 
-project(CompileFeatures)
-
+enable_language(C)
 set(ext_C c)
-set(ext_CXX cpp)
 set(std_C 90 99 11 17 23)
-set(std_CXX 98 11 14 17 20 23 26)
+
+if(CMake_TEST_CXX)
+  enable_language(CXX)
+  set(ext_CXX cpp)
+  set(std_CXX 98 11 14 17 20 23 26)
+endif()
 
 if(CMake_TEST_CUDA)
   enable_language(CUDA)
@@ -356,10 +359,7 @@
 # always add a target "CompileFeatures"
 if ((NOT CXX_expected_features) OR
     (NOT cxx_auto_type IN_LIST CXX_expected_features))
-  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp"
-    "int main(int,char**) { return 0; }\n"
-  )
-  add_executable(CompileFeatures "${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp")
+  add_executable(CompileFeatures main.c)
 else()
   # these tests only work if at least cxx_auto_type is available
   add_executable(CompileFeatures main.cpp)
diff --git a/Tests/CompileFeatures/c_std.h b/Tests/CompileFeatures/c_std.h
index afe0308..7e6402d 100644
--- a/Tests/CompileFeatures/c_std.h
+++ b/Tests/CompileFeatures/c_std.h
@@ -5,4 +5,6 @@
 
 #ifdef __STDC_VERSION__
 #  define C_STD __STDC_VERSION__
+#elif defined(__POCC_STDC_VERSION__)
+#  define C_STD __POCC_STDC_VERSION__
 #endif
diff --git a/Tests/CompileFeatures/main.c b/Tests/CompileFeatures/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/CompileFeatures/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt
index a355a4d..cfe47da 100644
--- a/Tests/Cuda/CMakeLists.txt
+++ b/Tests/Cuda/CMakeLists.txt
@@ -32,3 +32,4 @@
 
 add_cuda_test_macro(Cuda.WithC CudaWithC)
 add_cuda_test_macro(Cuda.Bin2C CudaBin2C)
+add_cuda_test_macro(Cuda.Sanitizer ${CMAKE_CTEST_COMMAND} --output-on-failure)
diff --git a/Tests/Cuda/Sanitizer/CMakeLists.txt b/Tests/Cuda/Sanitizer/CMakeLists.txt
new file mode 100644
index 0000000..df3b4f0
--- /dev/null
+++ b/Tests/Cuda/Sanitizer/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 4.3)
+project(Sanitizer CXX)
+
+enable_testing()
+
+# Goal for this example:
+# Validate that we can use CUDAToolkit sanitizer
+find_package(CUDAToolkit REQUIRED)
+
+if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1)
+  add_executable(CudaSanitizer test_sanitizer.cpp)
+  target_link_libraries(CudaSanitizer PRIVATE CUDA::sanitizer CUDA::cudart_static)
+
+  add_test(NAME CudaSanitizer COMMAND CudaSanitizer)
+  if(WIN32)
+    get_property(sanitizer_path TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION)
+    get_filename_component(sanitizer_dir "${sanitizer_path}" DIRECTORY)
+    set_property(TEST CudaSanitizer PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${sanitizer_dir}")
+  endif()
+endif()
diff --git a/Tests/Cuda/Sanitizer/test_sanitizer.cpp b/Tests/Cuda/Sanitizer/test_sanitizer.cpp
new file mode 100644
index 0000000..dc3f70d
--- /dev/null
+++ b/Tests/Cuda/Sanitizer/test_sanitizer.cpp
@@ -0,0 +1,74 @@
+#include <iostream>
+
+#include <cuda_runtime.h>
+#include <sanitizer.h>
+#include <stddef.h>
+
+namespace {
+
+bool malloc_called = false;
+bool free_called = false;
+
+void sanitizer_callback(void* userdata, Sanitizer_CallbackDomain domain,
+                        Sanitizer_CallbackId cbid, void const* cbdata)
+{
+  if (domain == SANITIZER_CB_DOMAIN_RESOURCE) {
+    switch (cbid) {
+      case SANITIZER_CBID_RESOURCE_DEVICE_MEMORY_ALLOC:
+        malloc_called = true;
+        break;
+      case SANITIZER_CBID_RESOURCE_DEVICE_MEMORY_FREE:
+        free_called = true;
+        break;
+      default:
+        break;
+    }
+  }
+}
+
+}
+
+#define SANITIZER_TRY(call)                                                   \
+  do {                                                                        \
+    SanitizerResult result;                                                   \
+    if ((result = (call)) != SANITIZER_SUCCESS) {                             \
+      const char* result_str;                                                 \
+      sanitizerGetResultString(result, &result_str);                          \
+      std::cerr << __FILE__ << ":" << __LINE__ << ": " << result_str << "\n"; \
+      return 1;                                                               \
+    }                                                                         \
+  } while (0)
+
+#define CUDA_TRY(call)                                                        \
+  do {                                                                        \
+    cudaError_t result;                                                       \
+    if ((result = (call)) != cudaSuccess) {                                   \
+      std::cerr << __FILE__ << ":" << __LINE__ << ": "                        \
+                << cudaGetErrorName(result) << "\n";                          \
+      return 1;                                                               \
+    }                                                                         \
+  } while (0)
+
+int main()
+{
+  Sanitizer_SubscriberHandle handle;
+  SANITIZER_TRY(sanitizerSubscribe(&handle, sanitizer_callback, NULL));
+  SANITIZER_TRY(
+    sanitizerEnableDomain(1, handle, SANITIZER_CB_DOMAIN_RESOURCE));
+
+  void* dev_ptr;
+  CUDA_TRY(cudaMalloc(&dev_ptr, 10));
+  if (!malloc_called) {
+    std::cerr << "cudaMalloc() did not invoke callback\n";
+    return 1;
+  }
+
+  CUDA_TRY(cudaFree(dev_ptr));
+  if (!free_called) {
+    std::cerr << "cudaFree() did not invoke callback\n";
+    return 1;
+  }
+
+  SANITIZER_TRY(sanitizerUnsubscribe(handle));
+  return 0;
+}
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index 2ec6ee8..396f6d3 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -41,6 +41,7 @@
 endif()
 
 add_cuda_test_macro(CudaOnly.DeviceLTO CudaOnlyDeviceLTO)
+add_cuda_test_macro(CudaOnly.Sanitizer ${CMAKE_CTEST_COMMAND} --output-on-failure)
 
 if(MSVC)
   # Tests for features that only work with MSVC
diff --git a/Tests/CudaOnly/Sanitizer/CMakeLists.txt b/Tests/CudaOnly/Sanitizer/CMakeLists.txt
new file mode 100644
index 0000000..10cf163
--- /dev/null
+++ b/Tests/CudaOnly/Sanitizer/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 4.3)
+project(Sanitizer CUDA)
+
+enable_testing()
+
+# Goal for this example:
+# Validate that we can use CUDAToolkit sanitizer
+find_package(CUDAToolkit REQUIRED)
+
+if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1)
+  add_executable(CudaOnlySanitizer test_sanitizer.cu)
+  target_link_libraries(CudaOnlySanitizer PRIVATE CUDA::sanitizer)
+
+  add_test(NAME CudaOnlySanitizer COMMAND CudaOnlySanitizer)
+  if(WIN32)
+    get_property(sanitizer_path TARGET CUDA::sanitizer PROPERTY IMPORTED_LOCATION)
+    get_filename_component(sanitizer_dir "${sanitizer_path}" DIRECTORY)
+    set_property(TEST CudaOnlySanitizer PROPERTY ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${sanitizer_dir}")
+  endif()
+endif()
diff --git a/Tests/CudaOnly/Sanitizer/test_sanitizer.cu b/Tests/CudaOnly/Sanitizer/test_sanitizer.cu
new file mode 100644
index 0000000..4fa9c25
--- /dev/null
+++ b/Tests/CudaOnly/Sanitizer/test_sanitizer.cu
@@ -0,0 +1 @@
+#include "../../Cuda/Sanitizer/test_sanitizer.cpp"
diff --git a/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar b/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar
new file mode 100644
index 0000000..2bcd3fd
--- /dev/null
+++ b/Tests/Fuzzing/corpus/archive/cab-huffman-uninit.tar
Binary files differ
diff --git a/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake
new file mode 100644
index 0000000..881d941
--- /dev/null
+++ b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake
@@ -0,0 +1,10 @@
+
+cmake_policy(SET CMP0217 NEW)
+
+macro(FOO)
+endmacro()
+
+get_property(macros DIRECTORY PROPERTY MACROS)
+if (macros)
+  set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.")
+endif()
diff --git a/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake
new file mode 100644
index 0000000..dee0cc3
--- /dev/null
+++ b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake
@@ -0,0 +1,10 @@
+
+cmake_policy(SET CMP0217 OLD)
+
+macro(FOO)
+endmacro()
+
+get_property(macros DIRECTORY PROPERTY MACROS)
+if (NOT "FOO" IN_LIST macros)
+  set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.")
+endif()
diff --git a/Tests/RunCMake/CMP0217/CMakeLists.txt b/Tests/RunCMake/CMP0217/CMakeLists.txt
new file mode 100644
index 0000000..955802c
--- /dev/null
+++ b/Tests/RunCMake/CMP0217/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 4.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0217/RunCMakeTest.cmake b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake
new file mode 100644
index 0000000..8b6cffa
--- /dev/null
+++ b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake
@@ -0,0 +1,4 @@
+include(RunCMake)
+
+run_cmake(CMP0217-OLD)
+run_cmake(CMP0217-NEW)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index b763eb7..6261947 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -184,6 +184,7 @@
 if(WIN32)
   add_RunCMake_test(CMP0212)
 endif()
+add_RunCMake_test(CMP0217)
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
   add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
diff --git a/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-result.txt b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt
new file mode 100644
index 0000000..e8d349d
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey:
+Invalid build preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in
new file mode 100644
index 0000000..7e9932b
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildEmptyEnvKey.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "buildPresets": [
+    {
+      "name": "EmptyEnvKey",
+      "environment": {
+        "": "value"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-result.txt b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt
new file mode 100644
index 0000000..e9ba8c2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/BuildInvalidInheritance:
+Invalid build preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in
new file mode 100644
index 0000000..2dd5d81
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/BuildInvalidInheritance.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "buildPresets": [
+    {
+      "name": "InvalidInheritance",
+      "inherits": [
+        "NoExist"
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
index 2d5b477..d7366f7 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/CyclicInheritance0:
-Cyclic preset inheritance for preset "CyclicInheritance0"$
+Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
index 596fcf8..e2d9d11 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/CyclicInheritance1:
-Cyclic preset inheritance for preset "CyclicInheritance0"$
+Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
index a6b83c8..99fe0bd 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/CyclicInheritance2:
-Cyclic preset inheritance for preset "CyclicInheritance0"$
+Cyclic preset inheritance for configure preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
index cb9e545..d6f402f 100644
--- a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/EmptyCacheKey:
-Invalid preset: "EmptyCacheKey"$
+Invalid configure preset: "EmptyCacheKey": Empty cache variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
index bc0f866..a357267 100644
--- a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/EmptyEnvKey:
-Invalid preset: "EmptyEnvKey"$
+Invalid configure preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
index 8e19c69..eb658ce 100644
--- a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/EnvCycle:
-Invalid preset: "EnvCycle"
+Invalid configure preset: "EnvCycle": Invalid macro expansion
 Invalid macro expansion in "EnvCycle"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt
new file mode 100644
index 0000000..6d9315e
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@
+CMake Warning \(unused-cli\):
+  Manually-specified variables were not used by the project:
+
+    RunCMake_GENERATOR
+    UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake
new file mode 100644
index 0000000..d6fe2ff
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake
@@ -0,0 +1,6 @@
+cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action)
+if(NOT "${action}" STREQUAL SEND_ERROR)
+   message(SEND_ERROR
+     "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION"
+     " (expected 'SEND_ERROR', actual '${action}')")
+endif()
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
index 5c02d5b..dd47dc7 100644
--- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated:
-Invalid preset: "ErrorNoWarningDeprecated"$
+Invalid configure preset: "ErrorNoWarningDeprecated"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
index 4640b1e..ca087b1 100644
--- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDev:
-Invalid preset: "ErrorNoWarningDev"$
+Invalid configure preset: "ErrorNoWarningDev"$
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
index f7e3461..357050f 100644
--- a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/IncludeCycle:
-Cyclic include among preset files: [^
+Cyclic include among presets files: [^
 ]*/CMakeUserPresets\.json$
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
index 85ddab1..c106f29 100644
--- a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
@@ -1,4 +1,4 @@
 CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/IncludeCycle3Files:
-Cyclic include among preset files: [^
+Cyclic include among presets files: [^
 ]*/CMakePresets\.json
diff --git a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
index 216f308..a8eb597 100644
--- a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/InvalidInheritance:
-Invalid preset: "InvalidInheritance"$
+Invalid configure preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
index 86cd861..b948676 100644
--- a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/InvalidRegex:
-Invalid preset: "InvalidRegex"
+Invalid configure preset: "InvalidRegex": Invalid condition
 Invalid macro expansion in "InvalidRegex"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
index bae9794..ccd3d4c 100644
--- a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/NoPresetBinaryDir:
 Preset "NoPresetBinaryDir" missing field "binaryDir"
-Invalid preset: "NoPresetBinaryDir"$
+Invalid configure preset: "NoPresetBinaryDir"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetFileArgument-result.txt b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt
new file mode 100644
index 0000000..29d37ad
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoPresetFileArgument-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error: No file specified for --presets-file
+CMake Error: Run 'cmake --help' for all supported options\.$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
index c7e5b5e..51558cd 100644
--- a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/NoPresetGenerator:
 Preset "NoPresetGenerator" missing field "generator"
-Invalid preset: "NoPresetGenerator"$
+Invalid configure preset: "NoPresetGenerator"$
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake
new file mode 100644
index 0000000..d15bd8d
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.cmake
@@ -0,0 +1,3 @@
+include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
+
+test_variable(PRESETS_FILE "UNINITIALIZED" "OtherCMakePresetsFile.json")
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in
new file mode 100644
index 0000000..af68ee8
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFile.json.in
@@ -0,0 +1,54 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "cacheVariables": {
+        "PRESETS_FILE": "OtherCMakePresetsFile.json"
+      }
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "configurePreset": "OtherCMakePresetsFile"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "configurePreset": "OtherCMakePresetsFile"
+    }
+  ],
+  "packagePresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "configurePreset": "OtherCMakePresetsFile"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "OtherCMakePresetsFile"
+        },
+        {
+          "type": "build",
+          "name": "OtherCMakePresetsFile"
+        },
+        {
+          "type": "test",
+          "name": "OtherCMakePresetsFile"
+        },
+        {
+          "type": "package",
+          "name": "OtherCMakePresetsFile"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in
new file mode 100644
index 0000000..4b7648d
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakePresets.json.in
@@ -0,0 +1,13 @@
+{
+  "version": 1,
+  "configurePresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "cacheVariables": {
+        "PRESETS_FILE": "CMakePresets.json"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in
new file mode 100644
index 0000000..185f46d
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileCMakeUserPresets.json.in
@@ -0,0 +1,13 @@
+{
+  "version": 1,
+  "configurePresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "cacheVariables": {
+        "PRESETS_FILE": "CMakeUserPresets.json"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt
new file mode 100644
index 0000000..7ec3a37
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileList-stdout.txt
@@ -0,0 +1,19 @@
+^Available configure presets:
+
+  "OtherCMakePresetsFile"
+
+Available build presets:
+
+  "OtherCMakePresetsFile"
+
+Available test presets:
+
+  "OtherCMakePresetsFile"
+
+Available package presets:
+
+  "OtherCMakePresetsFile"
+
+Available workflow presets:
+
+  "OtherCMakePresetsFile"$
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileRelPath.cmake b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileRelPath.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileRelPath.cmake
diff --git a/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileSubdir.cmake b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileSubdir.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/OtherCMakePresetsFileSubdir.cmake
diff --git a/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-result.txt b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt
new file mode 100644
index 0000000..6d18c7a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey:
+Invalid package preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in
new file mode 100644
index 0000000..f181e45
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageEmptyEnvKey.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 6,
+  "packagePresets": [
+    {
+      "name": "EmptyEnvKey",
+      "environment": {
+        "": "value"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-result.txt b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt
new file mode 100644
index 0000000..ad32da4
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/PackageInvalidInheritance:
+Invalid package preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in
new file mode 100644
index 0000000..7076ecc
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageInvalidInheritance.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 6,
+  "packagePresets": [
+    {
+      "name": "InvalidInheritance",
+      "inherits": [
+        "NoExist"
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-result.txt b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt
new file mode 100644
index 0000000..b29e953
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset:
+Invalid package preset: "noConfigurePreset": Package presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in
new file mode 100644
index 0000000..03b09ed
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PackageNoConfigurePreset.json.in
@@ -0,0 +1,8 @@
+{
+  "version": 6,
+  "packagePresets": [
+    {
+      "name": "noConfigurePreset"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-result.txt b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt
new file mode 100644
index 0000000..668f949
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/PresetsFileArgNoExist:
+File not found: [^
+]*/FileDoesNotExist\.json$
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index 303e0c9..95acf52 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -30,6 +30,33 @@
   endif()
   configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY)
 
+  set(_presets_file)
+  if(CMakePresets_FILE_ARG)
+    cmake_path(GET RunCMake_TEST_SOURCE_DIR PARENT_PATH CMakePresets_FILE_ARG_DIRECTORY)
+    cmake_path(APPEND CMakePresets_FILE_ARG_DIRECTORY "_OtherCMakePresetsFiles")
+    if(NOT RunCMake_TEST_SOURCE_DIR_NO_CLEAN)
+      file(REMOVE_RECURSE "${CMakePresets_FILE_ARG_DIRECTORY}")
+      file(MAKE_DIRECTORY "${CMakePresets_FILE_ARG_DIRECTORY}")
+    endif()
+    if(CMakePresets_FILE_ARG_RELATIVE)
+      cmake_path(RELATIVE_PATH
+        CMakePresets_FILE_ARG_DIRECTORY
+        BASE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}"
+        OUTPUT_VARIABLE CMakePresets_FILE_ARG_RELATIVE_PATH
+      )
+      set(_presets_file "--presets-file=${CMakePresets_FILE_ARG_RELATIVE_PATH}/${CMakePresets_FILE_ARG}")
+    else()
+      set(_presets_file "--presets-file=${CMakePresets_FILE_ARG_DIRECTORY}/${CMakePresets_FILE_ARG}")
+    endif()
+    foreach(_presets_file_arg IN ITEMS ${CMakePresets_FILE_ARG} ${CMakePresets_FILE_ARG_EXTRA_FILES})
+      configure_file(
+        "${RunCMake_SOURCE_DIR}/${_presets_file_arg}.in"
+        "${CMakePresets_FILE_ARG_DIRECTORY}/${_presets_file_arg}"
+        @ONLY
+      )
+    endforeach()
+  endif()
+
   if(NOT CMakePresets_FILE)
     set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in")
   endif()
@@ -93,6 +120,7 @@
     ${_make_program}
     ${_unused_cli}
     ${_preset}
+    ${_presets_file}
     ${_log_level}
     ${ARGN}
     )
@@ -153,6 +181,10 @@
 run_cmake_presets(CyclicInheritance1)
 run_cmake_presets(CyclicInheritance2)
 run_cmake_presets(InvalidInheritance)
+run_cmake_presets(BuildInvalidInheritance)
+run_cmake_presets(PackageInvalidInheritance)
+run_cmake_presets(TestInvalidInheritance)
+run_cmake_presets(PackageNoConfigurePreset)
 run_cmake_presets(ErrorNoWarningDev)
 run_cmake_presets(ErrorNoWarningDeprecated)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
@@ -162,6 +194,9 @@
 run_cmake_presets(UnknownToolsetStrategy)
 run_cmake_presets(EmptyCacheKey)
 run_cmake_presets(EmptyEnvKey)
+run_cmake_presets(BuildEmptyEnvKey)
+run_cmake_presets(PackageEmptyEnvKey)
+run_cmake_presets(TestEmptyEnvKey)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 run_cmake_presets(UnclosedMacro)
 run_cmake_presets(NoSuchMacro)
@@ -313,6 +348,7 @@
 run_cmake_presets(NoPresetArgument --preset)
 run_cmake_presets(NoPresetArgumentEq --preset= -DA=B)
 run_cmake_presets(UseHiddenPreset)
+run_cmake_presets(NoPresetFileArgument --preset GoodNoArgs --presets-file)
 
 # Test CMakeUserPresets.json
 unset(CMakePresets_FILE)
@@ -326,6 +362,30 @@
 run_cmake_presets(UserDuplicateCross)
 run_cmake_presets(UserInheritance)
 
+# Test --presets-file=<file>
+# <file> should be preferred over CMakePresets.json and CMakeUserPresets.json
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/OtherCMakePresetsFileCMakePresets.json.in")
+set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/OtherCMakePresetsFileCMakeUserPresets.json.in")
+# <file> must exist
+run_cmake_presets(PresetsFileArgNoExist --presets-file=FileDoesNotExist.json)
+set(CMakePresets_FILE_ARG OtherCMakePresetsFile.json)
+run_cmake_presets(OtherCMakePresetsFile)
+# --list-presets should only list the presets defined in <file>
+run_cmake_presets(OtherCMakePresetsFileList --list-presets=all)
+# <file> can be specified via relative path on the command line
+set(CMakePresets_FILE_ARG_RELATIVE 1)
+run_cmake_presets(OtherCMakePresetsFileRelPath --preset OtherCMakePresetsFile)
+set(CMakePresets_FILE_ARG "subdir/OtherCMakePresetsFileSubdir.json")
+set(CMakePresets_FILE_ARG_EXTRA_FILES "subdir/IncludeRelativeToSubdir.json")
+# Files included by <file> should have paths evaluated relative to <file>
+run_cmake_presets(OtherCMakePresetsFileSubdir)
+run_cmake_presets(OtherCMakePresetsFileSubdir --preset=IncludeRelativeToSubdir)
+unset(CMakePresets_FILE)
+unset(CMakeUserPresets_FILE)
+unset(CMakePresets_FILE_ARG)
+unset(CMakePresets_FILE_ARG_RELATIVE)
+unset(CMakePresets_FILE_ARG_EXTRA_FILES)
+
 # Test listing presets
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/ListPresets.json.in")
 run_cmake_presets(ListPresets --list-presets)
@@ -363,7 +423,9 @@
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings12.json.in")
 run_cmake_presets(NoWarningFlags)
 run_cmake_presets(WarningFlags)
+run_cmake_presets(WarningAbsInstallPath)
 run_cmake_presets(DisableWarningFlags)
+run_cmake_presets(ErrorAbsInstallPath)
 run_cmake_presets(ErrorDev)
 run_cmake_presets(ErrorUninitialized)
 run_cmake_presets(ErrorUnusedCli)
diff --git a/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-result.txt b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt
new file mode 100644
index 0000000..df00af9
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/TestEmptyEnvKey:
+Invalid test preset: "EmptyEnvKey": Empty environment variable names are not allowed$
diff --git a/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in
new file mode 100644
index 0000000..50741d1
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestEmptyEnvKey.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "testPresets": [
+    {
+      "name": "EmptyEnvKey",
+      "environment": {
+        "": "value"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/TestInvalidInheritance-result.txt b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt
new file mode 100644
index 0000000..a72e498
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestInvalidInheritance-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/TestInvalidInheritance:
+Invalid test preset: "InvalidInheritance": Could not find inherited preset "NoExist"$
diff --git a/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in b/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in
new file mode 100644
index 0000000..e137f52
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TestInvalidInheritance.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "testPresets": [
+    {
+      "name": "InvalidInheritance",
+      "inherits": [
+        "NoExist"
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
index 2ce1316..65d1a5f 100644
--- a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
@@ -1,3 +1,3 @@
 ^CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresets/UserInheritance:
-Inherited preset "UserInheritance" is unreachable from preset's file$
+Inherited configure preset "UserInheritance" is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt
new file mode 100644
index 0000000..6d9315e
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@
+CMake Warning \(unused-cli\):
+  Manually-specified variables were not used by the project:
+
+    RunCMake_GENERATOR
+    UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake
new file mode 100644
index 0000000..fc59b1c
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake
@@ -0,0 +1,6 @@
+cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action)
+if(NOT "${action}" STREQUAL WARN)
+   message(SEND_ERROR
+     "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION"
+     " (expected 'WARN', actual '${action}')")
+endif()
diff --git a/Tests/RunCMake/CMakePresets/Warnings12.json.in b/Tests/RunCMake/CMakePresets/Warnings12.json.in
index 650f111..701e5f4 100644
--- a/Tests/RunCMake/CMakePresets/Warnings12.json.in
+++ b/Tests/RunCMake/CMakePresets/Warnings12.json.in
@@ -30,6 +30,20 @@
       }
     },
     {
+      "name": "WarningAbsInstallPath",
+      "inherits": "NoWarningFlags",
+      "warnings": {
+        "installAbsoluteDestination": true
+      }
+    },
+    {
+      "name": "ErrorAbsInstallPath",
+      "inherits": "NoWarningFlags",
+      "errors": {
+        "installAbsoluteDestination": true
+      }
+    },
+    {
       "name": "ErrorDev",
       "inherits": "NoWarningFlags",
       "errors": {
diff --git a/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in b/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in
new file mode 100644
index 0000000..56db1ad
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/subdir/IncludeRelativeToSubdir.json.in
@@ -0,0 +1,10 @@
+{
+  "version": 4,
+  "configurePresets": [
+    {
+      "name": "IncludeRelativeToSubdir",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in b/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in
new file mode 100644
index 0000000..e8a5f7f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/subdir/OtherCMakePresetsFileSubdir.json.in
@@ -0,0 +1,13 @@
+{
+  "version": 4,
+  "include": [
+    "IncludeRelativeToSubdir.json"
+  ],
+  "configurePresets": [
+    {
+      "name": "OtherCMakePresetsFileSubdir",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
index 9ce6ea5..4749340 100644
--- a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
@@ -1,3 +1,3 @@
 CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset:
-Invalid preset: "noConfigurePreset"$
+Invalid build preset: "noConfigurePreset": Build presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.cmake
diff --git a/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in
new file mode 100644
index 0000000..474c669
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFile.json.in
@@ -0,0 +1,16 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default-from-other-file",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt
new file mode 100644
index 0000000..ee586df
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/OtherCMakePresetsFileListPresets-build-x-stdout.txt
@@ -0,0 +1,3 @@
+^Available build presets:
+
+  "default-from-other-file"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-result.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt
new file mode 100644
index 0000000..50d4e2b
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/PresetsFileNoArg-build-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: No file specified for --presets-file
diff --git a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
index 92378d3..50a4e8e 100644
--- a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
@@ -26,6 +26,12 @@
     configure_file("${CMakePresetsBuild_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY)
   endif()
 
+  set(_presets_file)
+  if(CMakePresets_FILE_ARG)
+    set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}")
+    configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY)
+  endif()
+
   if(NOT CMakeUserPresets_FILE)
     set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in")
   endif()
@@ -50,11 +56,11 @@
 
     if(eq)
       run_cmake_command(${name}-build-${BUILD_PRESET}
-        ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset=${BUILD_PRESET}" ${ARGN})
+        ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset=${BUILD_PRESET}" ${_presets_file} ${ARGN})
       set(eq 0)
     else()
       run_cmake_command(${name}-build-${BUILD_PRESET}
-        ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset" "${BUILD_PRESET}" ${ARGN})
+        ${CMAKE_COMMAND} "--build" ${CMakePresetsBuild_BUILD_DIR_OVERRIDE} "--preset" "${BUILD_PRESET}" ${_presets_file} ${ARGN})
       set(eq 1)
     endif()
   endforeach()
@@ -74,9 +80,17 @@
 run_cmake_build_presets(InvalidConfigurePreset "default" "badConfigurePreset" "")
 run_cmake_build_presets(Condition "default" "enabled;disabled" "")
 
+set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json")
+run_cmake_build_presets(OtherCMakePresetsFile "default" "default-from-other-file" "")
+set(CMakePresetsBuild_BUILD_ONLY 1)
+run_cmake_build_presets(OtherCMakePresetsFileListPresets "" "x" "--list-presets" "")
+unset(CMakePresetsBuild_BUILD_ONLY)
+unset(CMakePresets_FILE_ARG)
+
 set(CMakePresetsBuild_BUILD_ONLY 1)
 run_cmake_command(PresetsNoArg-build ${CMAKE_COMMAND} "--build" "--preset")
 run_cmake_command(PresetsNoArgEq-build ${CMAKE_COMMAND} "--build" "--preset=")
+run_cmake_Command(PresetsFileNoArg-build ${CMAKE_COMMAND} "--build" "--presets-file")
 run_cmake_build_presets(ListPresets "x" "x" "--list-presets" "")
 run_cmake_build_presets(NoConfigurePreset "x" "noConfigurePreset" "")
 run_cmake_build_presets(Invalid "x" "hidden;vendorMacro" "")
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake
new file mode 100644
index 0000000..a9068c7
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.cmake
@@ -0,0 +1,4 @@
+set(CPACK_PACKAGE_NAME OtherCMakePresetsFile)
+set(CPACK_GENERATOR "TGZ;TXZ")
+
+include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in
new file mode 100644
index 0000000..a015307
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFile.json.in
@@ -0,0 +1,22 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default"
+    }
+  ],
+  "packagePresets": [
+    {
+      "name": "default-from-other-file",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt
new file mode 100644
index 0000000..7e03528
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsPackage/OtherCMakePresetsFileListPresets-package-x-stdout.txt
@@ -0,0 +1,3 @@
+^Available package presets:
+
+  "default-from-other-file"$
diff --git a/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-result.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt
new file mode 100644
index 0000000..c532031
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsPackage/PresetsFileNoArg-package-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: No file specified for --presets-file$
diff --git a/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake
index 969fda2..b8c1eb3 100644
--- a/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake
@@ -33,6 +33,12 @@
     configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY)
   endif()
 
+  set(_presets_file)
+  if(CMakePresets_FILE_ARG)
+    set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}")
+    configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY)
+  endif()
+
   foreach(ASSET ${CMakePresetsPackage_ASSETS})
     configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY)
   endforeach()
@@ -63,11 +69,11 @@
 
     if(eq)
       run_cmake_command(${name}-package-${PACKAGE_PRESET}
-        ${CMAKE_CPACK_COMMAND} "--preset=${PACKAGE_PRESET}" ${ARGN})
+        ${CMAKE_CPACK_COMMAND} "--preset=${PACKAGE_PRESET}" ${_presets_file} ${ARGN})
       set(eq 0)
     else()
       run_cmake_command(${name}-package-${PACKAGE_PRESET}
-        ${CMAKE_CPACK_COMMAND} "--preset" "${PACKAGE_PRESET}" ${ARGN})
+        ${CMAKE_CPACK_COMMAND} "--preset" "${PACKAGE_PRESET}" ${_presets_file} ${ARGN})
       set(eq 1)
     endif()
   endforeach()
@@ -101,5 +107,11 @@
 run_cmake_package_presets(Good "default" "build-default-debug" "no-environment;with-environment;generators;configurations;variables;config-file;debug;verbose;package-name;package-version;package-directory;vendor-name")
 run_cmake_package_presets(ListPresets "default" "" "x" "--list-presets")
 
+set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json")
+run_cmake_package_presets(OtherCMakePresetsFile "default" "default" "default-from-other-file" "")
+run_cmake_package_presets(OtherCMakePresetsFileListPresets "" "" "x" "--list-presets")
+unset(CMakePresets_FILE_ARG)
+
 run_cmake_command(PresetsNoArg-package ${CMAKE_CPACK_COMMAND} "--preset")
 run_cmake_command(PresetsNoArgEq-package ${CMAKE_CPACK_COMMAND} "--preset=")
+run_cmake_command(PresetsFileNoArg-package ${CMAKE_CPACK_COMMAND} "--presets-file")
diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
index a70497f..d414ed8 100644
--- a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
@@ -1,3 +1,3 @@
 CMake Error: Could not read presets from [^
 ]*/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset:
-Invalid preset: "noConfigurePreset"$
+Invalid test preset: "noConfigurePreset": Test presets must either be hidden or have an associated configure preset$
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake
new file mode 100644
index 0000000..3b3b9f9
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.cmake
@@ -0,0 +1,5 @@
+enable_testing()
+add_test(testa ${CMAKE_COMMAND} -E echo testa)
+add_test(testb ${CMAKE_COMMAND} -E echo testb)
+add_test(testc ${CMAKE_COMMAND} -E echo testc)
+add_test(testd ${CMAKE_COMMAND} -E echo testd)
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in
new file mode 100644
index 0000000..7fb2df5
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFile.json.in
@@ -0,0 +1,16 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default-from-other-file",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-text-x-stdout.txt b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-text-x-stdout.txt
new file mode 100644
index 0000000..7623488
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/OtherCMakePresetsFileListPresets-text-x-stdout.txt
@@ -0,0 +1,3 @@
+^Available test presets:
+
+  "default-from-other-file"$
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-result.txt b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt
new file mode 100644
index 0000000..5235262
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/PresetsFileNoArg-test-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: '--presets-file' requires an argument$
diff --git a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
index 42dfe18..2b320f5 100644
--- a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
@@ -37,6 +37,12 @@
     configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY)
   endif()
 
+  set(_presets_file)
+  if(CMakePresets_FILE_ARG)
+    set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}")
+    configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY)
+  endif()
+
   foreach(ASSET ${CMakePresetsTest_ASSETS})
     configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY)
   endforeach()
@@ -65,11 +71,11 @@
 
     if(eq)
       run_cmake_command(${name}-test-${TEST_PRESET}
-        ${CMAKE_CTEST_COMMAND} "--preset=${TEST_PRESET}" ${ARGN})
+        ${CMAKE_CTEST_COMMAND} "--preset=${TEST_PRESET}" ${_presets_file} ${ARGN})
       set(eq 0)
     else()
       run_cmake_command(${name}-test-${TEST_PRESET}
-        ${CMAKE_CTEST_COMMAND} "--preset" "${TEST_PRESET}" ${ARGN})
+        ${CMAKE_CTEST_COMMAND} "--preset" "${TEST_PRESET}" ${_presets_file} ${ARGN})
       set(eq 1)
     endif()
   endforeach()
@@ -97,6 +103,7 @@
 
 run_cmake_command(PresetsNoArg-test ${CMAKE_CTEST_COMMAND} "--preset")
 run_cmake_command(PresetsNoArgEq-test ${CMAKE_CTEST_COMMAND} "--preset=")
+run_cmake_command(PresetsFileNoArg-test ${CMAKE_CTEST_COMMAND} "--presets-file")
 
 set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Condition.json.in")
 run_cmake_test_presets(ConditionListPresets "" "" "x" "--list-presets")
@@ -123,6 +130,7 @@
 
 run_cmake_test_presets(Passthrough "default" "" "basic;inherit;execOnly")
 run_cmake_test_presets(PassthroughCombined "default" "" "combined" "--" "--cli-arg")
-set(CMakePresetsTest_NO_CONFIGURE 1)
 
-set(CMakePresetsTest_NO_BUILD 0)
+set(CMakePresets_FILE_ARG OtherCMakePresetsFile.json)
+run_cmake_test_presets(OtherCMakePresetsFile "default" "" "default-from-other-file")
+run_cmake_test_presets(OtherCMakePresetsFileListPresets "" "" "x" "--list-presets")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt
index 8e23c9b..71a61f5 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt
@@ -1,6 +1,7 @@
 ^Unknown argument -DINVALID_OPTION
 Usage: cmake --workflow <options>
 Options:
-  --preset <preset> = Workflow preset to execute\.
-  --list-presets    = List available workflow presets\.
-  --fresh           = Configure a fresh build tree, removing any existing cache file\.$
+  --preset <preset>     = Workflow preset to execute\.
+  --presets-file <file>  = Path to a presets file\.
+  --list-presets        = List available workflow presets\.
+  --fresh               = Configure a fresh build tree, removing any existing cache file\.$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake
new file mode 100644
index 0000000..31ce7ff
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.cmake
@@ -0,0 +1,8 @@
+message(STATUS "Testing the configure step at ${CMAKE_BINARY_DIR}")
+
+add_custom_target(echo_test ALL COMMAND ${CMAKE_COMMAND} -E echo "Testing the build step at ${CMAKE_BINARY_DIR}")
+
+enable_testing()
+add_test(NAME EchoTest COMMAND ${CMAKE_COMMAND} -E echo "Testing the test step at ${CMAKE_BINARY_DIR}")
+
+include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in
new file mode 100644
index 0000000..4b3f7c3
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFile.json.in
@@ -0,0 +1,57 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "binaryDir": "${sourceDir}/build",
+      "generator": "@RunCMake_GENERATOR@"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "configuration": "Debug"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "configuration": "Debug"
+    }
+  ],
+  "packagePresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "generators": [
+        "External"
+      ],
+      "configurations": ["Debug"]
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "OtherCMakePresetsFile",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "build",
+          "name": "default"
+        },
+        {
+          "type": "test",
+          "name": "default"
+        },
+        {
+          "type": "package",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt
new file mode 100644
index 0000000..194fbfd
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsWorkflow/OtherCMakePresetsFileListPresets-stdout.txt
@@ -0,0 +1,3 @@
+^Available workflow presets:
+
+  "OtherCMakePresetsFile"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt
new file mode 100644
index 0000000..50d4e2b
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsWorkflow/PresetsFileNoArg-workflow-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: No file specified for --presets-file
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake
index fac8dfc..e121b1c 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake
@@ -35,6 +35,12 @@
     configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY)
   endif()
 
+  set(_presets_file)
+  if(CMakePresets_FILE_ARG)
+    set(_presets_file "--presets-file=${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}")
+    configure_file("${RunCMake_SOURCE_DIR}/${CMakePresets_FILE_ARG}.in" "${RunCMake_TEST_SOURCE_DIR}/${CMakePresets_FILE_ARG}" @ONLY)
+  endif()
+
   foreach(ASSET ${CMakePresets_ASSETS})
     configure_file("${RunCMake_SOURCE_DIR}/${ASSET}.in" "${RunCMake_TEST_SOURCE_DIR}/${ASSET}" @ONLY)
   endforeach()
@@ -54,7 +60,7 @@
     set(eq 1 PARENT_SCOPE)
     set(preset_arg "--preset" "${name}")
   endif()
-  run_cmake_command("${name}" "${CMAKE_COMMAND}" "--workflow" ${preset_arg} ${ARGN})
+  run_cmake_command("${name}" "${CMAKE_COMMAND}" "--workflow" ${preset_arg} ${_presets_file} ${ARGN})
 endfunction()
 
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
@@ -80,9 +86,15 @@
 unset(CMakeUserPresets_FILE)
 unset(CMakePresets_ASSETS)
 
+set(CMakePresets_FILE_ARG "OtherCMakePresetsFile.json")
+run_cmake_workflow_presets(OtherCMakePresetsFile)
+run_cmake_workflow_presets(OtherCMakePresetsFileListPreset --list-presets)
+unset(CMakePresets_FILE_ARG)
+
 run_cmake_workflow_presets(ListPresets --list-presets)
 run_cmake_command(PresetsNoArg-workflow ${CMAKE_COMMAND} "--workflow" "--preset")
 run_cmake_command(PresetsNoArgEq-workflow ${CMAKE_COMMAND} "--workflow" "--preset=")
+run_cmake_command(PresetsFileNoArg-workflow ${CMAKE_COMMAND} "--workflow" "--presets-file")
 run_cmake_workflow_presets(InvalidOption -DINVALID_OPTION)
 
 set(RunCMake_TEST_NO_CLEAN TRUE)
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
index 2b4587f..d90b345 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":10}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":1}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
+^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":11}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":1}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
diff --git a/Tests/RunCMake/Diagnostics/CacheInit.cmake b/Tests/RunCMake/Diagnostics/CacheInit.cmake
index 1af1495..24baac2 100644
--- a/Tests/RunCMake/Diagnostics/CacheInit.cmake
+++ b/Tests/RunCMake/Diagnostics/CacheInit.cmake
@@ -1,4 +1,5 @@
 include(Assertions.cmake)
 
 expect(CMD_AUTHOR IGNORE)
+expect(CMD_INSTALL_ABSOLUTE_DESTINATION IGNORE)
 expect(CMD_DEPRECATED SEND_ERROR)
diff --git a/Tests/RunCMake/Diagnostics/CommandLine3.cmake b/Tests/RunCMake/Diagnostics/CommandLine3.cmake
index e11e17d..96f8e1a 100644
--- a/Tests/RunCMake/Diagnostics/CommandLine3.cmake
+++ b/Tests/RunCMake/Diagnostics/CommandLine3.cmake
@@ -1,3 +1,5 @@
 include(Assertions.cmake)
 
 expect_cached(CMD_UNINITIALIZED IGNORE)
+
+expect_cached(CMD_INSTALL_ABSOLUTE_DESTINATION WARN)
diff --git a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake
index 5ffb6fa..5f77f8e 100644
--- a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake
@@ -12,4 +12,4 @@
 run_cmake_with_options(CommandLine1 -Werror=author -Wdeprecated)
 run_cmake_with_options(CommandLine1 -Wno-deprecated -Werror=author)
 run_cmake_with_options(CommandLine2 -Werror=author -Wno-deprecated)
-run_cmake_with_options(CommandLine3 -Wno-error=uninitialized)
+run_cmake_with_options(CommandLine3 -Wno-error=uninitialized -Winstall-absolute-destination)
diff --git a/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake b/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake
index f42bb1b..94ead02 100644
--- a/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake
+++ b/Tests/RunCMake/FASTBuild/UnityIsolate-check.cmake
@@ -10,7 +10,7 @@
       '.*some_source_file_3.cpp',
       '.*some_source_file_4.cpp'
     }
-    .UnityInputIsolatedFiles =
+    .UnityInputExcludedFiles =
     {
       '.*some_source_file_1.cpp',
       '.*some_source_file_4.cpp'
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 34d0e86..b3958e7 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -13,7 +13,7 @@
     assert is_list(o)
     assert len(o) == 1
     major = 2
-    minor = 10
+    minor = 11
     check_index_object(o[0], "codemodel", major, minor, check_object_codemodel(g, major, minor))
 
 def check_backtrace(t, b, backtrace):
@@ -1077,6 +1077,8 @@
 
         read_codemodel_json_data("targets/c_headers_1.json"),
         read_codemodel_json_data("targets/c_headers_2.json"),
+        read_codemodel_json_data("targets/c_sources_1.json"),
+        read_codemodel_json_data("targets/c_sources_2.json"),
     ]
 
     if sys.platform == "darwin":
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
index ea7a079..1e84bb9 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
@@ -5,7 +5,9 @@
   "childSources": null,
   "targetIds": [
     "^c_headers_1::@6b8db101d64c125f29fe$",
-    "^c_headers_2::@6b8db101d64c125f29fe$"
+    "^c_headers_2::@6b8db101d64c125f29fe$",
+    "^c_sources_1::@6b8db101d64c125f29fe$",
+    "^c_sources_2::@6b8db101d64c125f29fe$"
   ],
   "abstractTargetIds": null,
   "projectName": "codemodel-v2",
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json
index ad73de9..658d77b 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json
@@ -32,7 +32,9 @@
         "^c_static_exe::@6890427a1f51a3e7e1df$",
         "^c_subdir::@6890427a1f51a3e7e1df$",
         "^c_headers_1::@6b8db101d64c125f29fe$",
-        "^c_headers_2::@6b8db101d64c125f29fe$"
+        "^c_headers_2::@6b8db101d64c125f29fe$",
+        "^c_sources_1::@6b8db101d64c125f29fe$",
+        "^c_sources_2::@6b8db101d64c125f29fe$"
     ],
     "abstractTargetIds": [
         "^interface_lib::@6890427a1f51a3e7e1df$"
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 1cad522..8df8133 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
@@ -230,6 +230,14 @@
             "backtrace": null
         },
         {
+            "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+            "backtrace": null
+        },
+        {
+            "id": "^c_sources_2::@6b8db101d64c125f29fe$",
+            "backtrace": null
+        },
+        {
             "id": "^static_framework::@217a96c3a62328a73ef4$",
             "backtrace": null
         },
@@ -421,6 +429,14 @@
             "backtrace": null
         },
         {
+            "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+            "backtrace": null
+        },
+        {
+            "id": "^c_sources_2::@6b8db101d64c125f29fe$",
+            "backtrace": null
+        },
+        {
             "id": "^static_framework::@217a96c3a62328a73ef4$",
             "backtrace": null
         },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json
new file mode 100644
index 0000000..64adf2e
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_1.json
@@ -0,0 +1,188 @@
+{
+  "name": "c_sources_1",
+  "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+  "directorySource": "^fileset$",
+  "projectName": "codemodel-v2",
+  "type": "STATIC_LIBRARY",
+  "imported": null,
+  "local": null,
+  "abstract": null,
+  "symbolic": null,
+  "isGeneratorProvided": null,
+  "fileSets": [
+    {
+      "name": "SOURCES",
+      "type": "SOURCES",
+      "visibility": "PRIVATE",
+      "baseDirectories": [
+        "^fileset$"
+      ]
+    },
+    {
+      "name": "public_srcs",
+      "type": "SOURCES",
+      "visibility": "PUBLIC",
+      "baseDirectories": [
+        "^fileset$"
+      ]
+    }
+  ],
+  "sources": [
+    {
+      "path": "^fileset/empty\\.c$",
+      "isGenerated": null,
+      "fileSetName": "public_srcs",
+      "sourceGroupName": "Source Files",
+      "compileGroupLanguage": "C",
+      "backtrace": [
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": 40,
+          "command": "target_sources",
+          "hasParent": true
+        },
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": null,
+          "command": null,
+          "hasParent": false
+        }
+      ]
+    },
+    {
+      "path": "^fileset/other\\.c$",
+      "isGenerated": null,
+      "fileSetName": "SOURCES",
+      "sourceGroupName": "Source Files",
+      "compileGroupLanguage": null,
+      "backtrace": [
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": 35,
+          "command": "target_sources",
+          "hasParent": true
+        },
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": null,
+          "command": null,
+          "hasParent": false
+        }
+      ]
+    }
+  ],
+  "interfaceSources": [
+    {
+      "path": "^fileset/empty\\.c$",
+      "isGenerated": null,
+      "fileSetName": "SOURCES",
+      "sourceGroupName": "Source Files"
+    }
+  ],
+  "sourceGroups": [
+    {
+      "name": "Source Files",
+      "sourcePaths": [
+        "^fileset/empty\\.c$",
+        "^fileset/other\\.c$"
+      ],
+      "interfaceSourcePaths": [
+        "^fileset/empty\\.c$"
+      ]
+    }
+  ],
+  "compileGroups": [
+    {
+      "language": "C",
+      "sourcePaths": [
+        "^fileset/empty\\.c$"
+      ],
+      "includes": [
+        {
+          "path": "^.*/Tests/RunCMake/FileAPI/fileset/dir$",
+          "isSystem": null,
+          "backtrace": [
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": 46,
+              "command": "set_property",
+              "hasParent": true
+            },
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": null,
+              "command": null,
+              "hasParent": false
+            }
+          ]
+        }
+      ],
+      "frameworks": null,
+      "defines": [
+        {
+          "define": "DEF2",
+          "backtrace": [
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": 43,
+              "command": "set_property",
+              "hasParent": true
+            },
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": null,
+              "command": null,
+              "hasParent": false
+            }
+          ]
+        },
+        {
+          "define": "_MBCS",
+          "backtrace": null
+        }
+      ],
+      "compileCommandFragments": null
+    }
+  ],
+  "backtrace": [
+    {
+      "file": "^fileset/CMakeLists\\.txt$",
+      "line": 33,
+      "command": "add_library",
+      "hasParent": true
+    },
+    {
+      "file": "^fileset/CMakeLists\\.txt$",
+      "line": null,
+      "command": null,
+      "hasParent": false
+    }
+  ],
+  "folder": null,
+  "nameOnDisk": "^(lib)?c_sources_1\\.(a|lib|l)$",
+  "artifacts": [
+    {
+      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_sources_1\\.(a|lib|l)$",
+      "_dllExtra": false
+    }
+  ],
+  "build": "^fileset$",
+  "source": "^fileset$",
+  "install": null,
+  "link": null,
+  "archive": {
+    "lto": null
+  },
+  "dependencies": [
+    {
+      "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+      "backtrace": null
+    }
+  ],
+  "linkLibraries": null,
+  "interfaceLinkLibraries": null,
+  "compileDependencies": null,
+  "interfaceCompileDependencies": null,
+  "objectDependencies": null,
+  "orderDependencies": null
+}
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json
new file mode 100644
index 0000000..65c7c77
--- /dev/null
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_sources_2.json
@@ -0,0 +1,218 @@
+{
+  "name": "c_sources_2",
+  "id": "^c_sources_2::@6b8db101d64c125f29fe$",
+  "directorySource": "^fileset$",
+  "projectName": "codemodel-v2",
+  "type": "STATIC_LIBRARY",
+  "imported": null,
+  "local": null,
+  "abstract": null,
+  "symbolic": null,
+  "isGeneratorProvided": null,
+  "fileSets": null,
+  "sources": [
+    {
+      "path": "^fileset/empty\\.c$",
+      "isGenerated": null,
+      "fileSetName": null,
+      "sourceGroupName": "Source Files",
+      "compileGroupLanguage": "C",
+      "backtrace": [
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": 50,
+          "command": "target_link_libraries",
+          "hasParent": true
+        },
+        {
+          "file": "^fileset/CMakeLists\\.txt$",
+          "line": null,
+          "command": null,
+          "hasParent": false
+        }
+      ]
+    }
+  ],
+  "interfaceSources": null,
+  "sourceGroups": [
+    {
+      "name": "Source Files",
+      "sourcePaths": [
+        "^fileset/empty\\.c$"
+      ],
+      "interfaceSourcePaths": null
+    }
+  ],
+  "compileGroups": [
+    {
+      "language": "C",
+      "sourcePaths": [
+        "^fileset/empty\\.c$"
+      ],
+      "includes": [
+        {
+          "path": "^.*/Tests/RunCMake/FileAPI/fileset/dir$",
+          "isSystem": null,
+          "backtrace": [
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": 47,
+              "command": "set_property",
+              "hasParent": true
+            },
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": null,
+              "command": null,
+              "hasParent": false
+            }
+          ]
+        }
+      ],
+      "frameworks": null,
+      "defines": [
+        {
+          "define": "DEF3",
+          "backtrace": [
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": 44,
+              "command": "set_property",
+              "hasParent": true
+            },
+            {
+              "file": "^fileset/CMakeLists\\.txt$",
+              "line": null,
+              "command": null,
+              "hasParent": false
+            }
+          ]
+        },
+        {
+          "define": "_MBCS",
+          "backtrace": null
+        }
+      ],
+      "compileCommandFragments": null
+    }
+  ],
+  "backtrace": [
+    {
+      "file": "^fileset/CMakeLists\\.txt$",
+      "line": 49,
+      "command": "add_library",
+      "hasParent": true
+    },
+    {
+      "file": "^fileset/CMakeLists\\.txt$",
+      "line": null,
+      "command": null,
+      "hasParent": false
+    }
+  ],
+  "folder": null,
+  "nameOnDisk": "^(lib)?c_sources_2\\.(a|lib|l)$",
+  "artifacts": [
+    {
+      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_sources_2\\.(a|lib|l)$",
+      "_dllExtra": false
+    }
+  ],
+  "build": "^fileset$",
+  "source": "^fileset$",
+  "install": null,
+  "link": null,
+  "archive": {
+    "lto": null
+  },
+  "dependencies": [
+    {
+      "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+      "backtrace": null
+    },
+    {
+        "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+        "fragment": null,
+        "backtrace": [
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": 50,
+                "command": "target_link_libraries",
+                "hasParent": true
+            },
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": null,
+                "command": null,
+                "hasParent": false
+            }
+        ],
+        "fromDependency": null
+    }
+  ],
+  "linkLibraries": [
+    {
+        "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+        "fragment": null,
+        "backtrace": [
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": 50,
+                "command": "target_link_libraries",
+                "hasParent": true
+            },
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": null,
+                "command": null,
+                "hasParent": false
+            }
+        ],
+        "fromDependency": null
+    }
+  ],
+  "interfaceLinkLibraries": [
+    {
+        "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+        "fragment": null,
+        "backtrace": [
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": 50,
+                "command": "target_link_libraries",
+                "hasParent": true
+            },
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": null,
+                "command": null,
+                "hasParent": false
+            }
+        ]
+    }
+  ],
+  "compileDependencies": [
+    {
+        "id": "^c_sources_1::@6b8db101d64c125f29fe$",
+        "fragment": null,
+        "backtrace": [
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": 50,
+                "command": "target_link_libraries",
+                "hasParent": true
+            },
+            {
+                "file": "^fileset/CMakeLists\\.txt$",
+                "line": null,
+                "command": null,
+                "hasParent": false
+            }
+        ],
+        "fromDependency": null
+    }
+  ],
+  "interfaceCompileDependencies": null,
+  "objectDependencies": null,
+  "orderDependencies": null
+}
diff --git a/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt b/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt
index c318b21..183d721 100644
--- a/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt
+++ b/Tests/RunCMake/FileAPI/fileset/CMakeLists.txt
@@ -26,3 +26,25 @@
   FILE_SET d
   )
 install(TARGETS c_headers_2)
+
+#
+# SOURCES file set type
+#
+add_library(c_sources_1 STATIC)
+
+target_sources(c_sources_1
+  PRIVATE FILE_SET SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES other.c
+  )
+set_property(FILE_SET SOURCES TARGET c_sources_1 PROPERTY COMPILE_DEFINITIONS DEF1)
+
+target_sources(c_sources_1
+  PUBLIC FILE_SET public_srcs TYPE SOURCES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES empty.c
+  )
+set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY COMPILE_DEFINITIONS DEF2)
+set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INTERFACE_COMPILE_DEFINITIONS DEF3)
+
+set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir")
+set_property(FILE_SET public_srcs TARGET c_sources_1 PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/dir")
+
+add_library(c_sources_2 STATIC)
+target_link_libraries(c_sources_2 PRIVATE c_sources_1)
diff --git a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake
index b9b69fb..a42945a 100644
--- a/Tests/RunCMake/MultiLint/RunCMakeTest.cmake
+++ b/Tests/RunCMake/MultiLint/RunCMakeTest.cmake
@@ -28,9 +28,9 @@
   run_multilint(genex)
 endif()
 
-function(run_skip_linting test_name prop_sf prop_tgt)
-  set(RunCMake_TEST_VARIANT_DESCRIPTION " (prop_sf=${prop_sf}, prop_tgt=${prop_tgt})")
-  list(APPEND RunCMake_TEST_OPTIONS "-Dprop_sf=${prop_sf}" "-Dprop_tgt=${prop_tgt}")
+function(run_skip_linting test_name prop_fs prop_sf prop_tgt)
+  set(RunCMake_TEST_VARIANT_DESCRIPTION " (prop_fs=${prop_fs}, prop_sf=${prop_sf}, prop_tgt=${prop_tgt})")
+  list(APPEND RunCMake_TEST_OPTIONS "-Dprop_fs=${prop_fs}" "-Dprop_sf=${prop_sf}" "-Dprop_tgt=${prop_tgt}")
 
   set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test_name}-build")
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -40,52 +40,90 @@
   run_cmake_command(${test_name}-Build ${CMAKE_COMMAND} --build .)
 endfunction()
 
-# There are a few `SKIP_LINTING` source/target propertiy combinations
+# There are a few `SKIP_LINTING` file set/source/target propertiy combinations
 # that affect the final result:
 #
-#  prop_sf   prop_tgt   result
-#  ---------------------------
-#    -         -         OFF
-#    OFF       -         OFF
-#    ON        -         ON
-#    -         OFF       OFF
-#    OFF       OFF       OFF
-#    ON        OFF       ON
-#    -         ON        ON
-#    OFF       ON        OFF
-#    ON        ON        ON
+#  prop_fs  prop_sf   prop_tgt   result
+#  ------------------------------------
+#    -        -         -         OFF
+#    OFF      -         -         OFF
+#    ON       -         -         ON
+#    -        OFF       -         OFF
+#    OFF      OFF       -         OFF
+#    ON       OFF       -         ON
+#    -        ON        -         ON
+#    OFF      ON        -         OFF
+#    ON       ON        -         ON
+#    -        -         OFF       OFF
+#    OFF      -         OFF       OFF
+#    ON       -         OFF       ON
+#    -        -         ON        ON
+#    OFF      -         ON        OFF
+#    ON       -         ON        ON
+#    -        OFF       OFF       OFF
+#    OFF      OFF       OFF       OFF
+#    ON       OFF       OFF       ON
+#    -        OFF       ON        OFF
+#    OFF      OFF       ON        OFF
+#    ON       OFF       ON        ON
+#    -        ON        OFF       ON
+#    OFF      ON        OFF       OFF
+#    ON       ON        OFF       ON
+#    -        ON        ON        ON
+#    OFF      ON        ON        OFF
+#    ON       ON        ON        ON
 #
 # where `-` means unset property.
 #
 # Here's the same table for convenience sorted by `result`:
 #
-#  prop_sf   prop_tgt   result
-#  ---------------------------
-#    -         -         OFF
-#    OFF       -         OFF
-#    -         OFF       OFF
-#    OFF       OFF       OFF
-#    OFF       ON        OFF
-#    ON        -         ON
-#    ON        OFF       ON
-#    -         ON        ON
-#    ON        ON        ON
+#  prop_fs  prop_sf   prop_tgt   result
+#  ------------------------------------
+#    -        -         -         OFF
+#    OFF      -         -         OFF
+#    -        OFF       -         OFF
+#    OFF      OFF       -         OFF
+#    OFF      ON        -         OFF
+#    -        -         OFF       OFF
+#    OFF      -         OFF       OFF
+#    OFF      -         ON        OFF
+#    -        OFF       OFF       OFF
+#    OFF      OFF       OFF       OFF
+#    -        OFF       ON        OFF
+#    OFF      OFF       ON        OFF
+#    OFF      ON        OFF       OFF
+#    OFF      ON        ON        OFF
+#    ON       -         -         ON
+#    ON       OFF       -         ON
+#    -        ON        -         ON
+#    ON       ON        -         ON
+#    ON       -         OFF       ON
+#    -        -         ON        ON
+#    ON       -         ON        ON
+#    ON       OFF       OFF       ON
+#    ON       OFF       ON        ON
+#    -        ON        OFF       ON
+#    ON       ON        OFF       ON
+#    -        ON        ON        ON
+#    ON       ON        ON        ON
 
 foreach(lang IN ITEMS C CXX)
   # Testing `SKIP_LINTING=OFF` (first half of the table above)
-  set(prop_sf_OFF_variants "-" OFF "-" OFF OFF)
-  set(prop_tgt_OFF_variants "-" "-" OFF OFF ON)
-  foreach(prop_fs prop_tgt IN ZIP_LISTS prop_sf_OFF_variants prop_tgt_OFF_variants)
-    run_skip_linting(${lang}_skip_linting_OFF "${prop_fs}" "${prop_tgt}")
+  set(prop_fs_OFF_variants  "-" OFF "-" OFF OFF "-" OFF OFF "-" OFF "-" OFF OFF OFF)
+  set(prop_sf_OFF_variants  "-" "-" OFF OFF ON  "-" "-" "-" OFF OFF OFF OFF ON  ON)
+  set(prop_tgt_OFF_variants "-" "-" "-" "-" "-" OFF OFF ON  OFF OFF ON  ON  OFF ON)
+  foreach(prop_fs prop_sf prop_tgt IN ZIP_LISTS prop_fs_OFF_variants prop_sf_OFF_variants prop_tgt_OFF_variants)
+    run_skip_linting(${lang}_skip_linting_OFF "${prop_fs}" "${prop_sf}" "${prop_tgt}")
   endforeach()
 
   # Testing `SKIP_LINTING=ON` (second half of the table above)
-  set(prop_sf_ON_variants ON ON "-" ON)
-  set(prop_tgt_ON_variants "-" OFF ON ON)
-  foreach(prop_fs prop_tgt IN ZIP_LISTS prop_sf_ON_variants prop_tgt_ON_variants)
-    run_skip_linting(${lang}_skip_linting_ON "${prop_fs}" "${prop_tgt}")
+  set(prop_fs_ON_variants  ON  ON  "-" ON  ON  "-" ON  ON  ON  "-" ON  "-" ON)
+  set(prop_sf_ON_variants  "-" OFF ON  ON  "-" "-" "-" OFF OFF ON  ON  ON  ON)
+  set(prop_tgt_ON_variants "-" "-" "-" "-" OFF ON  ON  OFF ON  OFF OFF ON  ON)
+  foreach(prop_fs prop_sf prop_tgt IN ZIP_LISTS prop_fs_ON_variants prop_sf_ON_variants prop_tgt_ON_variants)
+    run_skip_linting(${lang}_skip_linting_ON "${prop_fs}" "${prop_sf}" "${prop_tgt}")
     if(NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
-      run_skip_linting(${lang}-launch_skip_linting_ON "${prop_fs}" "${prop_tgt}")
+      run_skip_linting(${lang}-launch_skip_linting_ON "${prop_fs}" "${prop_sf}" "${prop_tgt}")
     endif()
   endforeach()
 endforeach()
diff --git a/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake b/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake
index ab8d646..a655339 100644
--- a/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake
+++ b/Tests/RunCMake/MultiLint/setup_skip_linter_test.cmake
@@ -12,7 +12,12 @@
   set(CMAKE_${lang}_CPPCHECK "${maybe_genex_pre}${PSEUDO_CPPCHECK}${maybe_genex_post}" -error)
 
   string(TOLOWER "${lang}" ext)
-  add_executable(main main.${ext})
+  add_executable(main)
+  target_sources(main PRIVATE FILE_SET SOURCES FILES main.${ext})
+
+  if(NOT prop_fs STREQUAL "-")
+    set_property(FILE_SET SOURCES TARGET main PROPERTY SKIP_LINTING ${prop_fs})
+  endif()
 
   if(NOT prop_sf STREQUAL "-")
     set_source_files_properties(main.${ext} PROPERTIES SKIP_LINTING ${prop_sf})
diff --git a/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake b/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake
new file mode 100644
index 0000000..0998a22
--- /dev/null
+++ b/Tests/RunCMake/Ninja/FileSetJobPool-check.cmake
@@ -0,0 +1,17 @@
+set(log "${RunCMake_BINARY_DIR}/FileSetJobPool-build/build.ninja")
+file(READ "${log}" build_file)
+if(NOT "${build_file}" MATCHES "pool = file_set_compile_pool")
+  string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "does not have expected line: pool = file_set_compile_pool")
+endif()
+if(NOT "${build_file}" MATCHES "pool = target_link_pool")
+  string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "does not have expected line: pool = target_link_pool")
+endif()
+# Even though `source_file_compile_pool` and `target_compile_pool` were defined as the target's compile jobs pool,
+# since the file set property overrides it with `file_set_compile_pool` pool, the source file compile job
+# and the target compile job pool should not exist in the generated Ninja file.
+if("${build_file}" MATCHES "pool = source_file_compile_pool")
+  string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "have unexpected line: pool = source_file_compile_pool")
+endif()
+if("${build_file}" MATCHES "pool = target_compile_pool")
+  string(CONCAT RunCMake_TEST_FAILED "Log file:\n ${log}\n" "have unexpected line: pool = target_compile_pool")
+endif()
diff --git a/Tests/RunCMake/Ninja/FileSetJobPool.cmake b/Tests/RunCMake/Ninja/FileSetJobPool.cmake
new file mode 100644
index 0000000..cd9dfad
--- /dev/null
+++ b/Tests/RunCMake/Ninja/FileSetJobPool.cmake
@@ -0,0 +1,16 @@
+set_property(GLOBAL PROPERTY JOB_POOLS source_file_compile_tool=2 file_set_compile_pool=2
+                                       target_compile_pool=4 target_link_pool=1)
+
+enable_language(C)
+
+set_property(SOURCE hello.c PROPERTY JOB_POOL_COMPILE "source_file_compile_pool")
+
+add_executable(hello)
+target_sources(hello PRIVATE FILE_SET SOURCES FILES hello.c)
+
+set_property(FILE_SET SOURCES TARGET hello PROPERTY JOB_POOL_COMPILE "file_set_compile_pool")
+
+set_property(TARGET hello PROPERTY JOB_POOL_COMPILE "target_compile_pool")
+set_property(TARGET hello PROPERTY JOB_POOL_LINK "target_link_pool")
+
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 32eda9d..65202f7 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -121,6 +121,7 @@
 run_cmake_with_options(CustomCommandDepfileAsByproduct -DCMAKE_BUILD_TYPE=Debug)
 run_cmake(CustomCommandJobPool)
 run_cmake(SourceFileJobPool)
+run_cmake(FileSetJobPool)
 run_cmake(JobPoolUsesTerminal)
 
 run_cmake(RspFileC)
diff --git a/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake b/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake
new file mode 100644
index 0000000..02b763a
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0216-NEW-check.cmake
@@ -0,0 +1,21 @@
+if(RunCMake_GENERATOR MATCHES "Ninja")
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+  else()
+    set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+  endif()
+  file(READ "${path}" build_ninja)
+
+  if(NOT build_ninja MATCHES "-package-name")
+    set(RunCMake_TEST_FAILED
+      "Expected -package-name in generated Ninja build files when CMP0216 is NEW")
+  endif()
+elseif(RunCMake_GENERATOR MATCHES "Xcode")
+  file(READ
+    "${RunCMake_TEST_BINARY_DIR}/CMP0216-NEW.xcodeproj/project.pbxproj"
+    pbxproj)
+  if(NOT pbxproj MATCHES "SWIFT_PACKAGE_NAME")
+    set(RunCMake_TEST_FAILED
+      "Expected SWIFT_PACKAGE_NAME in generated Xcode project when CMP0216 is NEW")
+  endif()
+endif()
diff --git a/Tests/RunCMake/Swift/CMP0216-NEW.cmake b/Tests/RunCMake/Swift/CMP0216-NEW.cmake
new file mode 100644
index 0000000..0aaf393
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0216-NEW.cmake
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 4.0)
+
+cmake_policy(SET CMP0216 NEW)
+include(CMP0216-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake b/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake
new file mode 100644
index 0000000..abfe69c
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0216-OLD-check.cmake
@@ -0,0 +1,20 @@
+if(RunCMake_GENERATOR MATCHES "Ninja")
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+  else()
+    set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+  endif()
+  file(READ "${path}" build_ninja)
+  if(_content MATCHES "-package-name")
+    set(RunCMake_TEST_FAILED
+      "Unexpected -package-name in generated Ninja build files when CMP0216 is OLD")
+  endif()
+elseif(RunCMake_GENERATOR MATCHES "Xcode")
+  file(READ
+    "${RunCMake_TEST_BINARY_DIR}/CMP0216-OLD.xcodeproj/project.pbxproj"
+    pbxproj)
+  if(pbxproj MATCHES "SWIFT_PACKAGE_NAME")
+    set(RunCMake_TEST_FAILED
+      "Unexpected SWIFT_PACKAGE_NAME in generated Xcode project when CMP0216 is OLD")
+  endif()
+endif()
diff --git a/Tests/RunCMake/Swift/CMP0216-OLD.cmake b/Tests/RunCMake/Swift/CMP0216-OLD.cmake
new file mode 100644
index 0000000..f3dc575
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0216-OLD.cmake
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 4.0)
+
+cmake_policy(SET CMP0216 OLD)
+include(CMP0216-common.cmake)
diff --git a/Tests/RunCMake/Swift/CMP0216-common.cmake b/Tests/RunCMake/Swift/CMP0216-common.cmake
new file mode 100644
index 0000000..ae695d8
--- /dev/null
+++ b/Tests/RunCMake/Swift/CMP0216-common.cmake
@@ -0,0 +1,3 @@
+enable_language(Swift)
+
+add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/CompileCommands.cmake b/Tests/RunCMake/Swift/CompileCommands.cmake
index f0a88da..2e95093 100644
--- a/Tests/RunCMake/Swift/CompileCommands.cmake
+++ b/Tests/RunCMake/Swift/CompileCommands.cmake
@@ -4,6 +4,7 @@
 if(POLICY CMP0195)
   cmake_policy(SET CMP0195 NEW)
 endif()
+cmake_policy(SET CMP0216 NEW)
 
 set(CMAKE_Swift_COMPILATION_MODE "singlefile")
 
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake
index ec60bf8..dcae06c 100644
--- a/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake
+++ b/Tests/RunCMake/Swift/EmitModuleSeparately-build-check.cmake
@@ -2,8 +2,17 @@
   REGEX MATCHALL
   "swiftc(\\.exe)?\"? [^\n]* -emit-module-path [^\n]*L\\.swiftmodule"
   swift_module_commands "${actual_stdout}")
-list(LENGTH swift_module_commands swift_module_command_count)
-if(swift_module_command_count LESS 2)
+
+set(emit_module_commands "${swift_module_commands}")
+list(FILTER emit_module_commands EXCLUDE REGEX " -c ")
+if(NOT emit_module_commands)
   string(APPEND RunCMake_TEST_FAILED
-    "Expected separate compile and emit-module commands for L, found ${swift_module_command_count} command(s) with '-emit-module-path ... L.swiftmodule'.\n")
+    "Expected an emit-module command with '-emit-module-path ... L.swiftmodule'.\n")
+endif()
+
+set(compile_commands "${swift_module_commands}")
+list(FILTER compile_commands INCLUDE REGEX " -c ")
+if(compile_commands)
+  string(APPEND RunCMake_TEST_FAILED
+    "Compile command (-c) should not contain '-emit-module-path' when emitting module separately.\n")
 endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake
new file mode 100644
index 0000000..5a0943e
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle-build-check.cmake
@@ -0,0 +1,26 @@
+string(
+  REGEX MATCHALL
+  "swiftc(\\.exe)?\"? [^\n]* -emit-module-path [^\n]*L\\.swiftmodule[^ \n]*"
+  swift_module_commands "${actual_stdout}")
+
+set(emit_module_commands "${swift_module_commands}")
+list(FILTER emit_module_commands EXCLUDE REGEX " -c ")
+if(NOT emit_module_commands)
+  string(APPEND RunCMake_TEST_FAILED
+    "Expected an emit-module command with '-emit-module-path ... L.swiftmodule'.\n")
+endif()
+
+set(compile_commands "${swift_module_commands}")
+list(FILTER compile_commands INCLUDE REGEX " -c ")
+if(compile_commands)
+  string(APPEND RunCMake_TEST_FAILED
+    "Compile command (-c) should not contain '-emit-module-path' when emitting module separately.\n")
+endif()
+
+# CMP0195=NEW: the emit-module path should be directory-style
+# (L.swiftmodule/<triple>.swiftmodule), not flat.
+list(FILTER emit_module_commands INCLUDE REGEX "L\\.swiftmodule(/|\\\\)[-_a-zA-Z0-9]+\\.swiftmodule")
+if(NOT emit_module_commands)
+  string(APPEND RunCMake_TEST_FAILED
+    "Expected directory-style -emit-module-path (L.swiftmodule/<triple>.swiftmodule) from CMP0195=NEW.\n")
+endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake
new file mode 100644
index 0000000..076a974
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyDirectoryStyle.cmake
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 4.0)
+
+cmake_policy(SET CMP0157 NEW)
+cmake_policy(SET CMP0195 NEW)
+cmake_policy(SET CMP0215 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+add_library(L STATIC L.swift)
+add_library(LClient STATIC LClient.swift)
+target_link_libraries(LClient PRIVATE L)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath-build-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath-build-check.cmake
new file mode 100644
index 0000000..1a0c488
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath-build-check.cmake
@@ -0,0 +1,11 @@
+# When the target's flags already contain -emit-module-path, the emit-module
+# edge should not append a second one that clobbers it.
+# Match command lines that have two -emit-module-path flags on the same line.
+string(
+  REGEX MATCHALL
+  "-emit-module-path [^\n]*-emit-module-path"
+  duplicate_flags "${actual_stdout}")
+if(duplicate_flags)
+  string(APPEND RunCMake_TEST_FAILED
+    "Found command with duplicate -emit-module-path flags.\n")
+endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath.cmake
new file mode 100644
index 0000000..1decca4
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyExistingModulePath.cmake
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 4.0)
+
+cmake_policy(SET CMP0157 NEW)
+cmake_policy(SET CMP0195 NEW)
+cmake_policy(SET CMP0215 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+add_library(L STATIC L.swift)
+add_library(LClient STATIC LClient.swift)
+target_link_libraries(LClient PRIVATE L)
+
+# Simulate a target whose flags already contain -emit-module-path with a
+# directory-style path (as seen with swift-syntax's CMake config).  Use the
+# real module triple so the path matches GetSwiftModulePath() and the build
+# would succeed outside of dry-run.
+target_compile_options(L PRIVATE
+  -emit-module-path ${CMAKE_CURRENT_BINARY_DIR}/L.swiftmodule/${CMAKE_Swift_MODULE_TRIPLE}.swiftmodule)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake
new file mode 100644
index 0000000..90038ea
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake
@@ -0,0 +1,14 @@
+# The link edge for a Swift library must implicitly depend on the
+# .swiftmodule so the emit-module edge runs even when no other target
+# in the build depends on it.
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+else()
+  set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+endif()
+file(READ "${path}" build_ninja)
+
+if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*L\\.swiftmodule")
+  string(APPEND RunCMake_TEST_FAILED
+    "Link edge for L does not depend on L.swiftmodule.\n")
+endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake
new file mode 100644
index 0000000..7ef69cf
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake
@@ -0,0 +1,12 @@
+cmake_policy(SET CMP0157 NEW)
+cmake_policy(SET CMP0215 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+# A standalone library with no dependent Swift targets.  The link edge
+# must still depend on the .swiftmodule so the emit-module edge runs.
+add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake
new file mode 100644
index 0000000..2aca41d
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake
@@ -0,0 +1,14 @@
+# Same as EmitModuleSeparatelyLinkDep but with a custom module directory.
+# The link edge must depend on the actual emit-module output path, not a
+# default that doesn't match the target's configuration.
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+else()
+  set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+endif()
+file(READ "${path}" build_ninja)
+
+if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*custom(/|\\\\)[^\n]*L\\.swiftmodule")
+  string(APPEND RunCMake_TEST_FAILED
+    "Link edge for L does not depend on custom/.../L.swiftmodule.\n")
+endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake
new file mode 100644
index 0000000..fbb3ac8
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake
@@ -0,0 +1,15 @@
+cmake_policy(SET CMP0157 NEW)
+cmake_policy(SET CMP0215 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+# A standalone library with a custom module output path (like swiftCore
+# in the Swift stdlib).  The link edge must depend on the actual
+# emit-module output, not GetSwiftModulePath().
+add_library(L STATIC L.swift)
+set_target_properties(L PROPERTIES
+  Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake
new file mode 100644
index 0000000..b42cfd1
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake
@@ -0,0 +1,28 @@
+# The Swift object compile edge must order-only depend on the .swiftmodule
+# produced by the separate emit-module edge.  Both edges share the same
+# -output-file-map; running them concurrently corrupts the module-level
+# swift-dependencies file referenced by that map.
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+else()
+  set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+endif()
+file(READ "${path}" build_ninja)
+
+# Find the object compile edge for L and confirm L.swiftmodule appears
+# after the order-only `||` separator.  The edge ends at the next `build `
+# at the start of a line (or end of file).
+string(REGEX MATCH
+  "build [^\n]*L\\.swift\\.o[^\n]*:[^\n]*(\n [^\n]+)*"
+  obj_edge "${build_ninja}")
+
+if(NOT obj_edge)
+  string(APPEND RunCMake_TEST_FAILED
+    "Could not find object compile edge for L.swift.\n")
+  return()
+endif()
+
+if(NOT obj_edge MATCHES "\\|\\|[^\n]*L\\.swiftmodule")
+  string(APPEND RunCMake_TEST_FAILED
+    "Object compile edge for L.swift is missing an order-only dependency on L.swiftmodule.\nEdge:\n${obj_edge}\n")
+endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake
new file mode 100644
index 0000000..6264453
--- /dev/null
+++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake
@@ -0,0 +1,14 @@
+cmake_policy(SET CMP0157 NEW)
+cmake_policy(SET CMP0215 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+# The compile and emit-module edges share the same -output-file-map.
+# Within the target, the object compile edge must order-only depend on the
+# .swiftmodule so the two edges do not run concurrently and corrupt the
+# shared module-level swift-dependencies entry.
+add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/MultiLangChain.cmake b/Tests/RunCMake/Swift/MultiLangChain.cmake
index f8dccc9..ec56cd9 100644
--- a/Tests/RunCMake/Swift/MultiLangChain.cmake
+++ b/Tests/RunCMake/Swift/MultiLangChain.cmake
@@ -1,5 +1,6 @@
 cmake_policy(SET CMP0157 NEW)
 cmake_policy(SET CMP0195 NEW)
+cmake_policy(SET CMP0216 NEW)
 
 enable_language(Swift C)
 
diff --git a/Tests/RunCMake/Swift/PackageName-check.cmake b/Tests/RunCMake/Swift/PackageName-check.cmake
new file mode 100644
index 0000000..cc9177b
--- /dev/null
+++ b/Tests/RunCMake/Swift/PackageName-check.cmake
@@ -0,0 +1,27 @@
+if(RunCMake_GENERATOR MATCHES "Ninja")
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja")
+  else()
+    set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+  endif()
+  file(READ "${path}" build_ninja)
+
+  if(NOT build_ninja MATCHES "Swift_COMPILER__L1[^\n]*(\n  [^\n]+)*\n  FLAGS = [^\n]*-package-name SwiftPackageTest")
+    string(APPEND RunCMake_TEST_FAILED
+      "Expected L1 compile FLAGS to contain -package-name SwiftPackageTest\n")
+  endif()
+
+  if(build_ninja MATCHES "Swift_COMPILER__L2[^\n]*(\n  [^\n]+)*\n  FLAGS = [^\n]*-package-name")
+    string(APPEND RunCMake_TEST_FAILED
+      "L2 compile FLAGS should not contain -package-name\n")
+  endif()
+
+elseif(RunCMake_GENERATOR MATCHES "Xcode")
+  file(READ
+    "${RunCMake_TEST_BINARY_DIR}/PackageName.xcodeproj/project.pbxproj"
+    pbxproj)
+  if(NOT pbxproj MATCHES "SWIFT_PACKAGE_NAME = SwiftPackageTest")
+    string(APPEND RunCMake_TEST_FAILED
+      "Expected SWIFT_PACKAGE_NAME = SwiftPackageTest in Xcode project for L1\n")
+  endif()
+endif()
diff --git a/Tests/RunCMake/Swift/PackageName.cmake b/Tests/RunCMake/Swift/PackageName.cmake
new file mode 100644
index 0000000..caf138b
--- /dev/null
+++ b/Tests/RunCMake/Swift/PackageName.cmake
@@ -0,0 +1,13 @@
+cmake_policy(SET CMP0157 NEW)
+
+enable_language(Swift)
+
+add_library(L1 L.swift)
+set_target_properties(L1 PROPERTIES
+  Swift_PACKAGE_NAME "SwiftPackageTest"
+)
+
+add_library(L2 L.swift)
+set_target_properties(L2 PROPERTIES
+  Swift_PACKAGE_NAME ""
+)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake
index 21f4f35..a56d6bf 100644
--- a/Tests/RunCMake/Swift/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -163,6 +163,34 @@
   endblock()
 
   block()
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyDirectoryStyle-build)
+    run_cmake(EmitModuleSeparatelyDirectoryStyle)
+    set(RunCMake_TEST_NO_CLEAN 1)
+    # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling
+    run_cmake_command(EmitModuleSeparatelyDirectoryStyle-build ${CMAKE_COMMAND} --build . -- -vn)
+  endblock()
+
+  block()
+    run_cmake(EmitModuleSeparatelyLinkDep)
+  endblock()
+
+  block()
+    run_cmake(EmitModuleSeparatelyLinkDepCustomPath)
+  endblock()
+
+  block()
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyExistingModulePath-build)
+    run_cmake(EmitModuleSeparatelyExistingModulePath)
+    set(RunCMake_TEST_NO_CLEAN 1)
+    # -v: verbose to capture executed commands -n: dry-run to avoid actually compiling
+    run_cmake_command(EmitModuleSeparatelyExistingModulePath-build ${CMAKE_COMMAND} --build . -- -vn)
+  endblock()
+
+  block()
+    run_cmake(EmitModuleSeparatelyOrdering)
+  endblock()
+
+  block()
     if(CMAKE_SYSTEM_NAME MATCHES Windows)
       set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build)
       run_cmake(ImportLibraryFlags)
@@ -172,6 +200,13 @@
   endblock()
 
   block()
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftModuleNameHyphen-build)
+    run_cmake(SwiftModuleNameHyphen)
+    set(RunCMake_TEST_NO_CLEAN 1)
+    run_cmake_command(SwiftModuleNameHyphen-build ${CMAKE_COMMAND} --build . -- -vn)
+  endblock()
+
+  block()
     set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SwiftLibraryModuleCommand-build)
     run_cmake(SwiftLibraryModuleCommand)
     set(RunCMake_TEST_NO_CLEAN 1)
@@ -182,3 +217,10 @@
   run_cmake(CMP0214-OLD)
   run_cmake(CMP0214-NEW-CMP0157-OLD)
 endif()
+
+if(NOT RunCMake_GENERATOR STREQUAL "Xcode" OR
+    (RunCMake_GENERATOR STREQUAL "Xcode" AND XCODE_VERSION VERSION_GREATER_EQUAL 15.0))
+  run_cmake(PackageName)
+  run_cmake(CMP0216-OLD)
+  run_cmake(CMP0216-NEW)
+endif()
diff --git a/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake b/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake
new file mode 100644
index 0000000..f0e0f6c
--- /dev/null
+++ b/Tests/RunCMake/Swift/SwiftModuleNameHyphen-build-check.cmake
@@ -0,0 +1,9 @@
+# The module name should have underscores, not hyphens.
+if(NOT actual_stdout MATCHES "-module-name L_hyphen")
+  string(APPEND RunCMake_TEST_FAILED
+    "Expected '-module-name L_hyphen', hyphens should be replaced with underscores.\n")
+endif()
+if(actual_stdout MATCHES "-module-name L-hyphen")
+  string(APPEND RunCMake_TEST_FAILED
+    "Found '-module-name L-hyphen', hyphens are not valid in Swift module names.\n")
+endif()
diff --git a/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake b/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake
new file mode 100644
index 0000000..b1fa8ce
--- /dev/null
+++ b/Tests/RunCMake/Swift/SwiftModuleNameHyphen.cmake
@@ -0,0 +1,11 @@
+cmake_policy(SET CMP0157 NEW)
+
+if(NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}")
+endif()
+
+enable_language(Swift)
+
+# A target with hyphens in the name.  Swift module names cannot contain
+# hyphens, so CMake should replace them with underscores.
+add_library(L-hyphen STATIC L.swift)
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index c239c98..18d85ba 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -57,6 +57,7 @@
    \* CMP0211
    \* CMP0214
    \* CMP0215
+   \* CMP0216
 
 Call Stack \(most recent call first\):
   CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake
index 2efd560..c96279a 100644
--- a/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake
+++ b/Tests/RunCMake/VerifyHeaderSets/VerifyInterfaceHeaderSets.cmake
@@ -83,3 +83,10 @@
 target_sources(skip_linting INTERFACE FILE_SET HEADERS FILES lang_test.h skip_linting.h)
 set_property(SOURCE skip_linting.h PROPERTY LANGUAGE C)
 set_property(SOURCE skip_linting.h PROPERTY SKIP_LINTING TRUE)
+
+add_library(skip_linting2 STATIC lib.c)
+target_sources(skip_linting2 INTERFACE FILE_SET HEADERS FILES lang_test.h)
+target_sources(skip_linting2 INTERFACE FILE_SET skip_headers TYPE HEADERS FILES skip_linting2.h)
+set_property(FILE_SET skip_headers TARGET skip_linting2 PROPERTY SKIP_LINTING TRUE)
+
+set_property(SOURCE skip_linting2.h PROPERTY LANGUAGE C)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake
index 1fc92e1..975a398 100644
--- a/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake
+++ b/Tests/RunCMake/VerifyHeaderSets/VerifyPrivateHeaderSets.cmake
@@ -86,3 +86,10 @@
 target_sources(skip_linting PRIVATE FILE_SET HEADERS FILES lang_test.h skip_linting.h)
 set_property(SOURCE skip_linting.h PROPERTY LANGUAGE C)
 set_property(SOURCE skip_linting.h PROPERTY SKIP_LINTING TRUE)
+
+add_library(skip_linting2 STATIC lib.c)
+target_sources(skip_linting2 PRIVATE FILE_SET HEADERS FILES lang_test.h)
+target_sources(skip_linting2 PRIVATE FILE_SET skip_headers TYPE HEADERS FILES skip_linting2.h)
+set_property(FILE_SET skip_headers TARGET skip_linting2 PROPERTY SKIP_LINTING TRUE)
+
+set_property(SOURCE skip_linting2.h PROPERTY LANGUAGE C)
diff --git a/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h b/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h
new file mode 100644
index 0000000..8a268bd
--- /dev/null
+++ b/Tests/RunCMake/VerifyHeaderSets/skip_linting2.h
@@ -0,0 +1,3 @@
+#error "This file should not be included"
+
+extern void skip_linting2_h(void);
diff --git a/Tests/RunCMake/ctest_build/BuildPreset-check.cmake b/Tests/RunCMake/ctest_build/BuildPreset-check.cmake
new file mode 100644
index 0000000..7dd0228
--- /dev/null
+++ b/Tests/RunCMake/ctest_build/BuildPreset-check.cmake
@@ -0,0 +1,10 @@
+file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml")
+if(build_xml_file)
+  file(READ "${build_xml_file}" build_xml)
+  if(NOT build_xml MATCHES "--preset.*my-build-preset")
+    set(RunCMake_TEST_FAILED
+        "Build.xml does not contain the expected --preset argument")
+  endif()
+else()
+  set(RunCMake_TEST_FAILED "Build.xml not found")
+endif()
diff --git a/Tests/RunCMake/ctest_build/CMakePresets.json.in b/Tests/RunCMake/ctest_build/CMakePresets.json.in
new file mode 100644
index 0000000..a676de1
--- /dev/null
+++ b/Tests/RunCMake/ctest_build/CMakePresets.json.in
@@ -0,0 +1,21 @@
+{
+  "version": 2,
+  "cmakeMinimumRequired": {
+    "major": 3,
+    "minor": 20,
+    "patch": 0
+  },
+  "configurePresets": [
+    {
+      "name": "my-configure-preset",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/../BuildPreset-build"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "my-build-preset",
+      "configurePreset": "my-configure-preset"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
index 34ef64f..5ade9be 100644
--- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
@@ -72,6 +72,15 @@
 unset(RunCMake_BUILD_COMMAND)
 
 set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
+set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPreset")
+configure_file(
+  "${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
+  "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
+  @ONLY)
+run_ctest_build(BuildPreset PRESET my-build-preset)
+unset(RunCMake_TEST_SOURCE_DIR)
+
+set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
 if(RunCMake_GENERATOR MATCHES "Ninja")
   function(run_NinjaLauncherSingleBuildFailure)
     set(LANG C)
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt
index a42e3f4..f6dcbc8 100644
--- a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt
@@ -1 +1 @@
- *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+ *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt
index 093eefb..cb14372 100644
--- a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt
@@ -1,3 +1,3 @@
  *Error when uploading file: .*/Configure\.xml
- *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+ *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
  *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt
index a42e3f4..f6dcbc8 100644
--- a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt
@@ -1 +1 @@
- *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+ *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt
index a42e3f4..f6dcbc8 100644
--- a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt
@@ -1 +1 @@
- *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+ *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVerify-OFF-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVerify-OFF-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVerify-ON-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVerify-ON-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVersion-1.1-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-TLSVersion-1.1-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt
index 24d412d..085e90a 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/FailDrop-http-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt
index 9c3402e..d1a06c4 100644
--- a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt
@@ -1,2 +1,2 @@
-Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
    Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
index 24d412d..085e90a 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:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).*
+Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|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_submit/PARTSDone-stderr.txt b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt
index 2c5f900..8210ee3 100644
--- a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt
+++ b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt
@@ -1,3 +1,3 @@
  *Error when uploading file: .*/Done\.xml
- *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
+ *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
  *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
index 730efa8..9f014d0 100644
--- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -260,6 +260,15 @@
 endfunction()
 run_environment()
 
+# Test setting of CTEST_TEST_COVERAGE_TOOL
+function(run_CTestTestCoverageTool)
+  set(CASE_CTEST_TEST_ARGS "COVERAGE_TOOL" "LLVM-COV")
+  set(CASE_TEST_PREFIX_CODE [[ unset(ENV{LLVM_PROFILE_FILE})]])
+
+  run_ctest(TestCTestTestCoverageTool -VV)
+endfunction()
+run_CTestTestCoverageTool()
+
 # test for OUTPUT_JUNIT
 run_ctest_test(OutputJUnit OUTPUT_JUNIT junit.xml REPEAT UNTIL_FAIL:2)
 
diff --git a/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt b/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt
new file mode 100644
index 0000000..768bb03
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestCTestTestCoverageTool-stdout.txt
@@ -0,0 +1,3 @@
+1: Using environment variable LLVM_PROFILE_FILE=[^
+]*/Tests/RunCMake/ctest_test/[^
+]*_%p.profraw
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
index f6391ad..1f4da93 100644
--- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
+++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@
   find_file command specified for VALIDATOR is not a function: check\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_file\):
+  find_file command specified for VALIDATOR is not a function: check2\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
index ec0ce9a..861fbf5 100644
--- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
+++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@
 endmacro()
 
 find_file(result NAMES input.txt VALIDATOR check)
+
+add_subdirectory(subdir)
+find_file(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_file/subdir/CMakeLists.txt b/Tests/RunCMake/find_file/subdir/CMakeLists.txt
new file mode 100644
index 0000000..1edb776
--- /dev/null
+++ b/Tests/RunCMake/find_file/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+macro(CHECK2 result path)
+endmacro()
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
index 8f7f8c4..c220085 100644
--- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
+++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@
   find_library command specified for VALIDATOR is not a function: check\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_library\):
+  find_library command specified for VALIDATOR is not a function: check2\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
index fa90d72..f652828 100644
--- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
+++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@
 endmacro()
 
 find_library(result NAMES input.txt VALIDATOR check)
+
+add_subdirectory(subdir)
+find_library(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_library/subdir/CMakeLists.txt b/Tests/RunCMake/find_library/subdir/CMakeLists.txt
new file mode 100644
index 0000000..1edb776
--- /dev/null
+++ b/Tests/RunCMake/find_library/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+macro(CHECK2 result path)
+endmacro()
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
index 9f0e3e8..bff9f35 100644
--- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
+++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@
   find_path command specified for VALIDATOR is not a function: check\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_path\):
+  find_path command specified for VALIDATOR is not a function: check2\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
index 62b44ed..f1faded 100644
--- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
+++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@
 endmacro()
 
 find_path(result NAMES input.txt VALIDATOR check)
+
+add_subdirectory(subdir)
+find_path(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_path/subdir/CMakeLists.txt b/Tests/RunCMake/find_path/subdir/CMakeLists.txt
new file mode 100644
index 0000000..1edb776
--- /dev/null
+++ b/Tests/RunCMake/find_path/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+macro(CHECK2 result path)
+endmacro()
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
index 949f29d..567e577 100644
--- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
+++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@
   find_program command specified for VALIDATOR is not a function: check\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_program\):
+  find_program command specified for VALIDATOR is not a function: check2\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
index 43bb9ec..3e5e579 100644
--- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
+++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@
 endmacro()
 
 find_program(result NAMES input.txt VALIDATOR check)
+
+add_subdirectory(subdir)
+find_program(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_program/subdir/CMakeLists.txt b/Tests/RunCMake/find_program/subdir/CMakeLists.txt
new file mode 100644
index 0000000..1edb776
--- /dev/null
+++ b/Tests/RunCMake/find_program/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@
+
+macro(CHECK2 result path)
+endmacro()
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt
new file mode 100644
index 0000000..d742940
--- /dev/null
+++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at DIRECTORY-AbsoluteDest-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake
new file mode 100644
index 0000000..a76ddb4
--- /dev/null
+++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake
@@ -0,0 +1 @@
+install(DIRECTORY DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt
new file mode 100644
index 0000000..689d48b
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at FILES-AbsoluteDest-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake
new file mode 100644
index 0000000..28cb56b
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake
@@ -0,0 +1 @@
+install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt
new file mode 100644
index 0000000..f320131
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,2 @@
+CMake Warning \(install-absolute-destination\) at FILES-AbsoluteDest-warn\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake
new file mode 100644
index 0000000..28cb56b
--- /dev/null
+++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake
@@ -0,0 +1 @@
+install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index ff8e812..5e40e88 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -82,6 +82,14 @@
 run_cmake(FILES-DESTINATION-bad)
 run_cmake(FILES-RENAME-bad)
 run_cmake(TARGETS-DESTINATION-bad)
+run_cmake_with_options(DIRECTORY-AbsoluteDest-error -Werror=install-absolute-destination)
+run_cmake_with_options(FILES-AbsoluteDest-error -Werror=install-absolute-destination)
+run_cmake_with_options(FILES-AbsoluteDest-warn -Winstall-absolute-destination)
+run_cmake_with_options(TARGETS-AbsoluteDest-error -Werror=install-absolute-destination)
+run_cmake_with_options(TARGETS-AbsoluteDest-warn -Winstall-absolute-destination)
+run_cmake_with_options(TARGETS-AbsoluteDest-archive-error -Werror=install-absolute-destination)
+run_cmake_with_options(TARGETS-AbsoluteDest-library-error -Werror=install-absolute-destination)
+run_cmake_with_options(TARGETS-AbsoluteDest-runtime-error -Werror=install-absolute-destination)
 run_cmake(EXPORT-Component)
 run_cmake(EXPORT-FindDependencyExportGate)
 run_cmake(EXPORT-OldIFace)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt
new file mode 100644
index 0000000..e091ab0
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-archive-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/archive\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake
new file mode 100644
index 0000000..3ecc7e3
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+add_library(mylib STATIC empty.c)
+install(TARGETS mylib ARCHIVE DESTINATION /absolute/archive)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt
new file mode 100644
index 0000000..22c0d98
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake
new file mode 100644
index 0000000..b16af5c
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake
@@ -0,0 +1,12 @@
+enable_language(C)
+add_library(mylib STATIC empty.c)
+
+# FIXME(#27770): This diagnostic is issued at generate time. Currently, the
+# install generator doesn't capture the immediate diagnostic state, only the
+# top-most state of the current subdirectory, which we cannot affect (because
+# this test was include()d. When we fix that, we should test by changing the
+# diagnostic action here rather than by -W... in the test CLI arguments.
+# cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION SEND_ERROR)
+
+cmake_diagnostic(PROMOTE CMD_INSTALL_ABSOLUTE_DESTINATION WARN)
+install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt
new file mode 100644
index 0000000..368434d
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-library-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/lib\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake
new file mode 100644
index 0000000..b5be8fd
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+add_library(mylib MODULE empty.c)
+install(TARGETS mylib LIBRARY DESTINATION /absolute/lib)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt
new file mode 100644
index 0000000..279fb88
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-runtime-error\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/bin\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake
new file mode 100644
index 0000000..638f7fb
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+add_executable(myexe empty.c)
+install(TARGETS myexe RUNTIME DESTINATION /absolute/bin)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt
new file mode 100644
index 0000000..730a8ed
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,2 @@
+CMake Warning \(install-absolute-destination\) at TARGETS-AbsoluteDest-warn\.cmake:[0-9]+ \(install\):
+  INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake
new file mode 100644
index 0000000..e2ca4be
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+add_library(mylib STATIC empty.c)
+install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt
new file mode 100644
index 0000000..d9ba38d
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude-stderr.txt
@@ -0,0 +1,2 @@
+^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
+mylist is: DO_NOT_FILTER_THIS;thisisanitem$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake
new file mode 100644
index 0000000..7b1f349
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-Exclude.cmake
@@ -0,0 +1,50 @@
+# Predicate: returns TRUE for items starting with "FILTER_THIS_"
+function(starts_with_filter input result)
+  if(input MATCHES "^FILTER_THIS_")
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    set(${result} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
+message("mylist was: ${mylist}")
+list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
+message("mylist is: ${mylist}")
+
+# EXCLUDE with macro predicate
+macro(is_short input result)
+  string(LENGTH "${input}" _len)
+  if(_len LESS 6)
+    set(${result} TRUE)
+  else()
+    set(${result} FALSE)
+  endif()
+endmacro()
+
+set(mylist ab cdefgh ij klmnop qr)
+list(FILTER mylist EXCLUDE PREDICATE is_short)
+if(NOT mylist STREQUAL "cdefgh;klmnop")
+  message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE macro) is \"${mylist}\", expected \"cdefgh;klmnop\"")
+endif()
+
+# EXCLUDE on empty list
+set(empty_list "")
+list(FILTER empty_list EXCLUDE PREDICATE starts_with_filter)
+if(NOT empty_list STREQUAL "")
+  message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"")
+endif()
+
+# EXCLUDE where nothing matches (all elements kept)
+set(mylist alpha bravo charlie)
+list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
+if(NOT mylist STREQUAL "alpha;bravo;charlie")
+  message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE no-match) is \"${mylist}\", expected \"alpha;bravo;charlie\"")
+endif()
+
+# EXCLUDE where everything matches (all elements removed)
+set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C)
+list(FILTER mylist EXCLUDE PREDICATE starts_with_filter)
+if(NOT mylist STREQUAL "")
+  message(FATAL_ERROR "FILTER(EXCLUDE PREDICATE all-match) is \"${mylist}\", expected \"\"")
+endif()
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt
new file mode 100644
index 0000000..5e5280d
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-Include-stderr.txt
@@ -0,0 +1,2 @@
+^mylist was: FILTER_THIS_BIT;DO_NOT_FILTER_THIS;thisisanitem;FILTER_THIS_THING
+mylist is: FILTER_THIS_BIT;FILTER_THIS_THING$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake
new file mode 100644
index 0000000..412ff25
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-Include.cmake
@@ -0,0 +1,51 @@
+# Predicate: returns TRUE for items starting with "FILTER_THIS_"
+function(starts_with_filter input result)
+  if(input MATCHES "^FILTER_THIS_")
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    set(${result} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Predicate as macro
+macro(is_short input result)
+  string(LENGTH "${input}" _len)
+  if(_len LESS 6)
+    set(${result} TRUE)
+  else()
+    set(${result} FALSE)
+  endif()
+endmacro()
+
+set(mylist FILTER_THIS_BIT DO_NOT_FILTER_THIS thisisanitem FILTER_THIS_THING)
+message("mylist was: ${mylist}")
+list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
+message("mylist is: ${mylist}")
+
+# INCLUDE with macro predicate
+set(mylist ab cdefgh ij klmnop qr)
+list(FILTER mylist INCLUDE PREDICATE is_short)
+if(NOT mylist STREQUAL "ab;ij;qr")
+  message(FATAL_ERROR "FILTER(INCLUDE PREDICATE macro) is \"${mylist}\", expected \"ab;ij;qr\"")
+endif()
+
+# INCLUDE on empty list
+set(empty_list "")
+list(FILTER empty_list INCLUDE PREDICATE starts_with_filter)
+if(NOT empty_list STREQUAL "")
+  message(FATAL_ERROR "FILTER(INCLUDE PREDICATE empty) is \"${empty_list}\", expected \"\"")
+endif()
+
+# INCLUDE where nothing matches (all elements removed)
+set(mylist alpha bravo charlie)
+list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
+if(NOT mylist STREQUAL "")
+  message(FATAL_ERROR "FILTER(INCLUDE PREDICATE no-match) is \"${mylist}\", expected \"\"")
+endif()
+
+# INCLUDE where everything matches
+set(mylist FILTER_THIS_A FILTER_THIS_B FILTER_THIS_C)
+list(FILTER mylist INCLUDE PREDICATE starts_with_filter)
+if(NOT mylist STREQUAL "FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C")
+  message(FATAL_ERROR "FILTER(INCLUDE PREDICATE all-match) is \"${mylist}\", expected \"FILTER_THIS_A;FILTER_THIS_B;FILTER_THIS_C\"")
+endif()
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt
new file mode 100644
index 0000000..2fbc148
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at FILTER-PREDICATE-NoOutput\.cmake:6 \(list\):
+  list sub-command FILTER, mode PREDICATE: function "bad_predicate" did not
+  set the output variable\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake
new file mode 100644
index 0000000..f1f4621
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-NoOutput.cmake
@@ -0,0 +1,6 @@
+function(bad_predicate in out)
+  # Deliberately does NOT set ${out}
+endfunction()
+
+set(mylist alpha bravo charlie)
+list(FILTER mylist INCLUDE PREDICATE bad_predicate)
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt
new file mode 100644
index 0000000..e3c7a10
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at FILTER-PREDICATE-TooManyArguments\.cmake:6 \(list\):
+  list sub-command FILTER, mode PREDICATE requires five arguments\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake
new file mode 100644
index 0000000..87a8532
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-TooManyArguments.cmake
@@ -0,0 +1,6 @@
+function(my_predicate in out)
+  set(${out} TRUE PARENT_SCOPE)
+endfunction()
+
+set(mylist alpha bravo charlie)
+list(FILTER mylist INCLUDE PREDICATE my_predicate extra_arg)
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-result.txt b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt
new file mode 100644
index 0000000..2b6234b
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at FILTER-PREDICATE-UnknownFunction\.cmake:2 \(list\):
+  list sub-command FILTER, mode PREDICATE: unknown function
+  "no_such_function"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake
new file mode 100644
index 0000000..48461bd
--- /dev/null
+++ b/Tests/RunCMake/list/FILTER-PREDICATE-UnknownFunction.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(FILTER mylist INCLUDE PREDICATE no_such_function)
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake
index dd4264c..1fd86a9 100644
--- a/Tests/RunCMake/list/RunCMakeTest.cmake
+++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -37,6 +37,11 @@
 run_cmake(FILTER-REGEX-InvalidOperator)
 run_cmake(FILTER-REGEX-Valid0)
 run_cmake(FILTER-REGEX-Valid1)
+run_cmake(FILTER-PREDICATE-UnknownFunction)
+run_cmake(FILTER-PREDICATE-NoOutput)
+run_cmake(FILTER-PREDICATE-TooManyArguments)
+run_cmake(FILTER-PREDICATE-Include)
+run_cmake(FILTER-PREDICATE-Exclude)
 
 run_cmake(JOIN-NoArguments)
 run_cmake(JOIN-NoVariable)
@@ -65,6 +70,7 @@
 run_cmake(TRANSFORM-REPLACE-InvalidReplace1)
 run_cmake(TRANSFORM-REPLACE-InvalidReplace2)
 run_cmake(TRANSFORM-APPLY-NoFunction)
+run_cmake(TRANSFORM-APPLY-UnknownFunction)
 run_cmake(TRANSFORM-APPLY-NoOutput)
 # 'selector' oriented tests
 run_cmake(TRANSFORM-Selector-REGEX-NoArguments)
@@ -81,6 +87,9 @@
 run_cmake(TRANSFORM-Selector-FOR-ZeroStepArgument)
 run_cmake(TRANSFORM-Selector-FOR-NegativeStepArgument)
 run_cmake(TRANSFORM-Selector-FOR-BackwardsRange)
+run_cmake(TRANSFORM-Selector-PREDICATE-NoArguments)
+run_cmake(TRANSFORM-Selector-PREDICATE-UnknownFunction)
+run_cmake(TRANSFORM-Selector-PREDICATE-NoOutput)
 # 'output' oriented tests
 run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-NoArguments)
 run_cmake(TRANSFORM-Output-OUTPUT_VARIABLE-TooManyArguments)
@@ -93,6 +102,7 @@
 run_cmake(TRANSFORM-PREPEND)
 run_cmake(TRANSFORM-REPLACE)
 run_cmake(TRANSFORM-APPLY)
+run_cmake(TRANSFORM-PREDICATE)
 run_cmake(CMP0186)
 
 # argument tests
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-result.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt
new file mode 100644
index 0000000..cb59954
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-APPLY-UnknownFunction\.cmake:2 \(list\):
+  list sub-command TRANSFORM, action APPLY: unknown function
+  "no_such_function"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake
new file mode 100644
index 0000000..5275439
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-APPLY-UnknownFunction.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist APPLY no_such_function)
diff --git a/Tests/RunCMake/list/TRANSFORM-APPLY.cmake b/Tests/RunCMake/list/TRANSFORM-APPLY.cmake
index 14fe360..68e1528 100644
--- a/Tests/RunCMake/list/TRANSFORM-APPLY.cmake
+++ b/Tests/RunCMake/list/TRANSFORM-APPLY.cmake
@@ -104,3 +104,28 @@
 if(NOT mylist STREQUAL ";;")
   message(FATAL_ERROR "TRANSFORM(APPLY make_empty) is \"${mylist}\", expected is \";;\"")
 endif()
+
+# Recursive APPLY: an APPLY function that itself calls list(TRANSFORM APPLY).
+# The inner function returns the output variable name it was given.
+function(return_out_var_name in out)
+  set(${out} "${out}" PARENT_SCOPE)
+endfunction()
+
+# The outer function triggers a nested APPLY and verifies that the inner
+# output variable name differs from its own.
+function(inner_name_is_different in out)
+  set(_inner x)
+  list(TRANSFORM _inner APPLY return_out_var_name OUTPUT_VARIABLE _inner_out)
+  # _inner_out now holds the inner output variable name
+  if("${out}" STREQUAL "${_inner_out}")
+    set(${out} "FALSE" PARENT_SCOPE)
+  else()
+    set(${out} "TRUE" PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist a b c)
+list(TRANSFORM mylist APPLY inner_name_is_different OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "TRUE;TRUE;TRUE")
+  message(FATAL_ERROR "TRANSFORM(APPLY nested smoke) is \"${output}\", expected \"TRUE;TRUE;TRUE\"")
+endif()
diff --git a/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake b/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake
new file mode 100644
index 0000000..103f021
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-PREDICATE.cmake
@@ -0,0 +1,100 @@
+# Predicate: returns TRUE for items starting with "b"
+function(starts_with_b input result)
+  if(input MATCHES "^b")
+    set(${result} TRUE PARENT_SCOPE)
+  else()
+    set(${result} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
+# Predicate as macro
+macro(is_short input result)
+  string(LENGTH "${input}" _len)
+  if(_len LESS 4)
+    set(${result} TRUE)
+  else()
+    set(${result} FALSE)
+  endif()
+endmacro()
+
+set(mylist alpha bravo charlie bravo_two delta)
+
+# Basic PREDICATE with TOUPPER - only items starting with "b" are uppercased
+list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "alpha;BRAVO;charlie;BRAVO_TWO;delta")
+  message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE) is \"${output}\", expected \"alpha;BRAVO;charlie;BRAVO_TWO;delta\"")
+endif()
+
+# Verify original list unchanged (OUTPUT_VARIABLE)
+if(NOT mylist STREQUAL "alpha;bravo;charlie;bravo_two;delta")
+  message(FATAL_ERROR "Original list modified: \"${mylist}\"")
+endif()
+
+# PREDICATE in-place
+list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b)
+if(NOT mylist STREQUAL "alpha;BRAVO;charlie;BRAVO_TWO;delta")
+  message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE in-place) is \"${mylist}\", expected \"alpha;BRAVO;charlie;BRAVO_TWO;delta\"")
+endif()
+
+# PREDICATE with macro
+set(mylist ab cde fg hijklm no)
+list(TRANSFORM mylist TOUPPER PREDICATE is_short OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "AB;CDE;FG;hijklm;NO")
+  message(FATAL_ERROR "TRANSFORM(TOUPPER PREDICATE macro) is \"${output}\", expected \"AB;CDE;FG;hijklm;NO\"")
+endif()
+
+# PREDICATE combined with APPLY
+function(add_prefix in out)
+  set(${out} "prefix_${in}" PARENT_SCOPE)
+endfunction()
+
+set(mylist alpha bravo charlie delta)
+list(TRANSFORM mylist APPLY add_prefix PREDICATE starts_with_b OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "alpha;prefix_bravo;charlie;delta")
+  message(FATAL_ERROR "TRANSFORM(APPLY PREDICATE) is \"${output}\", expected \"alpha;prefix_bravo;charlie;delta\"")
+endif()
+
+# PREDICATE on empty list
+set(empty_list "")
+list(TRANSFORM empty_list TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "")
+  message(FATAL_ERROR "TRANSFORM(PREDICATE empty) is \"${output}\", expected \"\"")
+endif()
+
+# PREDICATE where nothing matches (all elements unchanged)
+set(mylist alpha charlie delta)
+list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "alpha;charlie;delta")
+  message(FATAL_ERROR "TRANSFORM(PREDICATE no-match) is \"${output}\", expected \"alpha;charlie;delta\"")
+endif()
+
+# PREDICATE where everything matches
+set(mylist bravo bronze bull)
+list(TRANSFORM mylist TOUPPER PREDICATE starts_with_b OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "BRAVO;BRONZE;BULL")
+  message(FATAL_ERROR "TRANSFORM(PREDICATE all-match) is \"${output}\", expected \"BRAVO;BRONZE;BULL\"")
+endif()
+
+# Recursive PREDICATE: a PREDICATE function that itself calls list(TRANSFORM APPLY).
+# The inner function returns the output variable name it was given.
+function(return_pred_var_name in out)
+  set(${out} "${out}" PARENT_SCOPE)
+endfunction()
+
+# The outer predicate triggers a nested APPLY and verifies that the inner
+# output variable name differs from its own result variable.
+function(inner_pred_name_is_different input result)
+  set(_inner x)
+  list(TRANSFORM _inner APPLY return_pred_var_name OUTPUT_VARIABLE _inner_names)
+  if("${result}" STREQUAL "${_inner_names}")
+    set(${result} FALSE PARENT_SCOPE)
+  else()
+    set(${result} TRUE PARENT_SCOPE)
+  endif()
+endfunction()
+
+set(mylist x y z)
+list(TRANSFORM mylist TOUPPER PREDICATE inner_pred_name_is_different OUTPUT_VARIABLE output)
+if(NOT output STREQUAL "X;Y;Z")
+  message(FATAL_ERROR "TRANSFORM(PREDICATE nested smoke) is \"${output}\", expected \"X;Y;Z\"")
+endif()
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt
new file mode 100644
index 0000000..22c4133
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-PREDICATE-NoArguments\.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector PREDICATE expects 'function name'
+  argument\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake
new file mode 100644
index 0000000..f263d8b
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoArguments.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER PREDICATE)
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt
new file mode 100644
index 0000000..87310b2
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-PREDICATE-NoOutput\.cmake:6 \(list\):
+  list sub-command TRANSFORM, selector PREDICATE: function "bad_predicate"
+  did not set the output variable\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake
new file mode 100644
index 0000000..73b18c2
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-NoOutput.cmake
@@ -0,0 +1,6 @@
+function(bad_predicate in out)
+  # Deliberately does NOT set ${out}
+endfunction()
+
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER PREDICATE bad_predicate)
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-result.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt
new file mode 100644
index 0000000..21bea51
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at TRANSFORM-Selector-PREDICATE-UnknownFunction\.cmake:2 \(list\):
+  list sub-command TRANSFORM, selector PREDICATE: unknown function
+  "no_such_function"\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake
new file mode 100644
index 0000000..d497288
--- /dev/null
+++ b/Tests/RunCMake/list/TRANSFORM-Selector-PREDICATE-UnknownFunction.cmake
@@ -0,0 +1,2 @@
+set(mylist alpha bravo charlie)
+list(TRANSFORM mylist TOUPPER PREDICATE no_such_function)
diff --git a/Tests/RunCMake/math/MATH-DivideMinByMinusOne-result.txt b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt
new file mode 100644
index 0000000..7ea1553
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideMinByMinusOne-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at MATH-DivideMinByMinusOne\.cmake:1 \(math\):
+  math cannot evaluate the expression: "~9223372036854775807/-1": signed
+  integer overflow in division\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake b/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake
new file mode 100644
index 0000000..38277b8
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-DivideMinByMinusOne.cmake
@@ -0,0 +1 @@
+math(EXPR var "~9223372036854775807/-1")
diff --git a/Tests/RunCMake/math/MATH-ModMinByMinusOne-result.txt b/Tests/RunCMake/math/MATH-ModMinByMinusOne-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-ModMinByMinusOne-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt b/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt
new file mode 100644
index 0000000..f2d7a1b
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-ModMinByMinusOne-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at MATH-ModMinByMinusOne\.cmake:1 \(math\):
+  math cannot evaluate the expression: "~9223372036854775807%-1": signed
+  integer overflow in modulo\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake b/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake
new file mode 100644
index 0000000..27e5f46
--- /dev/null
+++ b/Tests/RunCMake/math/MATH-ModMinByMinusOne.cmake
@@ -0,0 +1 @@
+math(EXPR var "~9223372036854775807%-1")
diff --git a/Tests/RunCMake/math/RunCMakeTest.cmake b/Tests/RunCMake/math/RunCMakeTest.cmake
index cda0094..3e8a499 100644
--- a/Tests/RunCMake/math/RunCMakeTest.cmake
+++ b/Tests/RunCMake/math/RunCMakeTest.cmake
@@ -8,6 +8,8 @@
 run_cmake(MATH-ToleratedExpression)
 run_cmake(MATH-DivideByZero)
 run_cmake(MATH-ModByZero)
+run_cmake(MATH-DivideMinByMinusOne)
+run_cmake(MATH-ModMinByMinusOne)
 
 if(CMake_TEST_MATH_OVERFLOW)
   run_cmake_script(Overflow)
diff --git a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
index 8265e32..801c9f6 100644
--- a/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
+++ b/Utilities/ClangTidyModule/OstringstreamUseCmstrcatCheck.cxx
@@ -18,13 +18,10 @@
 
 void OstringstreamUseCmstrcatCheck::registerMatchers(MatchFinder* Finder)
 {
-  Finder->addMatcher(
-    typeLoc(unless(elaboratedTypeLoc()),
-            optionally(hasParent(elaboratedTypeLoc().bind("parentType"))),
-            loc(qualType(
-              hasDeclaration(namedDecl(hasName("::std::ostringstream"))))))
-      .bind("ostringstream"),
-    this);
+  Finder->addMatcher(typeLoc(loc(qualType(hasDeclaration(
+                               namedDecl(hasName("::std::ostringstream"))))))
+                       .bind("ostringstream"),
+                     this);
 }
 
 void OstringstreamUseCmstrcatCheck::check(
diff --git a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
index 5f28054..cf75bfc 100644
--- a/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
+++ b/Utilities/ClangTidyModule/UseCmsysFstreamCheck.cxx
@@ -90,8 +90,7 @@
     loc(usingType(throughUsingDecl(namedDecl(hasName(CmsysName)))));
 
   Finder->addMatcher(
-    typeLoc(IsStd, unless(IsCmsys), unless(elaboratedTypeLoc()),
-            optionally(hasParent(elaboratedTypeLoc().bind("parentType"))),
+    typeLoc(IsStd, unless(IsCmsys),
             optionally(hasParent(nestedNameSpecifierLoc().bind("parentName"))))
       .bind(Bind),
     this);
diff --git a/Utilities/Scripts/regenerate-presets.py b/Utilities/Scripts/regenerate-presets.py
index 37f801a..665b712 100755
--- a/Utilities/Scripts/regenerate-presets.py
+++ b/Utilities/Scripts/regenerate-presets.py
@@ -31,11 +31,12 @@
 SCHEMA_JSON_FILENAME = 'schema.json'
 
 WORKSPACE = Path(__file__).parent.parent.parent.absolute()
+SCRIPT_REL = Path(__file__).relative_to(WORKSPACE)
 PRESETS = WORKSPACE / PRESETS_REL
 DIAGNOSTICS = WORKSPACE / 'Source/cmDiagnostics.h'
 
 RST_BANNER = f"""\
-.. This file was generated by {Path(__file__).relative_to(WORKSPACE)}
+.. This file was generated by {SCRIPT_REL.as_posix()}
    from {PRESETS_REL}/{SCHEMA_YAML_FILENAME}. Do not edit.
 """
 
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index 3964f5a..213b297 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_19_0"
+readonly tag="curl-8_20_0"
 readonly shortlog=false
 readonly exact_tree_match=false
 readonly paths="
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py
index ca0b7a1..27049c7 100644
--- a/Utilities/Sphinx/cmake.py
+++ b/Utilities/Sphinx/cmake.py
@@ -234,6 +234,7 @@
 _cmake_index_objs = {
     'command':    _cmake_index_entry('command'),
     'cpack_gen':  _cmake_index_entry('cpack generator'),
+    'diagnostic': _cmake_index_entry('diagnostic'),
     'envvar':     _cmake_index_entry('envvar'),
     'generator':  _cmake_index_entry('generator'),
     'genex':      _cmake_index_entry('genex'),
@@ -523,6 +524,79 @@
         return super().run()
 
 
+class CMakeDiagnosticObject(CMakeObject):
+    object_type = 'diagnostic'
+    required_arguments = 0
+    optional_arguments = 0
+
+    DEFAULT_CHOICES = {'ignore', 'warn', 'error'}
+
+    def default_option(argument):
+        return directives.choice(
+            argument, CMakeDiagnosticObject.DEFAULT_CHOICES)
+
+    option_spec = {
+        'default': default_option,
+        'parent': directives.unchanged,
+    }
+
+    def __init__(self, *args, **kwargs):
+        self.targetname = None
+        super().__init__(*args, **kwargs)
+
+    def _preset_name(self) -> str:
+        sep = False
+        out = ''
+        for c in self.targetname[4:]:
+            if c == '_':
+                sep = True
+            elif sep:
+                out += c
+                sep = False
+            else:
+                out += c.lower()
+        return out
+
+    def _build_field(self, name: str, content: str | list[Node]) -> Node:
+        if type(content) is not list:
+            content = self.parse_text_to_nodes(content)
+
+        name_node = nodes.field_name(text=name)
+        body_node = nodes.field_body('', *content)
+        return nodes.field('', name_node, body_node)
+
+    def _build_cli(self) -> list[Node]:
+        cname = self.targetname[4:].lower().replace('_', '-')
+        ctext = f'-W[no-][error=]{cname}'
+        return self.parse_text_to_nodes(f':option:`{ctext} <cmake -W>`')
+
+    def _build_preset_refs(self) -> list[Node]:
+        p = self._preset_name()
+        w = f':preset:`warnings.{p} <configurePresets.warnings.{p}>`'
+        e = f':preset:`errors.{p} <configurePresets.errors.{p}>`'
+        return self.parse_text_to_nodes(f'{w}, {e}')
+
+    def run(self) -> list[Node]:
+        self.domain, self.objtype = self.name.split(':', 1)
+        doc = self.state.document
+        self.targetname = doc.next_node(nodes.title).astext()
+
+        default = self.options['default'].capitalize()
+        parent = self.options.get('parent')
+
+        headers = nodes.field_list()
+        headers += self._build_field('Command Line', self._build_cli())
+        headers += self._build_field('Presets', self._build_preset_refs())
+        headers += self._build_field('Default', default)
+
+        if parent:
+            parentRef = self.parse_text_to_nodes(f':diagnostic:`{parent}`')
+            headers += self._build_field('Parent', parentRef)
+
+        content = self.parse_content_to_nodes()
+        return [headers] + content
+
+
 class CMakeReferenceRole:
     # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'.
     _re = re.compile(r'^(.+?)(\s*)(?<!\x00)<(.*?)>$', re.DOTALL)
@@ -680,6 +754,7 @@
     object_types = {
         'command':    ObjType('command',    'command'),
         'cpack_gen':  ObjType('cpack_gen',  'cpack_gen'),
+        'diagnostic': ObjType('diagnostic', 'diagnostic'),
         'envvar':     ObjType('envvar',     'envvar'),
         'generator':  ObjType('generator',  'generator'),
         'genex':      ObjType('genex',      'genex'),
@@ -700,6 +775,7 @@
     directives = {
         'command':    CMakeObject,
         'envvar':     CMakeObject,
+        'diagnostic': CMakeDiagnosticObject,
         'genex':      CMakeGenexObject,
         'signature':  CMakeSignatureObject,
         'variable':   CMakeObject,
@@ -710,6 +786,7 @@
         'cref':       CMakeCRefRole(),
         'command':    CMakeXRefRole(fix_parens=True, lowercase=True),
         'cpack_gen':  CMakeXRefRole(),
+        'diagnostic': CMakeXRefRole(),
         'envvar':     CMakeXRefRole(),
         'generator':  CMakeXRefRole(),
         'genex':      CMakeXRefRole(),
@@ -729,11 +806,12 @@
         'preset':     CMakeXRefRole(lowercase=True, warn_dangling=True),
         # Roles for program-specific command-line options without the program
         # name (which add the name to form the ref).
-        'cmake-option':         CMakeOptionXRefRole('cmake'),
-        'cmake-build-option':   CMakeOptionXRefRole('cmake--build'),
-        'cmake-install-option': CMakeOptionXRefRole('cmake--install'),
-        'cpack-option':         CMakeOptionXRefRole('cpack'),
-        'ctest-option':         CMakeOptionXRefRole('ctest'),
+        'cmake-option':          CMakeOptionXRefRole('cmake'),
+        'cmake-build-option':    CMakeOptionXRefRole('cmake--build'),
+        'cmake-install-option':  CMakeOptionXRefRole('cmake--install'),
+        'cmake-workflow-option': CMakeOptionXRefRole('cmake--workflow'),
+        'cpack-option':          CMakeOptionXRefRole('cpack'),
+        'ctest-option':          CMakeOptionXRefRole('ctest'),
     }
     initial_data = {
         'objects': {},  # fullname -> ObjectEntry
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake
index b5437e7..bd3363c 100644
--- a/Utilities/cmcurl/CMake/FindBrotli.cmake
+++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -84,10 +84,6 @@
 endif()
 
 if(BROTLI_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_brotli_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::brotli)
     add_library(CURL::brotli INTERFACE IMPORTED)
     set_target_properties(CURL::brotli PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindCares.cmake b/Utilities/cmcurl/CMake/FindCares.cmake
index 3c05c3b..26efdf8 100644
--- a/Utilities/cmcurl/CMake/FindCares.cmake
+++ b/Utilities/cmcurl/CMake/FindCares.cmake
@@ -37,11 +37,15 @@
 
 set(_cares_pc_requires "libcares")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED CARES_INCLUDE_DIR AND
+if(NOT DEFINED CARES_INCLUDE_DIR AND
    NOT DEFINED CARES_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_cares ${_cares_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_cares ${_cares_pc_requires})
+  endif()
+  if(NOT _cares_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(c-ares CONFIG QUIET)
+  endif()
 endif()
 
 if(_cares_FOUND)
@@ -55,6 +59,16 @@
     set(_cares_LIBRARIES    "${_cares_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found Cares (via pkg-config): ${_cares_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")")
+elseif(c-ares_CONFIG)
+  set(Cares_FOUND TRUE)
+  set(CARES_FOUND TRUE)
+  set(CARES_VERSION ${c-ares_VERSION})
+  if(CARES_USE_STATIC_LIBS)
+    set(_cares_LIBRARIES c-ares::cares_static)
+  else()
+    set(_cares_LIBRARIES c-ares::cares)
+  endif()
+  message(STATUS "Found Cares (via CMake Config): ${c-ares_CONFIG} (found version \"${CARES_VERSION}\")")
 else()
   find_path(CARES_INCLUDE_DIR NAMES "ares.h")
   if(CARES_USE_STATIC_LIBS)
@@ -106,10 +120,6 @@
     list(APPEND _cares_LIBRARIES "iphlpapi")  # for if_indextoname and others
   endif()
 
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_cares_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::cares)
     add_library(CURL::cares INTERFACE IMPORTED)
     set_target_properties(CURL::cares PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake
index e36f75b..9237fb3 100644
--- a/Utilities/cmcurl/CMake/FindGSS.cmake
+++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -258,10 +258,6 @@
 )
 
 if(GSS_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_gss_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::gss)
     add_library(CURL::gss INTERFACE IMPORTED)
     set_target_properties(CURL::gss PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGnuTLS.cmake b/Utilities/cmcurl/CMake/FindGnuTLS.cmake
index fff57b2..7f5b227 100644
--- a/Utilities/cmcurl/CMake/FindGnuTLS.cmake
+++ b/Utilities/cmcurl/CMake/FindGnuTLS.cmake
@@ -80,10 +80,6 @@
 endif()
 
 if(GNUTLS_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_gnutls_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::gnutls)
     add_library(CURL::gnutls INTERFACE IMPORTED)
     set_target_properties(CURL::gnutls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLDAP.cmake b/Utilities/cmcurl/CMake/FindLDAP.cmake
index 2f5cc71..8902b23 100644
--- a/Utilities/cmcurl/CMake/FindLDAP.cmake
+++ b/Utilities/cmcurl/CMake/FindLDAP.cmake
@@ -103,10 +103,6 @@
 endif()
 
 if(LDAP_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_ldap_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::ldap)
     add_library(CURL::ldap INTERFACE IMPORTED)
     set_target_properties(CURL::ldap PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake
index c6f7b70..59da7fd 100644
--- a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake
+++ b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake
@@ -47,10 +47,6 @@
   set(_libbacktrace_INCLUDE_DIRS ${LIBBACKTRACE_INCLUDE_DIR})
   set(_libbacktrace_LIBRARIES    ${LIBBACKTRACE_LIBRARY})
 
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libbacktrace_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libbacktrace)
     add_library(CURL::libbacktrace INTERFACE IMPORTED)
     set_target_properties(CURL::libbacktrace PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibgsasl.cmake b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
index 5ddf957..7fbaa71 100644
--- a/Utilities/cmcurl/CMake/FindLibgsasl.cmake
+++ b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
@@ -79,10 +79,6 @@
 endif()
 
 if(LIBGSASL_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libgsasl_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libgsasl)
     add_library(CURL::libgsasl INTERFACE IMPORTED)
     set_target_properties(CURL::libgsasl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibidn2.cmake b/Utilities/cmcurl/CMake/FindLibidn2.cmake
index 336a7f7..dc78734 100644
--- a/Utilities/cmcurl/CMake/FindLibidn2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibidn2.cmake
@@ -80,10 +80,6 @@
 endif()
 
 if(LIBIDN2_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libidn2_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libidn2)
     add_library(CURL::libidn2 INTERFACE IMPORTED)
     set_target_properties(CURL::libidn2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibpsl.cmake b/Utilities/cmcurl/CMake/FindLibpsl.cmake
index 9b1a0cd..2287e67 100644
--- a/Utilities/cmcurl/CMake/FindLibpsl.cmake
+++ b/Utilities/cmcurl/CMake/FindLibpsl.cmake
@@ -80,10 +80,6 @@
 endif()
 
 if(LIBPSL_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libpsl_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libpsl)
     add_library(CURL::libpsl INTERFACE IMPORTED)
     set_target_properties(CURL::libpsl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibrtmp.cmake b/Utilities/cmcurl/CMake/FindLibrtmp.cmake
deleted file mode 100644
index 853ba63..0000000
--- a/Utilities/cmcurl/CMake/FindLibrtmp.cmake
+++ /dev/null
@@ -1,116 +0,0 @@
-#***************************************************************************
-#                                  _   _ ____  _
-#  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
-#
-###########################################################################
-# Find the librtmp library
-#
-# Input variables:
-#
-# - `LIBRTMP_INCLUDE_DIR`:  Absolute path to librtmp include directory.
-# - `LIBRTMP_LIBRARY`:      Absolute path to `librtmp` library.
-#
-# Defines:
-#
-# - `LIBRTMP_FOUND`:        System has librtmp.
-# - `LIBRTMP_VERSION`:      Version of librtmp.
-# - `CURL::librtmp`:        librtmp library target.
-
-set(_librtmp_pc_requires "librtmp")
-
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED LIBRTMP_INCLUDE_DIR AND
-   NOT DEFINED LIBRTMP_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_librtmp ${_librtmp_pc_requires})
-endif()
-
-if(_librtmp_FOUND AND _librtmp_INCLUDE_DIRS)
-  set(Librtmp_FOUND TRUE)
-  set(LIBRTMP_FOUND TRUE)
-  set(LIBRTMP_VERSION ${_librtmp_VERSION})
-  message(STATUS "Found Librtmp (via pkg-config): ${_librtmp_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")")
-else()
-  find_path(LIBRTMP_INCLUDE_DIR NAMES "librtmp/rtmp.h")
-  find_library(LIBRTMP_LIBRARY NAMES "rtmp")
-
-  unset(LIBRTMP_VERSION CACHE)
-  if(LIBRTMP_INCLUDE_DIR AND EXISTS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h")
-    set(_version_regex "#[\t ]*define[\t ]+RTMP_LIB_VERSION[\t ]+0x([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F]).*")
-    file(STRINGS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h" _version_str REGEX "${_version_regex}")
-    string(REGEX REPLACE "${_version_regex}" "\\1" _version_str1 "${_version_str}")
-    string(REGEX REPLACE "${_version_regex}" "\\2" _version_str2 "${_version_str}")
-    if(CMAKE_VERSION VERSION_LESS 3.13)
-      # No support for hex version numbers, just strip leading zeroes
-      string(REGEX REPLACE "^0" "" _version_str1 "${_version_str1}")
-      string(REGEX REPLACE "^0" "" _version_str2 "${_version_str2}")
-    else()
-      math(EXPR _version_str1 "0x${_version_str1}" OUTPUT_FORMAT DECIMAL)
-      math(EXPR _version_str2 "0x${_version_str2}" OUTPUT_FORMAT DECIMAL)
-    endif()
-    set(LIBRTMP_VERSION "${_version_str1}.${_version_str2}")
-    unset(_version_regex)
-    unset(_version_str1)
-    unset(_version_str2)
-  endif()
-
-  include(FindPackageHandleStandardArgs)
-  find_package_handle_standard_args(Librtmp
-    REQUIRED_VARS
-      LIBRTMP_INCLUDE_DIR
-      LIBRTMP_LIBRARY
-    VERSION_VAR
-      LIBRTMP_VERSION
-  )
-
-  if(LIBRTMP_FOUND)
-    set(_librtmp_INCLUDE_DIRS ${LIBRTMP_INCLUDE_DIR})
-    set(_librtmp_LIBRARIES    ${LIBRTMP_LIBRARY})
-  endif()
-
-  mark_as_advanced(LIBRTMP_INCLUDE_DIR LIBRTMP_LIBRARY)
-
-  # Necessary when linking a static librtmp
-  find_package(OpenSSL)
-  if(OpenSSL_FOUND)
-    list(APPEND _librtmp_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
-  endif()
-endif()
-
-if(LIBRTMP_FOUND)
-  if(WIN32)
-    list(APPEND _librtmp_LIBRARIES "winmm")
-  endif()
-
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_librtmp_LIBRARY_DIRS})
-  endif()
-
-  if(NOT TARGET CURL::librtmp)
-    add_library(CURL::librtmp INTERFACE IMPORTED)
-    set_target_properties(CURL::librtmp PROPERTIES
-      INTERFACE_LIBCURL_PC_MODULES "${_librtmp_pc_requires}"
-      INTERFACE_COMPILE_OPTIONS "${_librtmp_CFLAGS}"
-      INTERFACE_INCLUDE_DIRECTORIES "${_librtmp_INCLUDE_DIRS}"
-      INTERFACE_LINK_DIRECTORIES "${_librtmp_LIBRARY_DIRS}"
-      INTERFACE_LINK_LIBRARIES "${_librtmp_LIBRARIES}")
-  endif()
-endif()
diff --git a/Utilities/cmcurl/CMake/FindLibssh.cmake b/Utilities/cmcurl/CMake/FindLibssh.cmake
index 732ab8e..3837d4d 100644
--- a/Utilities/cmcurl/CMake/FindLibssh.cmake
+++ b/Utilities/cmcurl/CMake/FindLibssh.cmake
@@ -106,10 +106,6 @@
     list(APPEND _libssh_LIBRARIES "iphlpapi")  # for if_nametoindex
   endif()
 
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libssh_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libssh)
     add_library(CURL::libssh INTERFACE IMPORTED)
     set_target_properties(CURL::libssh PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibssh2.cmake b/Utilities/cmcurl/CMake/FindLibssh2.cmake
index abed471..22481d6 100644
--- a/Utilities/cmcurl/CMake/FindLibssh2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibssh2.cmake
@@ -37,11 +37,15 @@
 
 set(_libssh2_pc_requires "libssh2")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED LIBSSH2_INCLUDE_DIR AND
+if(NOT DEFINED LIBSSH2_INCLUDE_DIR AND
    NOT DEFINED LIBSSH2_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_libssh2 ${_libssh2_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_libssh2 ${_libssh2_pc_requires})
+  endif()
+  if(NOT _libssh2_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(libssh2 CONFIG QUIET)
+  endif()
 endif()
 
 if(_libssh2_FOUND AND _libssh2_INCLUDE_DIRS)
@@ -55,6 +59,16 @@
     set(_libssh2_LIBRARIES    "${_libssh2_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found Libssh2 (via pkg-config): ${_libssh2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")")
+elseif(libssh2_CONFIG)
+  set(Libssh2_FOUND TRUE)
+  set(LIBSSH2_FOUND TRUE)
+  set(LIBSSH2_VERSION ${libssh2_VERSION})
+  if(LIBSSH2_USE_STATIC_LIBS)
+    set(_libssh2_LIBRARIES libssh2::libssh2_static)
+  else()
+    set(_libssh2_LIBRARIES libssh2::libssh2)
+  endif()
+  message(STATUS "Found Libssh2 (via CMake Config): ${libssh2_CONFIG} (found version \"${LIBSSH2_VERSION}\")")
 else()
   find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h")
   if(LIBSSH2_USE_STATIC_LIBS)
@@ -91,10 +105,6 @@
 endif()
 
 if(LIBSSH2_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libssh2_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libssh2)
     add_library(CURL::libssh2 INTERFACE IMPORTED)
     set_target_properties(CURL::libssh2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibuv.cmake b/Utilities/cmcurl/CMake/FindLibuv.cmake
index f9d614b..47d043c 100644
--- a/Utilities/cmcurl/CMake/FindLibuv.cmake
+++ b/Utilities/cmcurl/CMake/FindLibuv.cmake
@@ -90,10 +90,6 @@
 endif()
 
 if(LIBUV_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_libuv_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::libuv)
     add_library(CURL::libuv INTERFACE IMPORTED)
     set_target_properties(CURL::libuv PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
index 8b67ce9..21a5f4a 100644
--- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake
+++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -45,13 +45,17 @@
 
 set(_mbedtls_pc_requires "mbedtls" "mbedx509" "mbedcrypto")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED MBEDTLS_INCLUDE_DIR AND
+if(NOT DEFINED MBEDTLS_INCLUDE_DIR AND
    NOT DEFINED MBEDTLS_LIBRARY AND
    NOT DEFINED MBEDX509_LIBRARY AND
    NOT DEFINED MBEDCRYPTO_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_mbedtls ${_mbedtls_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_mbedtls ${_mbedtls_pc_requires})
+  endif()
+  if(NOT _mbedtls_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(MbedTLS CONFIG QUIET)
+  endif()
 endif()
 
 if(_mbedtls_FOUND)
@@ -65,6 +69,17 @@
     set(_mbedtls_LIBRARIES    "${_mbedtls_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found MbedTLS (via pkg-config): ${_mbedtls_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")")
+elseif(MbedTLS_CONFIG)
+  set(MbedTLS_FOUND TRUE)
+  set(MBEDTLS_FOUND TRUE)
+  set(MBEDTLS_VERSION ${MbedTLS_VERSION})
+  if(MBEDTLS_VERSION GREATER_EQUAL 4.0.0)
+    set(_mbedtls_LIBRARIES MbedTLS::tfpsacrypto)
+  else()
+    set(_mbedtls_LIBRARIES MbedTLS::mbedcrypto)
+  endif()
+  list(APPEND _mbedtls_LIBRARIES MbedTLS::mbedx509 MbedTLS::mbedtls)
+  message(STATUS "Found MbedTLS (via CMake Config): ${MbedTLS_CONFIG} (found version \"${MBEDTLS_VERSION}\")")
 else()
   set(_mbedtls_pc_requires "")  # Depend on pkg-config only when found via pkg-config
 
@@ -110,10 +125,6 @@
 endif()
 
 if(MBEDTLS_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_mbedtls_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::mbedtls)
     add_library(CURL::mbedtls INTERFACE IMPORTED)
     set_target_properties(CURL::mbedtls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
index b2a8c97..f93113f 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -37,11 +37,15 @@
 
 set(_nghttp2_pc_requires "libnghttp2")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED NGHTTP2_INCLUDE_DIR AND
+if(NOT DEFINED NGHTTP2_INCLUDE_DIR AND
    NOT DEFINED NGHTTP2_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires})
+  endif()
+  if(NOT _nghttp2_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(nghttp2 CONFIG QUIET)
+  endif()
 endif()
 
 if(_nghttp2_FOUND)
@@ -54,6 +58,15 @@
     set(_nghttp2_LIBRARIES    "${_nghttp2_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found NGHTTP2 (via pkg-config): ${_nghttp2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")")
+elseif(nghttp2_CONFIG)
+  set(NGHTTP2_FOUND TRUE)
+  set(NGHTTP2_VERSION ${nghttp2_VERSION})
+  if(NGHTTP2_USE_STATIC_LIBS)
+    set(_nghttp2_LIBRARIES nghttp2::nghttp2_static)
+  else()
+    set(_nghttp2_LIBRARIES nghttp2::nghttp2)
+  endif()
+  message(STATUS "Found NGHTTP2 (via CMake Config): ${nghttp2_CONFIG} (found version \"${NGHTTP2_VERSION}\")")
 else()
   find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h")
   if(NGHTTP2_USE_STATIC_LIBS)
@@ -91,10 +104,6 @@
 endif()
 
 if(NGHTTP2_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_nghttp2_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::nghttp2)
     add_library(CURL::nghttp2 INTERFACE IMPORTED)
     set_target_properties(CURL::nghttp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
index 57550bf..427c139 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
@@ -37,11 +37,15 @@
 
 set(_nghttp3_pc_requires "libnghttp3")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED NGHTTP3_INCLUDE_DIR AND
+if(NOT DEFINED NGHTTP3_INCLUDE_DIR AND
    NOT DEFINED NGHTTP3_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires})
+  endif()
+  if(NOT _nghttp3_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(nghttp3 CONFIG QUIET)
+  endif()
 endif()
 
 if(_nghttp3_FOUND)
@@ -54,6 +58,15 @@
     set(_nghttp3_LIBRARIES    "${_nghttp3_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found NGHTTP3 (via pkg-config): ${_nghttp3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")")
+elseif(nghttp3_CONFIG)
+  set(NGHTTP3_FOUND TRUE)
+  set(NGHTTP3_VERSION ${nghttp3_VERSION})
+  if(NGHTTP3_USE_STATIC_LIBS)
+    set(_nghttp3_LIBRARIES nghttp3::nghttp3_static)
+  else()
+    set(_nghttp3_LIBRARIES nghttp3::nghttp3)
+  endif()
+  message(STATUS "Found NGHTTP3 (via CMake Config): ${nghttp3_CONFIG} (found version \"${NGHTTP3_VERSION}\")")
 else()
   find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h")
   if(NGHTTP3_USE_STATIC_LIBS)
@@ -91,10 +104,6 @@
 endif()
 
 if(NGHTTP3_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_nghttp3_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::nghttp3)
     add_library(CURL::nghttp3 INTERFACE IMPORTED)
     set_target_properties(CURL::nghttp3 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
index 615ee4a..e492916 100644
--- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -74,12 +74,22 @@
 endif()
 
 set(_tried_pkgconfig FALSE)
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED NGTCP2_INCLUDE_DIR AND
+if(NOT DEFINED NGTCP2_INCLUDE_DIR AND
    NOT DEFINED NGTCP2_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires})
-  set(_tried_pkgconfig TRUE)
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires})
+    set(_tried_pkgconfig TRUE)
+  endif()
+  if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(ngtcp2 CONFIG QUIET)
+    # Skip using it if the crypto library target is not available
+    if(ngtcp2_CONFIG AND
+       NOT TARGET ngtcp2::${_crypto_library_lower}_static AND
+       NOT TARGET ngtcp2::${_crypto_library_lower})
+      unset(ngtcp2_CONFIG)
+    endif()
+  endif()
 endif()
 
 if(_ngtcp2_FOUND)
@@ -92,6 +102,15 @@
     set(_ngtcp2_LIBRARIES    "${_ngtcp2_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found NGTCP2 (via pkg-config): ${_ngtcp2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")")
+elseif(ngtcp2_CONFIG)
+  set(NGTCP2_FOUND TRUE)
+  set(NGTCP2_VERSION ${ngtcp2_VERSION})
+  if(NGTCP2_USE_STATIC_LIBS)
+    set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static)
+  else()
+    set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower})
+  endif()
+  message(STATUS "Found NGTCP2 (via CMake Config): ${ngtcp2_CONFIG} (found version \"${NGTCP2_VERSION}\")")
 else()
   find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h")
   if(NGTCP2_USE_STATIC_LIBS)
@@ -155,10 +174,6 @@
 endif()
 
 if(NGTCP2_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_ngtcp2_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::ngtcp2)
     add_library(CURL::ngtcp2 INTERFACE IMPORTED)
     set_target_properties(CURL::ngtcp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNettle.cmake b/Utilities/cmcurl/CMake/FindNettle.cmake
index d22865f..c963180 100644
--- a/Utilities/cmcurl/CMake/FindNettle.cmake
+++ b/Utilities/cmcurl/CMake/FindNettle.cmake
@@ -85,10 +85,6 @@
 endif()
 
 if(NETTLE_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_nettle_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::nettle)
     add_library(CURL::nettle INTERFACE IMPORTED)
     set_target_properties(CURL::nettle PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindQuiche.cmake b/Utilities/cmcurl/CMake/FindQuiche.cmake
index 22482d7..137679b 100644
--- a/Utilities/cmcurl/CMake/FindQuiche.cmake
+++ b/Utilities/cmcurl/CMake/FindQuiche.cmake
@@ -68,10 +68,6 @@
 endif()
 
 if(QUICHE_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_quiche_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::quiche)
     add_library(CURL::quiche INTERFACE IMPORTED)
     set_target_properties(CURL::quiche PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindRustls.cmake b/Utilities/cmcurl/CMake/FindRustls.cmake
index 270e99c..1e07565 100644
--- a/Utilities/cmcurl/CMake/FindRustls.cmake
+++ b/Utilities/cmcurl/CMake/FindRustls.cmake
@@ -104,10 +104,6 @@
     mark_as_advanced(MATH_LIBRARY)
   endif()
 
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_rustls_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::rustls)
     add_library(CURL::rustls INTERFACE IMPORTED)
     set_target_properties(CURL::rustls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
index 3a3c05f..964320a 100644
--- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -45,11 +45,15 @@
 
 set(_wolfssl_pc_requires "wolfssl")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED WOLFSSL_INCLUDE_DIR AND
+if(NOT DEFINED WOLFSSL_INCLUDE_DIR AND
    NOT DEFINED WOLFSSL_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_wolfssl ${_wolfssl_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_wolfssl ${_wolfssl_pc_requires})
+  endif()
+  if(NOT _wolfssl_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(wolfssl CONFIG QUIET)
+  endif()
 endif()
 
 if(_wolfssl_FOUND)
@@ -57,6 +61,12 @@
   set(WOLFSSL_FOUND TRUE)
   set(WOLFSSL_VERSION ${_wolfssl_VERSION})
   message(STATUS "Found WolfSSL (via pkg-config): ${_wolfssl_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")")
+elseif(wolfssl_CONFIG)
+  set(WolfSSL_FOUND TRUE)
+  set(WOLFSSL_FOUND TRUE)
+  set(WOLFSSL_VERSION ${wolfssl_VERSION})
+  set(_wolfssl_LIBRARIES wolfssl::wolfssl)
+  message(STATUS "Found WolfSSL (via CMake Config): ${wolfssl_CONFIG} (found version \"${WOLFSSL_VERSION}\")")
 else()
   find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h")
   find_library(WOLFSSL_LIBRARY NAMES "wolfssl")
@@ -113,10 +123,6 @@
     mark_as_advanced(MATH_LIBRARY)
   endif()
 
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_wolfssl_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::wolfssl)
     add_library(CURL::wolfssl INTERFACE IMPORTED)
     set_target_properties(CURL::wolfssl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake
index baf6148..8dc620a 100644
--- a/Utilities/cmcurl/CMake/FindZstd.cmake
+++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -46,11 +46,21 @@
 
 set(_zstd_pc_requires "libzstd")
 
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED ZSTD_INCLUDE_DIR AND
+if(NOT DEFINED ZSTD_INCLUDE_DIR AND
    NOT DEFINED ZSTD_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(_zstd ${_zstd_pc_requires})
+  if(CURL_USE_PKGCONFIG)
+    find_package(PkgConfig QUIET)
+    pkg_check_modules(_zstd ${_zstd_pc_requires})
+  endif()
+  if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG)
+    find_package(Zstd CONFIG QUIET)
+    # Skip using if older than v1.4.5
+    if(Zstd_CONFIG AND
+       NOT TARGET zstd::libzstd_static AND
+       NOT TARGET zstd::libzstd_shared)
+      unset(Zstd_CONFIG)
+    endif()
+  endif()
 endif()
 
 if(_zstd_FOUND)
@@ -64,6 +74,17 @@
     set(_zstd_LIBRARIES    "${_zstd_STATIC_LIBRARIES}")
   endif()
   message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")")
+elseif(Zstd_CONFIG)
+  set(ZSTD_FOUND TRUE)
+  set(ZSTD_VERSION ${Zstd_VERSION})
+  if(ZSTD_USE_STATIC_LIBS)
+    set(_zstd_LIBRARIES zstd::libzstd_static)
+  elseif(TARGET zstd::libzstd)
+    set(_zstd_LIBRARIES zstd::libzstd)  # v1.5.6+
+  else()
+    set(_zstd_LIBRARIES zstd::libzstd_shared)
+  endif()
+  message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")")
 else()
   find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h")
   if(ZSTD_USE_STATIC_LIBS)
@@ -110,10 +131,6 @@
 endif()
 
 if(ZSTD_FOUND)
-  if(CMAKE_VERSION VERSION_LESS 3.13)
-    link_directories(${_zstd_LIBRARY_DIRS})
-  endif()
-
   if(NOT TARGET CURL::zstd)
     add_library(CURL::zstd INTERFACE IMPORTED)
     set_target_properties(CURL::zstd PROPERTIES
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake
index e182f21..5e26c38 100644
--- a/Utilities/cmcurl/CMake/Macros.cmake
+++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -40,20 +40,12 @@
 # Return result in variable: CURL_TEST_OUTPUT
 macro(curl_internal_test _curl_test)
   if(NOT DEFINED "${_curl_test}")
-    string(REPLACE ";" " " _cmake_required_definitions "${CMAKE_REQUIRED_DEFINITIONS}")
-    set(_curl_test_add_libraries "")
-    if(CMAKE_REQUIRED_LIBRARIES)
-      set(_curl_test_add_libraries
-        "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}")
-    endif()
-
     message(STATUS "Performing Test ${_curl_test}")
     try_compile(${_curl_test}
       ${PROJECT_BINARY_DIR}
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c"
-      CMAKE_FLAGS
-        "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${_cmake_required_definitions}"
-        "${_curl_test_add_libraries}"
+      COMPILE_DEFINITIONS "-D${_curl_test}" ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${CMAKE_REQUIRED_DEFINITIONS}
+      LINK_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}"
       OUTPUT_VARIABLE CURL_TEST_OUTPUT)
     if(${_curl_test})
       set(${_curl_test} 1 CACHE INTERNAL "curl test")
@@ -252,3 +244,35 @@
     add_dependencies(tests-clang-tidy ${_target_clang_tidy})
   endif()
 endfunction()
+
+# Internal: Recurse into interface targets and collect their libraries
+# and library paths.
+macro(curl_collect_target_link_options _target)
+  get_target_property(_val ${_target} INTERFACE_LINK_DIRECTORIES)
+  if(_val)
+    list(APPEND _libdirs ${_val})
+  endif()
+  get_target_property(_val ${_target} IMPORTED)
+  if(_val)
+    # LOCATION is empty for interface library targets and safe to ignore.
+    # Explicitly skip this query to avoid CMake v3.18 and older erroring out.
+    get_target_property(_val ${_target} TYPE)
+    if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY")
+      get_target_property(_val ${_target} LOCATION)
+      if(_val)
+        list(APPEND _libs ${_val})
+      endif()
+    endif()
+  endif()
+  get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES)
+  if(_val)
+    foreach(_lib IN LISTS _val)
+      if(TARGET "${_lib}")
+        curl_collect_target_link_options(${_lib})
+      else()
+        list(APPEND _libs ${_lib})
+      endif()
+    endforeach()
+  endif()
+  unset(_val)
+endmacro()
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index 650a42d..6619f3a 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -78,7 +78,7 @@
 elseif(NOT HAVE_GETADDRINFO)
   set(HAVE_GETADDRINFO_THREADSAFE FALSE)
 elseif(APPLE OR
-       CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
+       AIX 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
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake
index 1b67b0b..aca79ec 100644
--- a/Utilities/cmcurl/CMake/PickyWarnings.cmake
+++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -60,7 +60,9 @@
 endif()
 
 if(PICKY_COMPILER)
-  if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+  # Leave disabled for GCC <4.6, because they lack #pragma features to silence locally.
+  if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.6) OR
+     CMAKE_C_COMPILER_ID MATCHES "Clang")
 
     # https://clang.llvm.org/docs/DiagnosticsReference.html
     # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
@@ -147,7 +149,7 @@
       list(APPEND _picky_enable
         ${_picky_common_old}
         -Wconditional-uninitialized        # clang  3.0
-        -Wno-used-but-marked-unused        # clang  2.9            # Triggered by typecheck-gcc.h with clang 14+, dependency headers
+        -Wno-used-but-marked-unused        # clang  2.9            # for typecheck-gcc.h with clang 14+, dependency headers
         -Wshift-sign-overflow              # clang  2.9
         -Wshorten-64-to-32                 # clang  1.0
         -Wformat=2                         # clang  2.7  gcc  4.8
@@ -161,7 +163,7 @@
       if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1)
         list(APPEND _picky_enable
           -Wno-covered-switch-default      # clang  3.1            appleclang  3.1  # Annoying to fix or silence
-          -Wno-disabled-macro-expansion    # clang  3.1            appleclang  3.1  # Triggered by curl/curl.h, standard headers
+          -Wno-disabled-macro-expansion    # clang  3.1            appleclang  3.1  # for std headers, and curl/curl.h (rare combos)
         )
         if(MSVC)
           list(APPEND _picky_enable
@@ -256,6 +258,7 @@
         list(APPEND _picky_enable
           -Warray-compare                  # clang 20.1  gcc 12.0  appleclang 26.4
           -Wc++-hidden-decl                # clang 21.1            appleclang 26.4
+          -Wimplicit-int-enum-cast         # clang 21.1
           -Wjump-misses-init               # clang 21.1  gcc  4.5  appleclang 26.4
           -Wno-implicit-void-ptr-cast      # clang 21.1            appleclang 26.4
           -Wtentative-definition-compat    # clang 21.1            appleclang 26.4
@@ -347,7 +350,7 @@
       endif()
     endif()
 
-    #
+    # Assemble list of flags
 
     set(_picky_skipped "")
     foreach(_ccopt IN LISTS _picky_enable)
@@ -378,15 +381,6 @@
     endforeach()
 
     if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5)
-        # Avoid false positives
-        list(APPEND _picky "-Wno-shadow")
-        list(APPEND _picky "-Wno-unreachable-code")
-      endif()
-      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6)
-        # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead.
-        list(APPEND _picky "-Wno-overlength-strings")
-      endif()
       if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7)
         list(APPEND _picky "-Wno-missing-field-initializers")  # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750
       endif()
@@ -436,11 +430,13 @@
   endforeach()
 endif()
 
-if(CMAKE_C_STANDARD STREQUAL 90 AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
-  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2)
+if(CMAKE_C_STANDARD STREQUAL 90)
+  if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.0) OR
+     (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2))
     list(APPEND _picky "-Wno-c99-extensions")  # Avoid: warning: '_Bool' is a C99 extension
   endif()
-  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)
+  if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR
+     (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1))
     list(APPEND _picky "-Wno-comma")  # Just silly
   endif()
 endif()
diff --git a/Utilities/cmcurl/CMake/curl-config.in.cmake b/Utilities/cmcurl/CMake/curl-config.in.cmake
index 4e7c29f..3174771 100644
--- a/Utilities/cmcurl/CMake/curl-config.in.cmake
+++ b/Utilities/cmcurl/CMake/curl-config.in.cmake
@@ -23,6 +23,8 @@
 ###########################################################################
 @PACKAGE_INIT@
 
+option(CURL_USE_CMAKECONFIG "Enable detecting @PROJECT_NAME@ dependencies via CMake Config. Default: @CURL_USE_CMAKECONFIG@"
+  "@CURL_USE_CMAKECONFIG@")
 option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependencies. Default: @CURL_USE_PKGCONFIG@"
   "@CURL_USE_PKGCONFIG@")
 
@@ -33,6 +35,10 @@
 
 include(CMakeFindDependencyMacro)
 
+if("@HAVE_THREADS_POSIX@" OR "@HAVE_THREADS_POSIX_BORINGSSL@")
+  find_dependency(Threads)  # for Threads::Threads
+endif()
+
 if("@USE_OPENSSL@")
   if("@OPENSSL_VERSION_MAJOR@")
     find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@")
@@ -42,8 +48,7 @@
   # Define lib duplicate to fixup lib order for GCC binutils ld in static builds
   if(TARGET OpenSSL::Crypto AND NOT TARGET CURL::OpenSSL_Crypto)
     add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED)
-    get_target_property(_curl_libname OpenSSL::Crypto LOCATION)
-    set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}")
+    set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
   endif()
 endif()
 if("@HAVE_LIBZ@")
@@ -51,13 +56,12 @@
   # Define lib duplicate to fixup lib order for GCC binutils ld in static builds
   if(TARGET ZLIB::ZLIB AND NOT TARGET CURL::ZLIB)
     add_library(CURL::ZLIB INTERFACE IMPORTED)
-    get_target_property(_curl_libname ZLIB::ZLIB LOCATION)
-    set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}")
+    set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
   endif()
 endif()
 
 set(_curl_cmake_module_path_save ${CMAKE_MODULE_PATH})
-set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH})
+list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
 
 set(_curl_libs "")
 
@@ -93,10 +97,6 @@
   find_dependency(Libpsl MODULE)
   list(APPEND _curl_libs CURL::libpsl)
 endif()
-if("@USE_LIBRTMP@")
-  find_dependency(Librtmp MODULE)
-  list(APPEND _curl_libs CURL::librtmp)
-endif()
 if("@USE_LIBSSH@")
   find_dependency(Libssh MODULE)
   list(APPEND _curl_libs CURL::libssh)
@@ -160,35 +160,9 @@
 
 # Alias for either shared or static library
 if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@)
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND CMAKE_VERSION VERSION_LESS 3.18)
-    set_target_properties(@PROJECT_NAME@::@LIB_SELECTED@ PROPERTIES IMPORTED_GLOBAL TRUE)
-  endif()
   add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
 endif()
 
-if(TARGET @PROJECT_NAME@::@LIB_STATIC@)
-  # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to
-  # targets. It expected libs to have an absolute filename. As a workaround,
-  # manually apply dependency libdirs, for CMake consumers without this policy.
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17)
-    cmake_policy(GET CMP0099 _has_CMP0099)  # https://cmake.org/cmake/help/latest/policy/CMP0099.html
-  endif()
-  if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND _curl_libs)
-    set(_curl_libdirs "")
-    foreach(_curl_lib IN LISTS _curl_libs)
-      if(TARGET "${_curl_lib}")
-        get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES)
-        if(_curl_libdir)
-          list(APPEND _curl_libdirs "${_curl_libdir}")
-        endif()
-      endif()
-    endforeach()
-    if(_curl_libdirs)
-      target_link_directories(@PROJECT_NAME@::@LIB_STATIC@ INTERFACE ${_curl_libdirs})
-    endif()
-  endif()
-endif()
-
 # For compatibility with CMake's FindCURL.cmake
 set(CURL_VERSION_STRING "@CURLVERSION@")
 set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@)
diff --git a/Utilities/cmcurl/CMake/unix-cache.cmake b/Utilities/cmcurl/CMake/unix-cache.cmake
index f358a47..f027f66 100644
--- a/Utilities/cmcurl/CMake/unix-cache.cmake
+++ b/Utilities/cmcurl/CMake/unix-cache.cmake
@@ -90,7 +90,6 @@
   set(HAVE_FSETXATTR_5 1)
   set(HAVE_FSETXATTR_6 0)
 endif()
-set(HAVE_FTRUNCATE 1)
 set(HAVE_GETADDRINFO 1)
 if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
   set(HAVE_GETADDRINFO_THREADSAFE 0)
@@ -199,9 +198,12 @@
    CYGWIN)
   set(HAVE_PIPE2 0)
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       BSD OR
+       CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
        CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
        CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
-       CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+       CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "SunOS")
   set(HAVE_PIPE2 1)
 endif()
 set(HAVE_POLL 1)
@@ -232,7 +234,6 @@
 set(HAVE_SIGINTERRUPT 1)
 set(HAVE_SIGNAL 1)
 set(HAVE_SIGSETJMP 1)
-set(HAVE_SNPRINTF 1)
 set(HAVE_SOCKADDR_IN6_SIN6_ADDR 1)
 set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
 set(HAVE_SOCKET 1)
diff --git a/Utilities/cmcurl/CMake/win32-cache.cmake b/Utilities/cmcurl/CMake/win32-cache.cmake
index 77535a6..bd9bb6e 100644
--- a/Utilities/cmcurl/CMake/win32-cache.cmake
+++ b/Utilities/cmcurl/CMake/win32-cache.cmake
@@ -29,7 +29,6 @@
   set(HAVE_BASENAME 1)
   set(HAVE_BOOL_T 1)  # = HAVE_STDBOOL_H
   set(HAVE_DIRENT_H 1)
-  set(HAVE_FTRUNCATE 1)
   set(HAVE_GETTIMEOFDAY 1)
   set(HAVE_LIBGEN_H 1)
   set(HAVE_OPENDIR 1)
@@ -42,7 +41,6 @@
   set(HAVE_UTIME_H 1)  # wrapper to sys/utime.h
 else()
   set(HAVE_DIRENT_H 0)
-  set(HAVE_FTRUNCATE 0)
   set(HAVE_GETTIMEOFDAY 0)
   set(HAVE_LIBGEN_H 0)
   set(HAVE_OPENDIR 0)
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 017c6ea..4566ff2 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -23,6 +23,9 @@
 set(CURL_USE_SCHANNEL OFF)
 set(CURL_USE_WOLFSSL OFF)
 set(CURL_BROTLI "OFF" CACHE INTERNAL "Build curl with BROTLI support (AUTO, ON or OFF)")
+set(CURL_DEBUG_GLOBAL_MEM OFF)
+set(CURL_ENABLE_NTLM OFF)
+set(CURL_ENABLE_SMB OFF)
 set(CURL_DISABLE_ALTSVC ON)
 set(CURL_DISABLE_AWS OFF)
 set(CURL_DISABLE_BASIC_AUTH OFF)
@@ -53,7 +56,6 @@
 set(CURL_DISABLE_MQTT ON)
 set(CURL_DISABLE_NEGOTIATE_AUTH OFF)
 set(CURL_DISABLE_NETRC OFF)
-set(CURL_DISABLE_NTLM OFF)
 set(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG OFF)
 set(CURL_DISABLE_PARSEDATE OFF)
 set(CURL_DISABLE_POP3 ON CACHE INTERNAL "Disable curl pop3 protocol?")
@@ -62,7 +64,6 @@
 set(CURL_DISABLE_RTSP ON CACHE INTERNAL "Disable curl rtsp protocol?")
 set(CURL_DISABLE_SHA512_256 OFF)
 set(CURL_DISABLE_SHUFFLE_DNS OFF)
-set(CURL_DISABLE_SMB OFF)
 set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?")
 set(CURL_DISABLE_SOCKETPAIR OFF)
 set(CURL_DISABLE_SRP OFF)
@@ -75,6 +76,7 @@
 set(CURL_HIDDEN_SYMBOLS OFF CACHE INTERNAL "No curl hidden symbols")
 set(CURL_LTO OFF CACHE INTERNAL "Turn on compiler Link Time Optimizations")
 set(CURL_STATIC_CRT OFF CACHE INTERNAL "Set to ON to build libcurl with static CRT on Windows (/MT).")
+set(CURL_USE_CMAKECONFIG OFF)
 set(CURL_WERROR OFF CACHE INTERNAL "Turn compiler warnings into errors")
 set(CURL_ZLIB "AUTO" CACHE INTERNAL "Build curl with ZLIB support (AUTO, ON or OFF)")
 set(CURL_ZSTD "OFF" CACHE INTERNAL "Build curl with ZSTD support (AUTO, ON or OFF)")
@@ -179,7 +181,7 @@
 ###########################################################################
 
 if(0) # XXX(cmake): not needed for build within cmake
-cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
 message(STATUS "Using CMake version ${CMAKE_VERSION}")
 
 # Collect command-line arguments for buildinfo.txt.
@@ -203,7 +205,7 @@
 endif()
 endif() # XXX(cmake): end
 
-set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
+list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
 include(Utilities)
 include(Macros)
 include(CMakeDependentOption)
@@ -447,7 +449,7 @@
     set(PICKY_COMPILER OFF)  # Do a best effort and skip passing non-clang warning options to clang-tidy.
     # This lets through warning options enabled via CURL_WERROR=ON, affecting lib and src.
   endif()
-  set(CURL_DISABLE_TYPECHECK ON)  # to improve performance and avoid potential interference.
+  set(CURL_DISABLE_TYPECHECK ON)  # to improve performance (1.4x), avoid potential interference and bugprone-macro-parentheses.
   set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}")
   list(APPEND CMAKE_C_CLANG_TIDY "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy.yml")
   if(CURL_WERROR)
@@ -459,6 +461,18 @@
   endif()
 endif()
 
+option(CURL_GCC_ANALYZER "Enable GCC --analyzer option" OFF)
+if(CURL_GCC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0)
+  set(CURL_DISABLE_TYPECHECK ON)  # to improve performance (1.1x).
+  # https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html
+  set(CURL_ANALYZER_CFLAGS "-fanalyzer")
+  # disable checks causing false positives only
+  list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-fd-leak" "-Wno-analyzer-fd-use-without-check" "-Wno-analyzer-file-leak")
+  list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-infinite-loop")
+  list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-malloc-leak")
+  list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-out-of-bounds")
+endif()
+
 option(CURL_CODE_COVERAGE "Enable code coverage build options" OFF)
 if(CURL_CODE_COVERAGE)
   if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
@@ -486,18 +500,10 @@
 option(CURL_DROP_UNUSED "Drop unused code and data from built binaries" OFF)
 if(CURL_DROP_UNUSED)
   if(APPLE)
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-      set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip")
-    else()
-      set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-Wl,-dead_strip")
-    endif()
+    set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip")
   elseif(MSVC)  # Options below are toolchain defaults in Release configurations.
     # This option does not seem to have an effect with VS2010:
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-      set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF")
-    else()
-      set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-OPT:REF")
-    endif()
+    set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF")
     # Optional, but reduces binary size further, with the cost of larger objects/static libraries:
     list(APPEND CURL_CFLAGS "-Gy")
   elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -505,11 +511,7 @@
       # To make -Wl,--gc-sections work on Windows: https://sourceware.org/bugzilla/show_bug.cgi?id=11539
       set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-fno-asynchronous-unwind-tables")
     endif()
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-      set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections")
-    else()
-      set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-Wl,--gc-sections")
-    endif()
+    set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections")
     # Optional, but reduces binary size further, with the cost of larger objects/static libraries:
     list(APPEND CURL_CFLAGS "-ffunction-sections" "-fdata-sections")
   endif()
@@ -560,6 +562,14 @@
   endif()
 endif()
 
+# Override to force-disable or force-enable the use of CMake Configs.
+if(MSVC AND NOT VCPKG_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING)
+  set(_curl_use_cmakeconfig_default ON)
+else()
+  set(_curl_use_cmakeconfig_default OFF)
+endif()
+option(CURL_USE_CMAKECONFIG "Enable detecting dependencies via CMake Config" ${_curl_use_cmakeconfig_default})
+
 # Override to force-disable or force-enable the use of pkg-config.
 if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) OR
    VCPKG_TOOLCHAIN OR
@@ -647,8 +657,8 @@
 mark_as_advanced(CURL_DISABLE_BINDLOCAL)
 option(CURL_DISABLE_NETRC "Disable netrc parser" OFF)
 mark_as_advanced(CURL_DISABLE_NETRC)
-option(CURL_DISABLE_NTLM "Disable NTLM support" OFF)
-mark_as_advanced(CURL_DISABLE_NTLM)
+option(CURL_ENABLE_NTLM "Enable NTLM support" OFF)
+mark_as_advanced(CURL_ENABLE_NTLM)
 option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF)
 mark_as_advanced(CURL_DISABLE_PARSEDATE)
 option(CURL_DISABLE_POP3 "Disable POP3" OFF)
@@ -665,8 +675,8 @@
 mark_as_advanced(CURL_DISABLE_SHA512_256)
 option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF)
 mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS)
-option(CURL_DISABLE_SMB "Disable SMB" OFF)
-mark_as_advanced(CURL_DISABLE_SMB)
+option(CURL_ENABLE_SMB "Enable SMB" OFF)
+mark_as_advanced(CURL_ENABLE_SMB)
 option(CURL_DISABLE_SMTP "Disable SMTP" OFF)
 mark_as_advanced(CURL_DISABLE_SMTP)
 option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll()" OFF)
@@ -681,7 +691,13 @@
 mark_as_advanced(CURL_DISABLE_TYPECHECK)
 option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF)
 mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
+option(CURL_DEBUG_GLOBAL_MEM "Debug curl_global_init_mem" OFF)
+mark_as_advanced(CURL_DEBUG_GLOBAL_MEM)
 
+if(CURL_DEBUG_GLOBAL_MEM)
+  # Set it via the command-line to make it apply to the entire directory.
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DEBUG_GLOBAL_MEM")
+endif()
 if(CURL_DISABLE_TYPECHECK)
   # Set it via the command-line to make it apply to examples also.
   set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DISABLE_TYPECHECK")
@@ -711,7 +727,6 @@
   set(CURL_DISABLE_MQTT ON)
   set(CURL_DISABLE_POP3 ON)
   set(CURL_DISABLE_RTSP ON)
-  set(CURL_DISABLE_SMB ON)
   set(CURL_DISABLE_SMTP ON)
   set(CURL_DISABLE_TELNET ON)
   set(CURL_DISABLE_TFTP ON)
@@ -741,9 +756,11 @@
   add_custom_target(curl-lint
     COMMENT "Running lint checks" VERBATIM USES_TERMINAL
     WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+    COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/badwords-all"
     COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl"
     COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl"
     DEPENDS
+      "${PROJECT_SOURCE_DIR}/scripts/badwords-all" "${PROJECT_SOURCE_DIR}/scripts/badwords"
       "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" "${PROJECT_SOURCE_DIR}/scripts/checksrc.pl"
       "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl"
   )
@@ -762,7 +779,7 @@
 endif() # XXX(cmake): end
 
 # If we are on AIX, do the _ALL_SOURCE magic
-if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
+if(AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX")
   set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_ALL_SOURCE")
 endif()
 
@@ -813,17 +830,23 @@
   set(HAVE_TIME_T_UNSIGNED 1)
 endif()
 
-if(ENABLE_THREADED_RESOLVER)
-  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})
-    list(APPEND CURL_NETWORK_AND_TIME_LIBS ${CMAKE_THREAD_LIBS_INIT})
+if(NOT WIN32)
+  find_package(Threads)
+  if(CMAKE_USE_PTHREADS_INIT)
+    set(HAVE_THREADS_POSIX 1)
+    list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads)
   endif()
 endif()
 
+if(ENABLE_THREADED_RESOLVER)
+  if(NOT WIN32 AND NOT HAVE_THREADS_POSIX)
+    message(FATAL_ERROR "Threaded resolver requires POSIX Threads.")
+  endif()
+  set(USE_RESOLV_THREADED ON)
+elseif(USE_ARES)
+  set(USE_RESOLV_ARES ON)
+endif()
+
 # Check for all needed libraries
 if(DOS)
   if(WATT_ROOT)
@@ -849,7 +872,7 @@
 elseif(NOT WIN32 AND NOT APPLE)
   check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
   if(HAVE_LIBSOCKET)
-    set(CURL_NETWORK_AND_TIME_LIBS "socket" ${CURL_NETWORK_AND_TIME_LIBS})
+    list(PREPEND CURL_NETWORK_AND_TIME_LIBS "socket")
   endif()
 endif()
 
@@ -1027,6 +1050,16 @@
   endif()
 
   if(HAVE_BORINGSSL OR HAVE_AWSLC)
+    if(NOT MSVC AND NOT ANDROID)  # BoringSSL/AWS-LC MSVC builds use native Windows threads
+      find_package(Threads)
+      if(CMAKE_USE_PTHREADS_INIT)
+        set(HAVE_THREADS_POSIX_BORINGSSL 1)
+        list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads)
+        list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)
+      elseif(OPENSSL_USE_STATIC_LIBS)
+        message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.")
+      endif()
+    endif()
     if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang")
       list(APPEND CURL_LIBS "stdc++")
       list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++")
@@ -1193,7 +1226,7 @@
       endif()
     endif()
     if(WIN32)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt")  # for OpenSSL/LibreSSL
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt")  # for OpenSSL/LibreSSL BCryptGenRandom()
     endif()
   endif()
   if(USE_WOLFSSL)
@@ -1241,9 +1274,9 @@
 if(USE_WOLFSSL)
   curl_openssl_check_exists("wolfSSL_get_peer_certificate" HAVE_WOLFSSL_GET_PEER_CERTIFICATE)
   curl_openssl_check_exists("wolfSSL_UseALPN" HAVE_WOLFSSL_USEALPN)
-  curl_openssl_check_exists("wolfSSL_DES_ecb_encrypt" HAVE_WOLFSSL_DES_ECB_ENCRYPT)
   curl_openssl_check_exists("wolfSSL_BIO_new" HAVE_WOLFSSL_BIO_NEW)
   curl_openssl_check_exists("wolfSSL_BIO_set_shutdown" HAVE_WOLFSSL_BIO_SET_SHUTDOWN)
+  curl_openssl_check_exists("wc_Des_EcbEncrypt" HAVE_WC_DES_ECBENCRYPT)
 endif()
 
 if(USE_OPENSSL)
@@ -1389,7 +1422,7 @@
     find_package(LDAP MODULE)
     if(LDAP_FOUND)
       set(HAVE_LBER_H 1)
-      set(CURL_LIBS CURL::ldap ${CURL_LIBS})
+      list(PREPEND CURL_LIBS CURL::ldap)
 
       # LDAP feature checks
 
@@ -1426,7 +1459,7 @@
 if(WIN32)
   option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF)
   if(USE_WIN32_IDN)
-    list(APPEND CURL_LIBS "normaliz")
+    list(APPEND CURL_LIBS "normaliz")  # for IdnToAscii(), IdnToUnicode()
   endif()
 else()
   set(USE_WIN32_IDN OFF)
@@ -1456,7 +1489,7 @@
 if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN)
   find_package(Libidn2 MODULE QUIET)
   if(LIBIDN2_FOUND)
-    set(CURL_LIBS CURL::libidn2 ${CURL_LIBS})
+    list(PREPEND CURL_LIBS CURL::libidn2)
     set(HAVE_IDN2_H 1)
     set(HAVE_LIBIDN2 1)
   endif()
@@ -1479,7 +1512,7 @@
 if(CURL_USE_LIBSSH2)
   find_package(Libssh2 MODULE)
   if(LIBSSH2_FOUND)
-    set(CURL_LIBS CURL::libssh2 ${CURL_LIBS})  # keep it before TLS-crypto, compression
+    list(PREPEND CURL_LIBS CURL::libssh2)  # keep it before TLS-crypto, compression
     set(USE_LIBSSH2 ON)
   endif()
 endif()
@@ -1489,7 +1522,7 @@
 mark_as_advanced(CURL_USE_LIBSSH)
 if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH)
   find_package(Libssh MODULE REQUIRED)
-  set(CURL_LIBS CURL::libssh ${CURL_LIBS})  # keep it before TLS-crypto, compression
+  list(PREPEND CURL_LIBS CURL::libssh)  # keep it before TLS-crypto, compression
   set(USE_LIBSSH ON)
 endif()
 
@@ -1548,12 +1581,6 @@
   set(HAVE_UV_H ON)
 endif()
 
-option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF)
-if(USE_LIBRTMP)
-  find_package(Librtmp MODULE REQUIRED)
-  list(APPEND CURL_LIBS CURL::librtmp)
-endif()
-
 option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON)
 if(ENABLE_UNIX_SOCKETS)
   if(WIN32 OR DOS)
@@ -1753,7 +1780,7 @@
     HAVE_UNISTD_H
 )
   if(${_variable})
-    string(APPEND CURL_TEST_DEFINES " -D${_variable}")
+    list(APPEND CURL_TEST_DEFINES "-D${_variable}")
   endif()
 endforeach()
 
@@ -1822,20 +1849,24 @@
 check_symbol_exists("getifaddrs"      "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)  # ifaddrs.h
 check_symbol_exists("freeaddrinfo"    "${CURL_INCLUDES}" HAVE_FREEADDRINFO)  # ws2tcpip.h sys/socket.h netdb.h
 check_function_exists("pipe"          HAVE_PIPE)
-check_function_exists("pipe2"         HAVE_PIPE2)
 check_function_exists("eventfd"       HAVE_EVENTFD)
-check_symbol_exists("ftruncate"       "unistd.h" HAVE_FTRUNCATE)
 check_symbol_exists("getpeername"     "${CURL_INCLUDES}" HAVE_GETPEERNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 check_symbol_exists("getsockname"     "${CURL_INCLUDES}" HAVE_GETSOCKNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 check_function_exists("getrlimit"     HAVE_GETRLIMIT)
 check_function_exists("setlocale"     HAVE_SETLOCALE)
 check_function_exists("setrlimit"     HAVE_SETRLIMIT)
 
+if(NOT APPLE)
+  # Apple platforms do not offer pipe2(), but the iPhone Simulator-specific
+  # /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false
+  # detection, omit this feature check for Apple targets.
+  check_function_exists("pipe2"         HAVE_PIPE2)
+endif()
+
 if(NOT WIN32)
   check_function_exists("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # net/if.h
   check_function_exists("realpath"        HAVE_REALPATH)
   check_function_exists("sched_yield"     HAVE_SCHED_YIELD)
-  check_function_exists("snprintf"        HAVE_SNPRINTF)  # to match detection method in ./configure
   check_symbol_exists("strcasecmp"        "string.h" HAVE_STRCASECMP)
   check_symbol_exists("stricmp"           "string.h" HAVE_STRICMP)
   check_symbol_exists("strcmpi"           "string.h" HAVE_STRCMPI)
@@ -1999,9 +2030,9 @@
 if(WIN32)
   list(APPEND CURL_NETWORK_AND_TIME_LIBS "ws2_32")
   if(NOT WINDOWS_STORE)
-    list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi")
+    list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi")  # for if_nametoindex()
   endif()
-  list(APPEND CURL_LIBS "bcrypt")
+  list(APPEND CURL_LIBS "bcrypt")  # for BCryptGenRandom()
 
   # We use crypto functions that are not available for UWP apps
   if(NOT WINDOWS_STORE)
@@ -2010,10 +2041,16 @@
 
   # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL
   if(USE_WIN32_CRYPTO OR USE_SCHANNEL)
-    list(APPEND CURL_LIBS "advapi32" "crypt32")
+    # for CryptAcquireContext(), CryptCreateHash(), CryptDestroyHash(), CryptGetHashParam(), CryptHashData(),
+    #     CryptReleaseContext() in NTLM, md4, md5, sha256, Schannel
+    # for CryptDestroyKey(), CryptEncrypt(), CryptImportKey() in NTLM
+    list(APPEND CURL_LIBS "advapi32")
+    # for Cert*() in openssl.c Native CA, Schannel
+    # for CryptDecodeObjectEx(), CryptQueryObject(), CryptStringToBinary(), PFXImportCertStore() in Schannel
+    list(APPEND CURL_LIBS "crypt32")
   endif()
   if(USE_WINDOWS_SSPI)
-    list(APPEND CURL_LIBS "secur32")
+    list(APPEND CURL_LIBS "secur32")  # for InitSecurityInterface()
   endif()
 endif()
 
@@ -2033,22 +2070,14 @@
 # to consume libcurl, regardless of the compiler used to build libcurl itself.
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
   if(USE_OPENSSL AND TARGET OpenSSL::Crypto)
-    get_target_property(_curl_imported OpenSSL::Crypto IMPORTED)
-    if(_curl_imported)
-      add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED)
-      get_target_property(_curl_libname OpenSSL::Crypto LOCATION)
-      set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}")
-      list(APPEND CURL_LIBS CURL::OpenSSL_Crypto)
-    endif()
+    add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED)
+    set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto)
+    list(APPEND CURL_LIBS CURL::OpenSSL_Crypto)
   endif()
   if(HAVE_LIBZ AND TARGET ZLIB::ZLIB)
-    get_target_property(_curl_imported ZLIB::ZLIB IMPORTED)
-    if(_curl_imported)
-      add_library(CURL::ZLIB INTERFACE IMPORTED)
-      get_target_property(_curl_libname ZLIB::ZLIB LOCATION)
-      set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}")
-      list(APPEND CURL_LIBS CURL::ZLIB)
-    endif()
+    add_library(CURL::ZLIB INTERFACE IMPORTED)
+    set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
+    list(APPEND CURL_LIBS CURL::ZLIB)
   endif()
   if(WIN32)
     add_library(CURL::win32_winsock INTERFACE IMPORTED)
@@ -2063,12 +2092,6 @@
 endif()
 
 if(CURL_LTO)
-  if(CMAKE_VERSION VERSION_LESS 3.9)
-    message(FATAL_ERROR "LTO has been requested, but your cmake version ${CMAKE_VERSION} is to old. You need at least 3.9")
-  endif()
-
-  cmake_policy(SET CMP0069 NEW)
-
   include(CheckIPOSupported)
   check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT _lto_error LANGUAGES C)
   if(CURL_HAS_LTO)
@@ -2164,12 +2187,12 @@
 
 # NTLM support requires crypto functions from various SSL libs.
 # These conditions must match those in lib/curl_setup.h.
-if(NOT CURL_DISABLE_NTLM AND
+if(CURL_ENABLE_NTLM AND
    ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR
     (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR
     USE_GNUTLS OR
     USE_WIN32_CRYPTO OR
-    (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
+    (USE_WOLFSSL AND HAVE_WC_DES_ECBENCRYPT)))
   set(_use_curl_ntlm_core ON)
 endif()
 
@@ -2194,9 +2217,9 @@
 curl_add_if("POP3S"         NOT CURL_DISABLE_POP3 AND _ssl_enabled)
 curl_add_if("IMAP"          NOT CURL_DISABLE_IMAP)
 curl_add_if("IMAPS"         NOT CURL_DISABLE_IMAP AND _ssl_enabled)
-curl_add_if("SMB"           NOT CURL_DISABLE_SMB AND
+curl_add_if("SMB"           CURL_ENABLE_SMB AND
                             _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
-curl_add_if("SMBS"          NOT CURL_DISABLE_SMB AND _ssl_enabled AND
+curl_add_if("SMBS"          CURL_ENABLE_SMB AND _ssl_enabled AND
                             _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
 curl_add_if("SMTP"          NOT CURL_DISABLE_SMTP)
 curl_add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND _ssl_enabled)
@@ -2205,7 +2228,6 @@
 curl_add_if("IPFS"          NOT CURL_DISABLE_IPFS)
 curl_add_if("IPNS"          NOT CURL_DISABLE_IPFS)
 curl_add_if("RTSP"          NOT CURL_DISABLE_RTSP)
-curl_add_if("RTMP"          USE_LIBRTMP)
 curl_add_if("MQTT"          NOT CURL_DISABLE_MQTT)
 curl_add_if("MQTTS"         NOT CURL_DISABLE_MQTT AND _ssl_enabled)
 curl_add_if("WS"            NOT CURL_DISABLE_WEBSOCKETS)
@@ -2227,8 +2249,8 @@
 curl_add_if("brotli"        HAVE_BROTLI)
 curl_add_if("gsasl"         USE_GSASL)
 curl_add_if("zstd"          HAVE_ZSTD)
-curl_add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
-curl_add_if("asyn-rr"       USE_ARES AND ENABLE_THREADED_RESOLVER AND USE_HTTPSRR)
+curl_add_if("AsynchDNS"     USE_RESOLV_ARES OR USE_RESOLV_THREADED)
+curl_add_if("asyn-rr"       USE_ARES AND USE_RESOLV_THREADED AND USE_HTTPSRR)
 curl_add_if("IDN"           (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR
                             USE_WIN32_IDN OR
                             USE_APPLE_IDN)
@@ -2241,7 +2263,7 @@
                             (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
 curl_add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
                             (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-curl_add_if("NTLM"          NOT CURL_DISABLE_NTLM AND
+curl_add_if("NTLM"          CURL_ENABLE_NTLM AND
                             (_use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 curl_add_if("TLS-SRP"       USE_TLS_SRP)
 curl_add_if("HTTP2"         USE_NGHTTP2)
@@ -2251,7 +2273,7 @@
                             OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
                             (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW)))
 curl_add_if("Unicode"       ENABLE_UNICODE)
-curl_add_if("threadsafe"    HAVE_ATOMIC OR (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR WIN32)
+curl_add_if("threadsafe"    HAVE_ATOMIC OR HAVE_THREADS_POSIX OR WIN32)
 curl_add_if("Debug"         ENABLE_DEBUG)
 curl_add_if("ECH"           _ssl_enabled AND HAVE_ECH)
 curl_add_if("HTTPSRR"       _ssl_enabled AND USE_HTTPSRR)
@@ -2261,11 +2283,7 @@
 curl_add_if("AppleSecTrust" USE_APPLE_SECTRUST AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS))
 curl_add_if("NativeCA"      NOT USE_APPLE_SECTRUST AND _ssl_enabled AND CURL_CA_NATIVE)
 if(_items)
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-    list(SORT _items CASE INSENSITIVE)
-  else()
-    list(SORT _items)
-  endif()
+  list(SORT _items CASE INSENSITIVE)
 endif()
 set(CURL_SUPPORTED_FEATURES_LIST "${_items}")
 string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
@@ -2281,11 +2299,7 @@
 curl_add_if("Rustls"           _ssl_enabled AND USE_RUSTLS)
 
 if(_items)
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-    list(SORT _items CASE INSENSITIVE)
-  else()
-    list(SORT _items)
-  endif()
+  list(SORT _items CASE INSENSITIVE)
 endif()
 string(REPLACE ";" " " SSL_BACKENDS "${_items}")
 message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
@@ -2375,29 +2389,15 @@
         # Assume the user will not need this information in the .pc file.
         continue()
       endif()
-      if(_lib MATCHES "CURL::")
-        # This is empty for 'CURL::*' targets and safe to ignore.
-        # Explicitly skip this query to avoid CMake v3.18 and older erroring out.
-        set(_libname "")
-      else()
-        get_target_property(_libname "${_lib}" LOCATION)
-      endif()
-      if(_libname)
-        list(APPEND _explicit_libs "${_libname}")
-      else()
-        get_target_property(_libs "${_lib}" INTERFACE_LINK_LIBRARIES)
-        if(_libs)
-          list(APPEND _explicit_libs "${_libs}")
-        endif()
-        get_target_property(_libdirs "${_lib}" INTERFACE_LINK_DIRECTORIES)
-        if(_libdirs)
-          list(APPEND _explicit_libdirs "${_libdirs}")
-        endif()
-      endif()
-      if(NOT _libname AND NOT _libs AND NOT _libdirs)
+      set(_libdirs "")
+      set(_libs "")
+      curl_collect_target_link_options("${_lib}")  # look into the target recursively
+      list(APPEND _explicit_libdirs ${_libdirs})
+      list(APPEND _explicit_libs ${_libs})
+      if(NOT _libs AND NOT _libdirs AND NOT _lib STREQUAL Threads::Threads)
         message(WARNING "Bad lib in library list: ${_lib}")
       endif()
-      if(_lib STREQUAL OpenSSL::SSL)
+      if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL)  # BoringSSL does not provide openssl.pc
         set(_modules "openssl")
       elseif(_lib STREQUAL ZLIB::ZLIB)
         set(_modules "zlib")
@@ -2578,18 +2578,20 @@
   #   TARGETS_EXPORT_NAME
   #   CURL_SUPPORTED_FEATURES_LIST
   #   CURL_SUPPORTED_PROTOCOLS_LIST
+  #   CURL_USE_CMAKECONFIG
   #   CURL_USE_PKGCONFIG
   #   HAVE_BROTLI
   #   HAVE_GSSAPI
   #   HAVE_LIBIDN2
   #   HAVE_LIBZ ZLIB_VERSION_MAJOR
+  #   HAVE_THREADS_POSIX
+  #   HAVE_THREADS_POSIX_BORINGSSL
   #   HAVE_ZSTD
   #   USE_ARES
   #   USE_BACKTRACE
   #   USE_GNUTLS
   #   USE_GSASL
   #   USE_LIBPSL
-  #   USE_LIBRTMP
   #   USE_LIBSSH
   #   USE_LIBSSH2
   #   USE_LIBUV
@@ -2626,7 +2628,6 @@
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibgsasl.cmake"
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibidn2.cmake"
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibpsl.cmake"
-      "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibrtmp.cmake"
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh.cmake"
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh2.cmake"
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibuv.cmake"
@@ -2651,12 +2652,14 @@
       COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake")
   endif()
 
-  install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl"
-    DESTINATION ${CMAKE_INSTALL_BINDIR}
-    PERMISSIONS
-      OWNER_READ OWNER_WRITE OWNER_EXECUTE
-      GROUP_READ GROUP_EXECUTE
-      WORLD_READ WORLD_EXECUTE)
+  if(BUILD_CURL_EXE)
+    install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl"
+      DESTINATION ${CMAKE_INSTALL_BINDIR}
+      PERMISSIONS
+        OWNER_READ OWNER_WRITE OWNER_EXECUTE
+        GROUP_READ GROUP_EXECUTE
+        WORLD_READ WORLD_EXECUTE)
+  endif()
 
   # The `-DEV` part is important
   string(REGEX REPLACE "([0-9]+\.[0-9]+)\.([0-9]+.*)" "\\2" CPACK_PACKAGE_VERSION_PATCH "${_curl_version}")
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index 10351cb..3b0af7a 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -220,8 +220,8 @@
 #define CURL_HTTPPOST_LARGE (1 << 7)
 
   char *showfilename;               /* The filename to show. If not set, the
-                                       actual filename will be used (if this
-                                       is a file part) */
+                                       actual filename is used (if this is
+                                       a file part) */
   void *userp;                      /* custom pointer used for
                                        HTTPPOST_CALLBACK posts */
   curl_off_t contentlen;            /* alternative length of contents
@@ -229,8 +229,8 @@
                                        set. Added in 7.46.0 */
 };
 
-/* This is a return code for the progress callback that, when returned, will
-   signal libcurl to continue executing the default progress function */
+/* This is a return code for the progress callback that, when returned,
+   signals libcurl to continue executing the default progress function */
 #define CURL_PROGRESSFUNC_CONTINUE 0x10000001
 
 /* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now
@@ -267,17 +267,17 @@
 
 #ifndef CURL_MAX_HTTP_HEADER
 /* The only reason to have a max limit for this is to avoid the risk of a bad
-   server feeding libcurl with a never-ending header that will cause reallocs
+   server feeding libcurl with a never-ending header that causes reallocs
    infinitely */
 #define CURL_MAX_HTTP_HEADER (100*1024)
 #endif
 
 /* This is a magic return code for the write callback that, when returned,
-   will signal libcurl to pause receiving on the current transfer. */
+   signals libcurl to pause receiving on the current transfer. */
 #define CURL_WRITEFUNC_PAUSE 0x10000001
 
 /* This is a magic return code for the write callback that, when returned,
-   will signal an error from the callback. */
+   signals an error from the callback. */
 #define CURL_WRITEFUNC_ERROR 0xFFFFFFFF
 
 typedef size_t (*curl_write_callback)(char *buffer,
@@ -285,7 +285,7 @@
                                       size_t nitems,
                                       void *outstream);
 
-/* This callback will be called when a new resolver request is made */
+/* This callback is called when a new resolver request is made */
 typedef int (*curl_resolver_start_callback)(void *resolver_state,
                                             void *reserved, void *userdata);
 
@@ -385,11 +385,11 @@
                                   curl_off_t offset,
                                   int origin); /* 'whence' */
 
-/* This is a return code for the read callback that, when returned, will
-   signal libcurl to immediately abort the current transfer. */
+/* This is a return code for the read callback that, when returned,
+   signals libcurl to immediately abort the current transfer. */
 #define CURL_READFUNC_ABORT 0x10000000
-/* This is a return code for the read callback that, when returned, will
-   signal libcurl to pause sending data on the current transfer. */
+/* This is a return code for the read callback that, when returned,
+   signals libcurl to pause sending data on the current transfer. */
 #define CURL_READFUNC_PAUSE 0x10000001
 
 /* Return code for when the trailing headers' callback has terminated
@@ -609,7 +609,7 @@
                                     or wrong format */
   CURLE_REMOTE_FILE_NOT_FOUND,   /* 78 - remote file not found */
   CURLE_SSH,                     /* 79 - error from the SSH layer, somewhat
-                                    generic so the error message will be of
+                                    generic so the error message is of
                                     interest when this has happened */
 
   CURLE_SSL_SHUTDOWN_FAILED,     /* 80 - Failed to shut down the SSL
@@ -627,7 +627,7 @@
   CURLE_FTP_BAD_FILE_LIST,       /* 87 - unable to parse FTP file list */
   CURLE_CHUNK_FAILED,            /* 88 - chunk callback reported error */
   CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
-                                    session will be queued */
+                                    session is queued */
   CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
                                      match */
   CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */
@@ -888,7 +888,7 @@
   CURLKHSTAT_REJECT, /* reject the connection, return an error */
   CURLKHSTAT_DEFER,  /* do not accept it, but we cannot answer right now.
                         Causes a CURLE_PEER_FAILED_VERIFICATION error but the
-                        connection will be left intact etc */
+                        connection is left intact etc */
   CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */
   CURLKHSTAT_LAST    /* not for use, only a marker for last-in-list */
 };
@@ -1170,12 +1170,12 @@
    * bytes big. */
   CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10),
 
-  /* Function that will be called to store the output (instead of fwrite). The
-   * parameters will use fwrite() syntax, make sure to follow them. */
+  /* Function that is called to store the output (instead of fwrite). The
+   * parameters use fwrite() syntax, make sure to follow them. */
   CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11),
 
-  /* Function that will be called to read the input (instead of fread). The
-   * parameters will use fread() syntax, make sure to follow them. */
+  /* Function that is called to read the input (instead of fread). The
+   * parameters use fread() syntax, make sure to follow them. */
   CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12),
 
   /* Time-out the read operation after this amount of seconds */
@@ -1329,7 +1329,7 @@
   /* 55 = OBSOLETE */
 
   /* DEPRECATED
-   * Function that will be called instead of the internal progress display
+   * Function that is called instead of the internal progress display
    * function. This function should be defined as the curl_progress_callback
    * prototype defines. */
   CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56,
@@ -1358,7 +1358,7 @@
 
   /* Set the krb4/5 security level, this also enables krb4/5 awareness. This
    * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string
-   * is set but does not match one of these, 'private' will be used. */
+   * is set but does not match one of these, 'private' is used. */
   CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63,
                     8.17.0, "removed"),
 
@@ -1411,8 +1411,8 @@
      OK within this time, then fine... This only aborts the connect phase. */
   CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78),
 
-  /* Function that will be called to store headers (instead of fwrite). The
-   * parameters will use fwrite() syntax, make sure to follow them. */
+  /* Function that is called to store headers (instead of fwrite). The
+   * parameters use fwrite() syntax, make sure to follow them. */
   CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79),
 
   /* Set this to force the HTTP request to get back to GET. Only really usable
@@ -1437,7 +1437,7 @@
   CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84),
 
   /* Specifically switch on or off the FTP engine's use of the EPSV command. By
-     default, that one will always be attempted before the more traditional
+     default, that one is always attempted before the more traditional
      PASV command. */
   CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85),
 
@@ -1515,7 +1515,7 @@
   CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105),
 
   /* Specifically switch on or off the FTP engine's use of the EPRT command (
-     it also disables the LPRT attempt). By default, those ones will always be
+     it also disables the LPRT attempt). By default, those ones are always
      attempted before the good old traditional PORT command. */
   CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106),
 
@@ -1555,7 +1555,7 @@
      systems with support for more than one, i.e IPv4 _and_ IPv6. */
   CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113),
 
-  /* Set this option to limit the size of a file that will be downloaded from
+  /* Set this option to limit the size of a file that is to be downloaded from
      an HTTP or FTP server.
 
      Note there is also _LARGE version which adds large file support for
@@ -1572,13 +1572,13 @@
    */
   CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116),
 
-  /* Sets the maximum size of data that will be downloaded from
+  /* Sets the maximum size of data that is to be downloaded from
    * an HTTP or FTP server. See MAXFILESIZE above for the LONG version.
    */
   CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117),
 
   /* Set this option to the filename of your .netrc file you want libcurl
-     to parse (using the CURLOPT_NETRC option). If not set, libcurl will do
+     to parse (using the CURLOPT_NETRC option). If not set, libcurl does
      a poor attempt to find the user's home directory and check for a .netrc
      file in there. */
   CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118),
@@ -1636,7 +1636,7 @@
 
   /* Set to non-zero to skip the IP address received in a 227 PASV FTP server
      response. Typically used for FTP-SSL purposes but is not restricted to
-     that. libcurl will then instead use the same IP address it used for the
+     that. libcurl is then instead using the same IP address it used for the
      control connection. */
   CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137),
 
@@ -1648,7 +1648,7 @@
   CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139),
 
   /* Number of ports to try, including the first one set with LOCALPORT.
-     Thus, setting it to 1 will make no additional attempts but the first.
+     Thus, setting it to 1 makes no additional attempts but the first.
   */
   CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140),
 
@@ -1656,19 +1656,19 @@
      extracting it with CURLINFO_LASTSOCKET */
   CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141),
 
-  /* Function that will be called to convert from the
+  /* Function that is called to convert from the
      network encoding (instead of using the iconv calls in libcurl) */
   CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION,
                     CURLOPTTYPE_FUNCTIONPOINT, 142,
                     7.82.0, "Serves no purpose anymore"),
 
-  /* Function that will be called to convert to the
+  /* Function that is called to convert to the
      network encoding (instead of using the iconv calls in libcurl) */
   CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION,
                     CURLOPTTYPE_FUNCTIONPOINT, 143,
                     7.82.0, "Serves no purpose anymore"),
 
-  /* Function that will be called to convert from UTF8
+  /* Function that is called to convert from UTF8
      (instead of using the iconv calls in libcurl)
      Note that this is used only for SSL certificate processing */
   CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION,
@@ -1762,9 +1762,9 @@
 
   /* Comma separated list of hostnames defining no-proxy zones. These should
      match both hostnames directly, and hostnames within a domain. For
-     example, local.com will match local.com and www.local.com, but NOT
+     example, local.com matches local.com and www.local.com, but NOT
      notlocal.com or www.notlocal.com. For compatibility with other
-     implementations of this, .local.com will be considered to be the same as
+     implementations of this, .local.com is considered to be the same as
      local.com. A single * is the only valid wildcard, and effectively
      disables the use of proxy. */
   CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177),
@@ -1874,8 +1874,8 @@
      in outgoing requests. The current default is 0, but it might change in a
      future libcurl release.
 
-     libcurl will ask for the compressed methods it knows of, and if that
-     is not any, it will not ask for transfer-encoding at all even if this
+     libcurl asks for the compressed methods it knows of, and if that
+     is not any, it does not ask for transfer-encoding at all even if this
      option is set to 1. */
   CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207),
 
@@ -1911,7 +1911,7 @@
   /* Enable/disable SASL initial response */
   CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218),
 
-  /* Function that will be called instead of the internal progress display
+  /* Function that is called instead of the internal progress display
    * function. This function should be defined as the curl_xferinfo_callback
    * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */
   CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219),
@@ -2093,7 +2093,7 @@
   /* Head start in milliseconds to give happy eyeballs. */
   CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271),
 
-  /* Function that will be called before a resolver request is made */
+  /* Function that is called before a resolver request is made */
   CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272),
 
   /* User data to pass to the resolver start callback. */
@@ -2202,7 +2202,7 @@
   /* used by scp/sftp to verify the host's public key */
   CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311),
 
-  /* Function that will be called immediately before the initial request
+  /* Function that is called immediately before the initial request
      is made on a connection (after any protocol negotiation step). */
   CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312),
 
@@ -2348,13 +2348,13 @@
 #define CURL_RTSPREQ_LAST          12L /* not used */
 
   /* These enums are for use with the CURLOPT_NETRC option. */
-#define CURL_NETRC_IGNORED  0L /* The .netrc will never be read.
+#define CURL_NETRC_IGNORED  0L /* The .netrc is never read.
                                   This is the default. */
-#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred
+#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred
                                   to one in the .netrc. */
-#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored.
+#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL is ignored.
                                   Unless one is set programmatically, the
-                                  .netrc will be queried. */
+                                  .netrc is queried. */
 enum CURL_NETRC_OPTION {
   /* we set a single member here, to make sure we still provide the enum, but
      the values to use are defined above with L suffixes */
@@ -2635,7 +2635,7 @@
 
 /*
  * callback function for curl_formget()
- * The void *arg pointer will be the one passed as second argument to
+ * The void *arg pointer is the one passed as second argument to
  *   curl_formget().
  * The character buffer passed to it must not be freed.
  * Should return the buffer length passed to it as the argument "len" on
@@ -2650,7 +2650,7 @@
  * DESCRIPTION
  *
  * Serialize a curl_httppost struct built with curl_formadd().
- * Accepts a void pointer as second argument which will be passed to
+ * Accepts a void pointer as second argument which is passed to
  * the curl_formget_callback function.
  * Returns 0 on success.
  */
@@ -2695,7 +2695,7 @@
  * %XX versions). This function returns a new allocated string or NULL if an
  * error occurred.
  */
-CURL_EXTERN char *curl_easy_escape(CURL *handle,
+CURL_EXTERN char *curl_easy_escape(CURL *curl,
                                    const char *string,
                                    int length);
 
@@ -2714,9 +2714,9 @@
  * Conversion Note: On non-ASCII platforms the ASCII %XX codes are
  * converted into the host encoding.
  */
-CURL_EXTERN char *curl_easy_unescape(CURL *handle,
+CURL_EXTERN char *curl_easy_unescape(CURL *curl,
                                      const char *string,
-                                     int length,
+                                     int inlength,
                                      int *outlength);
 
 /* the previous version */
@@ -2756,7 +2756,7 @@
  * initialize libcurl and set user defined memory management callback
  * functions. Users can implement memory management routines to check for
  * memory leaks, check for misuse of the curl library etc. User registered
- * callback routines will be invoked by this library instead of the system
+ * callback routines is invoked by this library instead of the system
  * memory management routines like malloc, free etc.
  */
 CURL_EXTERN CURLcode curl_global_init_mem(long flags,
@@ -2806,8 +2806,8 @@
  *
  * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The
  * backend can also be specified via the name parameter (passing -1 as id). If
- * both id and name are specified, the name will be ignored. If neither id nor
- * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND
+ * both id and name are specified, the name is ignored. If neither id nor
+ * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND
  * and set the "avail" pointer to the NULL-terminated list of available
  * backends.
  *
@@ -2818,7 +2818,7 @@
  * NULL-terminated list of available SSL backends.
  *
  * The SSL backend can be set only once. If it has already been set, a
- * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.
+ * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE.
  */
 
 struct curl_ssl_backend {
@@ -2842,8 +2842,8 @@
  *
  * DESCRIPTION
  *
- * Appends a string to a linked list. If no list exists, it will be created
- * first. Returns the new list, after appending.
+ * Appends a string to a linked list. If no list exists, it is created first.
+ * Returns the new list, after appending.
  */
 CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list,
                                                  const char *data);
@@ -2991,7 +2991,8 @@
   CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68,
   CURLINFO_HTTPAUTH_USED    = CURLINFO_LONG + 69,
   CURLINFO_PROXYAUTH_USED   = CURLINFO_LONG + 70,
-  CURLINFO_LASTONE          = 70
+  CURLINFO_SIZE_DELIVERED   = CURLINFO_OFF_T + 71,
+  CURLINFO_LASTONE          = 71
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -3075,9 +3076,8 @@
 } CURLSHoption;
 
 CURL_EXTERN CURLSH *curl_share_init(void);
-CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option,
-                                         ...);
-CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share);
+CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...);
+CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *sh);
 
 /****************************************************************************
  * Structures for querying information about the curl library at runtime.
@@ -3245,10 +3245,10 @@
  * DESCRIPTION
  *
  * The curl_easy_pause function pauses or unpauses transfers. Select the new
- * state by setting the bitmask, use the convenience defines below.
+ * state by setting the action bitmask, use the convenience defines below.
  *
  */
-CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
+CURL_EXTERN CURLcode curl_easy_pause(CURL *curl, int action);
 
 #define CURLPAUSE_RECV      (1 << 0)
 #define CURLPAUSE_RECV_CONT 0
@@ -3267,7 +3267,7 @@
  * The curl_easy_ssls_import function adds a previously exported SSL session
  * to the SSL session cache of the easy handle (or the underlying share).
  */
-CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle,
+CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *curl,
                                            const char *session_key,
                                            const unsigned char *shmac,
                                            size_t shmac_len,
@@ -3276,7 +3276,7 @@
 
 /* This is the curl_ssls_export_cb callback prototype. It
  * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */
-typedef CURLcode curl_ssls_export_cb(CURL *handle,
+typedef CURLcode curl_ssls_export_cb(CURL *curl,
                                      void *userptr,
                                      const char *session_key,
                                      const unsigned char *shmac,
@@ -3298,7 +3298,7 @@
  * callback.
  *
  */
-CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle,
+CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *curl,
                                            curl_ssls_export_cb *export_fn,
                                            void *userptr);
 
@@ -3325,18 +3325,19 @@
 #else
 #if defined(__STDC__) && (__STDC__ >= 1)
 #if 0 /* Triggers clang -Wdisabled-macro-expansion, skip for CMake.  */
-/* This preprocessor magic that replaces a call with the exact same call is
-   only done to make sure application authors pass exactly three arguments
-   to these functions. Use recursive macros to allow using these symbols via
-   the C++ global namespace '::' or reusing them as method names. */
+/* This preprocessor magic ensures that application authors pass exactly three
+   arguments to these functions. For compatibility with C++ global namespace
+   '::' and reusing these symbols as method names, while also avoiding
+   recursive macros, use a two-stage solution. */
+#define curl_exactly_three_arguments(a, b, c) (a, b, c)
 #define curl_easy_setopt(handle, opt, param) \
-  curl_easy_setopt(handle, opt, param)
+  curl_easy_setopt curl_exactly_three_arguments(handle, opt, param)
 #define curl_easy_getinfo(handle, info, arg) \
-  curl_easy_getinfo(handle, info, arg)
+  curl_easy_getinfo curl_exactly_three_arguments(handle, info, arg)
 #define curl_share_setopt(share, opt, param) \
-  curl_share_setopt(share, opt, param)
+  curl_share_setopt curl_exactly_three_arguments(share, opt, param)
 #define curl_multi_setopt(handle, opt, param) \
-  curl_multi_setopt(handle, opt, param)
+  curl_multi_setopt curl_exactly_three_arguments(handle, opt, param)
 #endif
 #endif /* __STDC__ >= 1 */
 #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 2ea215b..8ae3fbc 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,16 +32,16 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.19.0"
+#define LIBCURL_VERSION "8.20.0"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 19
+#define LIBCURL_VERSION_MINOR 20
 #define LIBCURL_VERSION_PATCH 0
 /* This is the numeric version of the libcurl version number, meant for easier
-   parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
-   always follow this syntax:
+   parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always
+   follows this syntax:
 
          0xXXYYZZ
 
@@ -58,7 +58,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 0x081300
+#define LIBCURL_VERSION_NUM 0x081400
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h
index 0be6915..197e6e7 100644
--- a/Utilities/cmcurl/include/curl/easy.h
+++ b/Utilities/cmcurl/include/curl/easy.h
@@ -51,7 +51,7 @@
  * Request internal information from the curl session with this function.
  * The third argument MUST be pointing to the specific type of the used option
  * which is documented in each man page of the option. The data pointed to
- * will be filled in accordingly and can be relied upon only if the function
+ * is filled in accordingly and can be relied upon only if the function
  * returns CURLE_OK. This function is intended to get used *AFTER* a performed
  * transfer, all results from this function are undefined until the transfer
  * is completed.
diff --git a/Utilities/cmcurl/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h
index 85c10c8..5c3281a 100644
--- a/Utilities/cmcurl/include/curl/header.h
+++ b/Utilities/cmcurl/include/curl/header.h
@@ -55,14 +55,14 @@
   CURLHE_NOT_BUILT_IN   /* if API was disabled in the build */
 } CURLHcode;
 
-CURL_EXTERN CURLHcode curl_easy_header(CURL *easy,
+CURL_EXTERN CURLHcode curl_easy_header(CURL *curl,
                                        const char *name,
-                                       size_t index,
+                                       size_t nameindex,
                                        unsigned int origin,
                                        int request,
                                        struct curl_header **hout);
 
-CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy,
+CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *curl,
                                                      unsigned int origin,
                                                      int request,
                                                      struct curl_header *prev);
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h
index ad6f53f..060b73e 100644
--- a/Utilities/cmcurl/include/curl/multi.h
+++ b/Utilities/cmcurl/include/curl/multi.h
@@ -132,8 +132,8 @@
  *
  * Returns: CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,
-                                            CURL *curl_handle);
+CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *m,
+                                            CURL *curl);
 
 /*
  * Name:    curl_multi_remove_handle()
@@ -142,8 +142,8 @@
  *
  * Returns: CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
-                                               CURL *curl_handle);
+CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *m,
+                                               CURL *curl);
 
 /*
  * Name:    curl_multi_fdset()
@@ -154,7 +154,7 @@
  *
  * Returns: CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *m,
                                        fd_set *read_fd_set,
                                        fd_set *write_fd_set,
                                        fd_set *exc_fd_set,
@@ -168,7 +168,7 @@
  *
  * Returns:  CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_wait(CURLM *m,
                                       struct curl_waitfd extra_fds[],
                                       unsigned int extra_nfds,
                                       int timeout_ms,
@@ -182,7 +182,7 @@
  *
  * Returns:  CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_poll(CURLM *m,
                                       struct curl_waitfd extra_fds[],
                                       unsigned int extra_nfds,
                                       int timeout_ms,
@@ -195,7 +195,7 @@
  *
  * Returns:  CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle);
+CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *m);
 
 /*
  * Name:    curl_multi_perform()
@@ -213,7 +213,7 @@
  *          still have occurred problems on individual transfers even when
  *          this returns OK.
  */
-CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_perform(CURLM *m,
                                          int *running_handles);
 
 /*
@@ -221,12 +221,12 @@
  *
  * Desc:    Cleans up and removes a whole multi stack. It does not free or
  *          touch any individual easy handles in any way. We need to define
- *          in what state those handles will be if this function is called
- *          in the middle of a transfer.
+ *          in what state those handles are going to be if this function is
+ *          called in the middle of a transfer.
  *
  * Returns: CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
+CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *m);
 
 /*
  * Name:    curl_multi_info_read()
@@ -236,19 +236,19 @@
  *          error code from the transfer or the fact that a transfer is
  *          completed. More details on these should be written down as well.
  *
- *          Repeated calls to this function will return a new struct each
- *          time, until a special "end of msgs" struct is returned as a signal
- *          that there is no more to get at this point.
+ *          Repeated calls to this function return a new struct each time,
+ *          until a special "end of msgs" struct is returned as a signal that
+ *          there is no more to get at this point.
  *
- *          The data the returned pointer points to will not survive calling
+ *          The data the returned pointer points to does not survive calling
  *          curl_multi_cleanup().
  *
  *          The 'CURLMsg' struct is meant to be simple and only contain basic
- *          information. If more involved information is wanted, we will
- *          provide the particular "transfer handle" in that struct and that
+ *          information. If more involved information is wanted, we provide
+ *          the particular "transfer handle" in that struct and that
  *          should/could/would be used in subsequent curl_easy_getinfo() calls
  *          (or similar). The point being that we must never expose complex
- *          structs to applications, as then we will undoubtably get backwards
+ *          structs to applications, as then we undoubtably get backwards
  *          compatibility problems in the future.
  *
  * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out
@@ -256,7 +256,7 @@
  *          queue (after this read) in the integer the second argument points
  *          to.
  */
-CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
+CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *m,
                                           int *msgs_in_queue);
 
 /*
@@ -308,21 +308,21 @@
  *
  * Returns: The callback should return zero.
  */
-typedef int (*curl_multi_timer_callback)(CURLM *multi,    /* multi handle */
+typedef int (*curl_multi_timer_callback)(CURLM *m,        /* multi handle */
                                          long timeout_ms, /* see above */
                                          void *userp);    /* private callback
                                                              pointer */
 
 CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()")
-curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles);
+curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles);
 
-CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *m,
                                                curl_socket_t s,
                                                int ev_bitmask,
                                                int *running_handles);
 
 CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()")
-curl_multi_socket_all(CURLM *multi_handle, int *running_handles);
+curl_multi_socket_all(CURLM *m, int *running_handles);
 
 #ifndef CURL_ALLOW_OLD_MULTI_SOCKET
 /* This macro below was added in 7.16.3 to push users who recompile to use
@@ -340,8 +340,8 @@
  *
  * Returns: CURLM error code.
  */
-CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,
-                                         long *milliseconds);
+CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *m,
+                                         long *timeout_ms);
 
 typedef enum {
   /* This is the socket callback function pointer */
@@ -369,11 +369,11 @@
   CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8),
 
   /* a connection with a content-length longer than this
-     will not be considered for pipelining */
+     is not considered for pipelining */
   CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9),
 
   /* a connection with a chunk length longer than this
-     will not be considered for pipelining */
+     is not considered for pipelining */
   CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10),
 
   /* a list of site names(+port) that are blocked from pipelining */
@@ -403,20 +403,30 @@
   /* This is the argument passed to the notify callback */
   CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19),
 
+  /* maximum number of threads used with threaded DNS resolver */
+  CURLOPT(CURLMOPT_RESOLVE_THREADS_MAX, CURLOPTTYPE_LONG, 20),
+
+  /* set to 1L for not joining threads when multi is cleaned up */
+  CURLOPT(CURLMOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 21),
+
   CURLMOPT_LASTENTRY /* the last unused */
 } CURLMoption;
 
 /* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */
 
-/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing
-   connections. Connections that are idle will be closed. Ongoing transfers
-   will continue with the connection they have. */
-#define CURLMNWC_CLEAR_CONNS (1L << 0)
+/* - CURLMNWC_CLEAR_ALL tells libcurl to clear "everything" that could be
+   associated with this network, including both connections and DNS data. */
+#define CURLMNWC_CLEAR_ALL (1L << 0)
 
-/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing
-   connections. Connections that are idle will be closed. Ongoing transfers
-   will continue with the connection they have. */
-#define CURLMNWC_CLEAR_DNS (1L << 0)
+/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing
+   connections. Connections that are idle are closed. Ongoing transfers do
+   continue with the connection they have. */
+#define CURLMNWC_CLEAR_CONNS (1L << 1)
+
+/* - CURLMNWC_CLEAR_DNS tells libcurl to clear the DNS cache associated with
+   this multi handle. Ongoing transfers keep using their already resolved
+   addresses, but future name resolutions are performed again. */
+#define CURLMNWC_CLEAR_DNS (1L << 2)
 
 /*
  * Name:    curl_multi_setopt()
@@ -425,7 +435,7 @@
  *
  * Returns: CURLM error code.
  */
-CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *m,
                                         CURLMoption option, ...);
 
 /*
@@ -437,7 +447,7 @@
  *
  * Returns: CURLM error code.
  */
-CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_assign(CURLM *m,
                                         curl_socket_t sockfd, void *sockp);
 
 /*
@@ -450,7 +460,7 @@
  *
  * Returns: NULL on failure, otherwise a CURL **array pointer
  */
-CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle);
+CURL_EXTERN CURL **curl_multi_get_handles(CURLM *m);
 
 typedef enum {
   CURLMINFO_NONE, /* first, never use this */
@@ -479,7 +489,7 @@
  *
  * Returns: CULRM_OK or error when value could not be obtained.
  */
-CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *m,
                                           CURLMinfo_offt info,
                                           curl_off_t *pvalue);
 
@@ -518,7 +528,7 @@
  *
  * Returns: CURLMcode type, general multi error code.
  */
-CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi,
+CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *m,
                                          struct curl_waitfd *ufds,
                                          unsigned int size,
                                          unsigned int *fd_count);
@@ -532,15 +542,15 @@
 /*
  * Callback to install via CURLMOPT_NOTIFYFUNCTION.
  */
-typedef void (*curl_notify_callback)(CURLM *multi,
+typedef void (*curl_notify_callback)(CURLM *m,
                                      unsigned int notification,
                                      CURL *easy,
                                      void *user_data);
 
-CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *multi,
+CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *m,
                                                 unsigned int notification);
 
-CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *multi,
+CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *m,
                                                unsigned int notification);
 
 #ifdef __cplusplus
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h
index 064833d..c2dbab5 100644
--- a/Utilities/cmcurl/include/curl/system.h
+++ b/Utilities/cmcurl/include/curl/system.h
@@ -45,7 +45,7 @@
  * size of off_t is independent of large file support settings. Keep your
  * build on the safe side avoiding an off_t gating. If you have a 64-bit
  * off_t then take for sure that another 64-bit data type exists, dig deeper
- * and you will find it.
+ * to find it.
  *
  */
 
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h
index d9a672e..d600c73 100644
--- a/Utilities/cmcurl/include/curl/typecheck-gcc.h
+++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -37,8 +37,8 @@
  *       order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x
  *       when compiling with -Wlogical-op.
  *
- * To add an option that uses the same type as an existing option, you will
- * need to extend the appropriate _curl_*_option macro
+ * To add an option that uses the same type as an existing option, you need
+ * to extend the appropriate _curl_*_option macro
  */
 
 #define curl_easy_setopt(handle, option, value)                         \
@@ -856,7 +856,7 @@
                                             const void *);
 #ifdef HEADER_SSL_H
 /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX
- * this will of course break if we are included before OpenSSL headers...
+ * this of course breaks if we are included before OpenSSL headers...
  */
 typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *);
 typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *);
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index f7a42b3..b1f3a23 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -114,10 +114,10 @@
 
 /*
  * curl_url_cleanup() frees the CURLU handle and related resources used for
- * the URL parsing. It will not free strings previously returned with the URL
+ * the URL parsing. It does not free strings previously returned with the URL
  * API.
  */
-CURL_EXTERN void curl_url_cleanup(CURLU *handle);
+CURL_EXTERN void curl_url_cleanup(CURLU *u);
 
 /*
  * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new
@@ -130,15 +130,15 @@
  * handle. Returns error code. The returned pointer MUST be freed with
  * curl_free() afterwards.
  */
-CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what,
+CURL_EXTERN CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
                                    char **part, unsigned int flags);
 
 /*
  * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns
- * error code. The passed in string will be copied. Passing a NULL instead of
+ * error code. The passed in string is copied. Passing a NULL instead of
  * a part string, clears that part.
  */
-CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what,
+CURL_EXTERN CURLUcode curl_url_set(CURLU *u, CURLUPart what,
                                    const char *part, unsigned int flags);
 
 /*
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h
index 402a2ca..bf93715 100644
--- a/Utilities/cmcurl/include/curl/websockets.h
+++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -67,7 +67,7 @@
  * Sends data over the websocket connection. Use after successful
  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
  */
-CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
+CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer_arg,
                                   size_t buflen, size_t *sent,
                                   curl_off_t fragsize,
                                   unsigned int flags);
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index 673e9be..a11224c 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -138,9 +138,9 @@
   endif()
 endif()
 
-if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
+if(SHARE_LIB_OBJECT)
   set(LIB_OBJECT "libcurl_object")
-  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})  # Requires CMake 3.12
+  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
   if(WIN32)
     # Define CURL_STATICLIB always, to disable __declspec(dllexport) for
     # exported libcurl symbols. We handle exports via libcurl.def instead.
@@ -168,6 +168,10 @@
   if(CURL_CLANG_TIDY)
     set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF)
   endif()
+  if(CURL_ANALYZER_CFLAGS)
+    set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF)
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS})
+  endif()
   if(CURL_CODE_COVERAGE)
     set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
     set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
@@ -217,6 +221,10 @@
       set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF)
     endif()
   endif()
+  if(CURL_ANALYZER_CFLAGS)
+    set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF)
+    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS})
+  endif()
   if(CURL_CODE_COVERAGE)
     set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
     set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
@@ -225,27 +233,6 @@
   target_include_directories(${LIB_STATIC} INTERFACE
     "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
     "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
-
-  # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to
-  # targets. It expected libs to have an absolute filename. As a workaround,
-  # manually apply dependency libdirs, for CMake consumers without this policy.
-  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17)
-    cmake_policy(GET CMP0099 _has_CMP0099)  # https://cmake.org/cmake/help/latest/policy/CMP0099.html
-  endif()
-  if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CURL_LIBS)
-    set(_curl_libdirs "")
-    foreach(_curl_lib IN LISTS CURL_LIBS)
-      if(TARGET "${_curl_lib}")
-        get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES)
-        if(_curl_libdir)
-          list(APPEND _curl_libdirs "${_curl_libdir}")
-        endif()
-      endif()
-    endforeach()
-    if(_curl_libdirs)
-      target_link_directories(${LIB_STATIC} INTERFACE ${_curl_libdirs})
-    endif()
-  endif()
 endif()
 
 if(BUILD_SHARED_LIBS)
@@ -282,14 +269,14 @@
   if(CURL_CLANG_TIDY)
     set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF)
   endif()
+  if(CURL_ANALYZER_CFLAGS)
+    set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF)
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS})
+  endif()
   if(CURL_CODE_COVERAGE)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
-    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS})
-    else()
-      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS ${CURL_COVERAGE_LDFLAGS})
-    endif()
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS})
   endif()
 
   target_include_directories(${LIB_SHARED} INTERFACE
@@ -299,7 +286,7 @@
   if(CMAKE_DLL_NAME_WITH_SOVERSION OR
     CYGWIN OR
     APPLE OR
-    CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
+    AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
     CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
     CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
     CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR
@@ -360,11 +347,7 @@
     set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
     check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
     if(HAVE_VERSIONED_SYMBOLS)
-      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-        set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
-      else()
-        set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
-      endif()
+      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
     else()
       message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
     endif()
@@ -402,7 +385,7 @@
 if(PERL_EXECUTABLE)
   add_custom_target(curl-optiontable
     COMMENT "Generating lib/easyoptions.c" VERBATIM USES_TERMINAL
-    COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" < "${PROJECT_SOURCE_DIR}/include/curl/curl.h"
+    COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h"
       > "${PROJECT_SOURCE_DIR}/lib/easyoptions.c"
     DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h"
   )
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index 84b4e54..d762f72 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -47,7 +47,6 @@
 LIB_CURLX_HFILES =      \
   curlx/base64.h        \
   curlx/basename.h      \
-  curlx/curlx.h         \
   curlx/dynbuf.h        \
   curlx/fopen.h         \
   curlx/inet_ntop.h     \
@@ -150,6 +149,7 @@
   asyn-thrdd.c       \
   bufq.c             \
   bufref.c           \
+  cf-dns.c           \
   cf-h1-proxy.c      \
   cf-h2-proxy.c      \
   cf-haproxy.c       \
@@ -172,7 +172,6 @@
   curl_memrchr.c     \
   curl_ntlm_core.c   \
   curl_range.c       \
-  curl_rtmp.c        \
   curl_sasl.c        \
   curl_sha512_256.c  \
   curl_share.c       \
@@ -182,6 +181,7 @@
   cw-out.c           \
   cw-pause.c         \
   dict.c             \
+  dnscache.c         \
   doh.c              \
   dynhds.c           \
   easy.c             \
@@ -236,6 +236,7 @@
   pingpong.c         \
   pop3.c             \
   progress.c         \
+  protocol.c         \
   psl.c              \
   rand.c             \
   ratelimit.c        \
@@ -259,6 +260,8 @@
   system_win32.c     \
   telnet.c           \
   tftp.c             \
+  thrdpool.c         \
+  thrdqueue.c        \
   transfer.c         \
   uint-bset.c        \
   uint-hash.c        \
@@ -276,6 +279,7 @@
   asyn.h             \
   bufq.h             \
   bufref.h           \
+  cf-dns.h           \
   cf-h1-proxy.h      \
   cf-h2-proxy.h      \
   cf-haproxy.h       \
@@ -304,7 +308,6 @@
   curl_ntlm_core.h   \
   curl_printf.h      \
   curl_range.h       \
-  curl_rtmp.h        \
   curl_sasl.h        \
   curl_setup.h       \
   curl_sha256.h      \
@@ -316,6 +319,7 @@
   cw-out.h           \
   cw-pause.h         \
   dict.h             \
+  dnscache.h         \
   doh.h              \
   dynhds.h           \
   easy_lock.h        \
@@ -327,6 +331,7 @@
   fileinfo.h         \
   formdata.h         \
   ftp.h              \
+  ftp-int.h          \
   ftplistparser.h    \
   functypes.h        \
   getinfo.h          \
@@ -362,6 +367,7 @@
   pingpong.h         \
   pop3.h             \
   progress.h         \
+  protocol.h         \
   psl.h              \
   rand.h             \
   ratelimit.h        \
@@ -386,6 +392,8 @@
   system_win32.h     \
   telnet.h           \
   tftp.h             \
+  thrdpool.h         \
+  thrdqueue.h        \
   transfer.h         \
   uint-bset.h        \
   uint-hash.h        \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index 34da3e1..81b5379 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -45,6 +45,24 @@
 
 #define H3VERSION "h3"
 
+#if defined(DEBUGBUILD) || defined(UNITTESTS)
+/* to play well with debug builds, we can *set* a fixed time this will
+   return */
+static time_t altsvc_debugtime(void *unused)
+{
+  const char *timestr = getenv("CURL_TIME");
+  (void)unused;
+  if(timestr) {
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, TIME_T_MAX);
+    return (time_t)val;
+  }
+  return time(NULL);
+}
+#undef time
+#define time(x) altsvc_debugtime(x)
+#endif
+
 /* Given the ALPN ID, return the name */
 const char *Curl_alpnid2str(enum alpnid id)
 {
@@ -126,6 +144,20 @@
                          srcport, dstport);
 }
 
+/* append the new entry to the list after possibly removing an old entry
+   first */
+static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as)
+{
+  while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) {
+    /* It's full. Remove the first entry in the list */
+    struct Curl_llist_node *e = Curl_llist_head(&asi->list);
+    struct altsvc *oldas = Curl_node_elem(e);
+    Curl_node_remove(e);
+    altsvc_free(oldas);
+  }
+  Curl_llist_append(&asi->list, as, &as->node);
+}
+
 /* only returns SERIOUS errors */
 static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
 {
@@ -162,25 +194,27 @@
      curlx_str_newline(&line))
     ;
   else {
-    struct altsvc *as;
     char dbuf[MAX_ALTSVC_DATELEN + 1];
     time_t expires = 0;
+    time_t now = time(NULL);
 
     /* The date parser works on a null-terminated string. The maximum length
        is upheld by curlx_str_quotedword(). */
     memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
     dbuf[curlx_strlen(&date)] = 0;
     Curl_getdate_capped(dbuf, &expires);
-    as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
-                       (size_t)srcport, (size_t)dstport);
-    if(as) {
-      as->expires = expires;
-      as->prio = 0; /* not supported, set zero */
-      as->persist = persist ? 1 : 0;
-      Curl_llist_append(&asi->list, as, &as->node);
+
+    if(now < expires) {
+      struct altsvc *as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
+                                        (size_t)srcport, (size_t)dstport);
+      if(as) {
+        as->expires = expires;
+        as->persist = persist ? 1 : 0;
+        altsvc_append(asi, as);
+      }
+      else
+        return CURLE_OUT_OF_MEMORY;
     }
-    else
-      return CURLE_OUT_OF_MEMORY;
   }
 
   return CURLE_OK;
@@ -208,19 +242,22 @@
 
   fp = curlx_fopen(file, FOPEN_READTEXT);
   if(fp) {
-    bool eof = FALSE;
-    struct dynbuf buf;
-    curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
-    do {
-      result = Curl_get_line(&buf, fp, &eof);
-      if(!result) {
-        const char *lineptr = curlx_dyn_ptr(&buf);
-        curlx_str_passblanks(&lineptr);
-        if(curlx_str_single(&lineptr, '#'))
-          altsvc_add(asi, lineptr);
-      }
-    } while(!result && !eof);
-    curlx_dyn_free(&buf); /* free the line buffer */
+    curlx_struct_stat stat;
+    if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
+      bool eof = FALSE;
+      struct dynbuf buf;
+      curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
+      do {
+        result = Curl_get_line(&buf, fp, &eof);
+        if(!result) {
+          const char *lineptr = curlx_dyn_ptr(&buf);
+          curlx_str_passblanks(&lineptr);
+          if(curlx_str_single(&lineptr, '#'))
+            altsvc_add(asi, lineptr);
+        }
+      } while(!result && !eof);
+      curlx_dyn_free(&buf); /* free the line buffer */
+    }
     curlx_fclose(fp);
   }
   return result;
@@ -257,7 +294,7 @@
                 "%s %s%s%s %u "
                 "\"%d%02d%02d "
                 "%02d:%02d:%02d\" "
-                "%u %u\n",
+                "%d 0\n", /* prio still always zero */
                 Curl_alpnid2str(as->src.alpnid),
                 src6_pre, as->src.host, src6_post,
                 as->src.port,
@@ -268,7 +305,7 @@
 
                 stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
                 stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
-                as->persist, as->prio);
+                as->persist);
   return CURLE_OK;
 }
 
@@ -427,24 +464,6 @@
   }
 }
 
-#if defined(DEBUGBUILD) || defined(UNITTESTS)
-/* to play well with debug builds, we can *set* a fixed time this will
-   return */
-static time_t altsvc_debugtime(void *unused)
-{
-  const char *timestr = getenv("CURL_TIME");
-  (void)unused;
-  if(timestr) {
-    curl_off_t val;
-    curlx_str_number(&timestr, &val, TIME_T_MAX);
-    return (time_t)val;
-  }
-  return time(NULL);
-}
-#undef time
-#define time(x) altsvc_debugtime(x)
-#endif
-
 /*
  * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
  * the data correctly in the cache.
@@ -588,7 +607,7 @@
             else
               as->expires = maxage + secs;
             as->persist = persist;
-            Curl_llist_append(&asi->list, as, &as->node);
+            altsvc_append(asi, as);
             infof(data, "Added alt-svc: %.*s:%d over %s",
                   (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
                   dstport, Curl_alpnid2str(dstalpnid));
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h
index 2931af4..dc1740b 100644
--- a/Utilities/cmcurl/lib/altsvc.h
+++ b/Utilities/cmcurl/lib/altsvc.h
@@ -28,9 +28,12 @@
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
 #include "llist.h"
 
+/* the maximum number of alt-svc entries kept in a single cache */
+#define MAX_ALTSVC_ENTRIES 5000
+
 struct althost {
   char *host;
-  unsigned short port;
+  uint16_t port;
   enum alpnid alpnid;
 };
 
@@ -39,7 +42,6 @@
   struct althost dst;
   time_t expires;
   struct Curl_llist_node node;
-  unsigned int prio;
   BIT(persist);
 };
 
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c
index d2afdbf..e4f3bfb 100644
--- a/Utilities/cmcurl/lib/amigaos.c
+++ b/Utilities/cmcurl/lib/amigaos.c
@@ -118,7 +118,7 @@
  * allocates memory also.
  */
 
-struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port)
+struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port)
 {
   struct Curl_addrinfo *ai = NULL;
   struct hostent *h;
@@ -141,7 +141,7 @@
     }
   }
   else {
-#ifdef CURLRES_THREADED
+#ifdef USE_RESOLV_THREADED
     /* gethostbyname() is not thread-safe, so we need to reopen bsdsocket
      * on the thread's context
      */
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 4d063d9..a4a7464 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -23,12 +23,12 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#ifdef CURLRES_ARES
+#ifdef USE_RESOLV_ARES
 
 /***********************************************************************
- * Only for ares-enabled builds
- * And only for functions that fulfill the asynch resolver backend API
- * as defined in asyn.h, nothing else belongs in this file!
+ * Only for ares-enabled builds and only for functions that fulfill
+ * the asynch resolver backend API as defined in asyn.h,
+ * nothing else belongs in this file!
  **********************************************************************/
 
 #ifdef HAVE_NETINET_IN_H
@@ -58,35 +58,10 @@
 #include "progress.h"
 #include "curlx/timediff.h"
 #include "httpsrr.h"
-
 #include <ares.h>
-#include <ares_version.h> /* really old c-ares did not include this by
-                             itself */
 
-#if ARES_VERSION >= 0x010704
-#define HAVE_CARES_SERVERS_CSV 1
-#define HAVE_CARES_LOCAL_DEV 1
-#define HAVE_CARES_SET_LOCAL 1
-#endif
-
-#if ARES_VERSION >= 0x010b00
-#define HAVE_CARES_PORTS_CSV 1
-#endif
-
-#if ARES_VERSION >= 0x011000
-/* 1.16.0 or later has ares_getaddrinfo */
-#define HAVE_CARES_GETADDRINFO 1
-#else
-/* How long we are willing to wait for additional parallel responses after
-   obtaining a "definitive" one. For old c-ares without getaddrinfo.
-
-   This is intended to equal the c-ares default timeout. curl always uses that
-   default value. Unfortunately, c-ares does not expose its default timeout in
-   its API, but it is officially documented as 5 seconds.
-
-   See query_completed_cb() for an explanation of how this is used.
- */
-#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
+#if ARES_VERSION < 0x011000
+#error "requires c-ares 1.16.0 or newer"
 #endif
 
 #ifdef USE_HTTPSRR
@@ -101,7 +76,13 @@
 static int ares_ver = 0;
 
 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
-                                           bool reset_on_null);
+                                           struct Curl_resolv_async *async);
+static CURLcode async_ares_set_dns_interface(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async);
+static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async);
+static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async);
 
 /*
  * Curl_async_global_init() - the generic low-level asynchronous name
@@ -142,13 +123,18 @@
   }
 }
 
-static CURLcode async_ares_init(struct Curl_easy *data)
+static CURLcode async_ares_init(struct Curl_easy *data,
+                                struct Curl_resolv_async *async)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = &async->ares;
   int status;
   struct ares_options options;
   int optmask = ARES_OPT_SOCK_STATE_CB;
-  CURLcode rc = CURLE_OK;
+  CURLcode result = CURLE_OK;
+
+  /* initial status - failed */
+  ares->ares_status = ARES_ENOTFOUND;
+  async->queries_ongoing = 0;
 
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb_data = data;
@@ -172,199 +158,168 @@
   status = ares_init_options(&ares->channel, &options, optmask);
   if(status != ARES_SUCCESS) {
     ares->channel = NULL;
-    rc = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
+    result = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
     goto out;
   }
 
-  rc = async_ares_set_dns_servers(data, FALSE);
-  if(rc && rc != CURLE_NOT_BUILT_IN)
+  result = async_ares_set_dns_servers(data, async);
+  if(result && result != CURLE_NOT_BUILT_IN)
     goto out;
 
-  rc = Curl_async_ares_set_dns_interface(data);
-  if(rc && rc != CURLE_NOT_BUILT_IN)
+  result = async_ares_set_dns_interface(data, async);
+  if(result && result != CURLE_NOT_BUILT_IN)
     goto out;
 
-  rc = Curl_async_ares_set_dns_local_ip4(data);
-  if(rc && rc != CURLE_NOT_BUILT_IN)
+  result = async_ares_set_dns_local_ip4(data, async);
+  if(result && result != CURLE_NOT_BUILT_IN)
     goto out;
 
-  rc = Curl_async_ares_set_dns_local_ip6(data);
-  if(rc && rc != CURLE_NOT_BUILT_IN)
+  result = async_ares_set_dns_local_ip6(data, async);
+  if(result && result != CURLE_NOT_BUILT_IN)
     goto out;
 
-  rc = CURLE_OK;
+  result = CURLE_OK;
 
 out:
-  if(rc && ares->channel) {
+  if(result && ares->channel) {
     ares_destroy(ares->channel);
     ares->channel = NULL;
   }
-  return rc;
-}
-
-static CURLcode async_ares_init_lazy(struct Curl_easy *data)
-{
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  if(!ares->channel)
-    return async_ares_init(data);
-  return CURLE_OK;
-}
-
-CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
-{
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  CURLcode result = CURLE_OK;
-  if(!ares->channel) {
-    result = async_ares_init(data);
-  }
-  *impl = ares->channel;
   return result;
 }
 
 /*
  * async_ares_cleanup() cleans up async resolver data.
  */
-static void async_ares_cleanup(struct Curl_easy *data)
+static void async_ares_cleanup(struct Curl_resolv_async *async)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  if(ares->temp_ai) {
-    Curl_freeaddrinfo(ares->temp_ai);
-    ares->temp_ai = NULL;
+  struct async_ares_ctx *ares = &async->ares;
+  if(ares->res_A) {
+    Curl_freeaddrinfo(ares->res_A);
+    ares->res_A = NULL;
+  }
+  if(ares->res_AAAA) {
+    Curl_freeaddrinfo(ares->res_AAAA);
+    ares->res_AAAA = NULL;
   }
 #ifdef USE_HTTPSRR
   Curl_httpsrr_cleanup(&ares->hinfo);
 #endif
 }
 
-void Curl_async_ares_shutdown(struct Curl_easy *data)
+void Curl_async_ares_shutdown(struct Curl_easy *data,
+                              struct Curl_resolv_async *async)
 {
   /* c-ares has a method to "cancel" operations on a channel, but
    * as reported in #18216, this does not totally reset the channel
    * and ares may get stuck.
    * We need to destroy the channel and on demand create a new
    * one to avoid that. */
-  Curl_async_ares_destroy(data);
+  Curl_async_ares_destroy(data, async);
 }
 
-void Curl_async_ares_destroy(struct Curl_easy *data)
+void Curl_async_ares_destroy(struct Curl_easy *data,
+                             struct Curl_resolv_async *async)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = &async->ares;
+  (void)data;
   if(ares->channel) {
     ares_destroy(ares->channel);
     ares->channel = NULL;
   }
-  async_ares_cleanup(data);
+  async_ares_cleanup(async);
 }
 
-/*
- * Curl_async_pollset() is called when someone from the outside world
- * (using curl_multi_fdset()) wants to get our fd_set setup.
- */
-
-CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
+CURLcode Curl_async_pollset(struct Curl_easy *data,
+                            struct Curl_resolv_async *async,
+                            struct easy_pollset *ps)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  if(ares->channel)
-    return Curl_ares_pollset(data, ares->channel, ps);
-  return CURLE_OK;
+  struct async_ares_ctx *ares = &async->ares;
+  CURLcode result = CURLE_OK;
+
+  if(ares->channel) {
+    result = Curl_ares_pollset(data, ares->channel, ps);
+    if(!result) {
+      timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel);
+      Curl_expire(data, ms, EXPIRE_ASYNC_NAME);
+    }
+  }
+  return result;
 }
 
 /*
- * Curl_async_is_resolved() is called repeatedly to check if a previous
+ * Curl_async_take_result() is called repeatedly to check if a previous
  * name resolve request has completed. It should also make sure to time-out if
  * the operation seems to take too long.
  *
  * Returns normal CURLcode errors.
  */
-CURLcode Curl_async_is_resolved(struct Curl_easy *data,
-                                struct Curl_dns_entry **dns)
+CURLcode Curl_async_take_result(struct Curl_easy *data,
+                                struct Curl_resolv_async *async,
+                                struct Curl_dns_entry **pdns)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = &async->ares;
   CURLcode result = CURLE_OK;
 
-  DEBUGASSERT(dns);
-  *dns = NULL;
-
-  if(data->state.async.done) {
-    *dns = data->state.async.dns;
-    return ares->result;
-  }
+  DEBUGASSERT(pdns);
+  *pdns = NULL;
+  if(!ares)
+    return CURLE_FAILED_INIT;
 
   if(Curl_ares_perform(ares->channel, 0) < 0) {
     result = CURLE_UNRECOVERABLE_POLL;
     goto out;
   }
 
-#ifndef HAVE_CARES_GETADDRINFO
-  /* Now that we have checked for any last minute results above, see if there
-     are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
-     expires. */
-  if(ares->num_pending &&
-     /* This is only set to non-zero if the timer was started. */
-     (ares->happy_eyeballs_dns_time.tv_sec ||
-      ares->happy_eyeballs_dns_time.tv_usec) &&
-     (curlx_ptimediff_ms(Curl_pgrs_now(data),
-                         &ares->happy_eyeballs_dns_time) >=
-      HAPPY_EYEBALLS_DNS_TIMEOUT)) {
-    /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
-       running. */
-    memset(&ares->happy_eyeballs_dns_time, 0,
-           sizeof(ares->happy_eyeballs_dns_time));
-
-    /* Cancel the raw c-ares request, which will fire query_completed_cb() with
-       ARES_ECANCELLED synchronously for all pending responses. This will
-       leave us with res->num_pending == 0, which is perfect for the next
-       block. */
-    ares_cancel(ares->channel);
-    DEBUGASSERT(ares->num_pending == 0);
+  if(async->queries_ongoing) {
+    result = CURLE_AGAIN;
+    goto out;
   }
-#endif
 
-  if(!ares->num_pending) {
-    /* all c-ares operations done, what is the result to report? */
-    Curl_resolv_unlink(data, &data->state.async.dns);
-    data->state.async.done = TRUE;
-    result = ares->result;
-    if(ares->ares_status == ARES_SUCCESS && !result) {
-      data->state.async.dns =
-        Curl_dnscache_mk_entry(data, &ares->temp_ai,
-                               data->state.async.hostname, 0,
-                               data->state.async.port, FALSE);
-      if(!data->state.async.dns) {
-        result = CURLE_OUT_OF_MEMORY;
-        goto out;
-      }
+  /* all c-ares operations done, what is the result to report? */
+  result = ares->result;
+  if(ares->ares_status == ARES_SUCCESS && !result) {
+    struct Curl_dns_entry *dns =
+      Curl_dnscache_mk_entry2(data, async->dns_queries,
+                             &ares->res_AAAA, &ares->res_A,
+                             async->hostname, async->port);
+    if(!dns) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
 #ifdef HTTPSRR_WORKS
-      {
+    if(async->dns_queries & CURL_DNSQ_HTTPS) {
+      if(ares->hinfo.complete) {
         struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
         if(!lhrr)
           result = CURLE_OUT_OF_MEMORY;
         else
-          data->state.async.dns->hinfo = lhrr;
+          Curl_dns_entry_set_https_rr(dns, lhrr);
       }
+      else
+        Curl_dns_entry_set_https_rr(dns, NULL);
+    }
 #endif
-      if(!result)
-        result = Curl_dnscache_add(data, data->state.async.dns);
+    if(!result) {
+      *pdns = dns;
     }
-    /* if we have not found anything, report the proper
-     * CURLE_COULDNT_RESOLVE_* code */
-    if(!result && !data->state.async.dns) {
-      const char *msg = NULL;
-      if(ares->ares_status != ARES_SUCCESS)
-        msg = ares_strerror(ares->ares_status);
-      result = Curl_resolver_error(data, msg);
-    }
-
-    if(result)
-      Curl_resolv_unlink(data, &data->state.async.dns);
-    *dns = data->state.async.dns;
-    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
-                 result, *dns ? "" : "not ");
-    async_ares_cleanup(data);
+  }
+  /* if we have not found anything, report the proper
+   * CURLE_COULDNT_RESOLVE_* code */
+  if(!result && !*pdns) {
+    const char *msg = NULL;
+    if(ares->ares_status != ARES_SUCCESS)
+      msg = ares_strerror(ares->ares_status);
+    result = Curl_async_failed(data, async, msg);
   }
 
+  CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound",
+               result, *pdns ? "" : "not ");
+  async_ares_cleanup(async);
+
 out:
-  ares->result = result;
+  if(result != CURLE_AGAIN)
+    ares->result = result;
   return result;
 }
 
@@ -375,10 +330,10 @@
   int itimeout_ms;
 
 #if TIMEDIFF_T_MAX > INT_MAX
-    itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX :
-                   ((timeout_ms < 0) ? -1 : (int)timeout_ms);
+  itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX :
+                 ((timeout_ms < 0) ? -1 : (int)timeout_ms);
 #else
-    itimeout_ms = (int)timeout_ms;
+  itimeout_ms = (int)timeout_ms;
 #endif
   max_timeout.tv_sec = itimeout_ms / 1000;
   max_timeout.tv_usec = (itimeout_ms % 1000) * 1000;
@@ -394,26 +349,91 @@
     return 1000;
 }
 
+static const struct Curl_addrinfo *async_ares_get_ai(
+  const struct Curl_addrinfo *ai,
+  int ai_family,
+  unsigned int index)
+{
+  unsigned int i = 0;
+  for(i = 0; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family) {
+      if(i == index)
+        return ai;
+      ++i;
+    }
+  }
+  return NULL;
+}
+
+const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data,
+                                              struct Curl_resolv_async *async,
+                                              int ai_family,
+                                              unsigned int index)
+{
+  struct async_ares_ctx *ares = &async->ares;
+
+  (void)data;
+  switch(ai_family) {
+  case AF_INET:
+    if(ares->res_A)
+      return async_ares_get_ai(ares->res_A, ai_family, index);
+    break;
+  case AF_INET6:
+    if(ares->res_AAAA)
+      return async_ares_get_ai(ares->res_AAAA, ai_family, index);
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *Curl_async_get_https(
+  struct Curl_easy *data,
+  struct Curl_resolv_async *async)
+{
+  if(Curl_async_knows_https(data, async))
+    return &async->ares.hinfo;
+  return NULL;
+}
+
+bool Curl_async_knows_https(struct Curl_easy *data,
+                            struct Curl_resolv_async *async)
+{
+  (void)data;
+  if(async->dns_queries & CURL_DNSQ_HTTPS)
+    return ((async->dns_responses & CURL_DNSQ_HTTPS) ||
+            !async->queries_ongoing);
+  return TRUE; /* we know it will never come */
+}
+
+#endif /* USE_HTTPSRR */
+
 /*
  * Curl_async_await()
  *
  * Waits for a resolve to finish. This function should be avoided since using
  * this risk getting the multi interface to "hang".
  *
- * 'entry' MUST be non-NULL.
+ * 'pdns' MUST be non-NULL.
  *
  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
  */
-CURLcode Curl_async_await(struct Curl_easy *data,
-                          struct Curl_dns_entry **dns)
+CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
+                          struct Curl_dns_entry **pdns)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
   struct curltime start = *Curl_pgrs_now(data);
   CURLcode result = CURLE_OK;
 
-  DEBUGASSERT(dns);
-  *dns = NULL; /* clear on entry */
+  DEBUGASSERT(pdns);
+  *pdns = NULL; /* clear on entry */
+
+  if(!ares)
+    return CURLE_FAILED_INIT;
 
   /* Wait for the name resolve query to complete or time out. */
   while(!result) {
@@ -422,8 +442,8 @@
     timeout_ms = Curl_timeleft_ms(data);
     if(!timeout_ms) { /* no applicable timeout from `data`*/
       timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start);
-      if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000))
-        timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms;
+      if(elapsed_ms < CURL_TIMEOUT_RESOLVE_MS)
+        timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms;
       else
         timeout_ms = -1;
     }
@@ -439,8 +459,10 @@
       break;
     }
 
-    result = Curl_async_is_resolved(data, dns);
-    if(result || data->state.async.done)
+    result = Curl_async_take_result(data, async, pdns);
+    if(result == CURLE_AGAIN)
+      result = CURLE_OK;
+    else if(result || *pdns)
       break;
 
     if(Curl_pgrsUpdate(data)) {
@@ -449,148 +471,17 @@
     }
   }
 
-  /* Operation complete, if the lookup was successful we now have the entry
-     in the cache. */
-  data->state.async.done = TRUE;
-  *dns = data->state.async.dns;
-
   if(result)
     ares_cancel(ares->channel);
   return result;
 }
 
-#ifndef HAVE_CARES_GETADDRINFO
-
-/* Connects results to the list */
-static void async_addr_concat(struct Curl_addrinfo **pbase,
-                              struct Curl_addrinfo *ai)
-{
-  if(!ai)
-    return;
-
-  /* When adding `ai` to an existing address list, we prefer ipv6
-   * to be in front. */
-#ifdef USE_IPV6 /* CURLRES_IPV6 */
-  if(*pbase && (*pbase)->ai_family == PF_INET6) {
-    /* ipv6 already in front, append `ai` */
-    struct Curl_addrinfo *tail = *pbase;
-    while(tail->ai_next)
-      tail = tail->ai_next;
-    tail->ai_next = ai;
-  }
-  else
-#endif /* CURLRES_IPV6 */
-  {
-    /* prepend to the (possibly) existing list. */
-    struct Curl_addrinfo *tail = ai;
-    while(tail->ai_next)
-      tail = tail->ai_next;
-    tail->ai_next = *pbase;
-    *pbase = ai;
-  }
-}
-
-/*
- * ares_query_completed_cb() is the callback that ares will call when
- * the host query initiated by ares_gethostbyname() from
- * Curl_async_getaddrinfo(), when using ares, is completed either
- * successfully or with failure.
- */
-static void async_ares_hostbyname_cb(void *user_data,
-                                     int status,
-                                     int timeouts,
-                                     struct hostent *hostent)
-{
-  struct Curl_easy *data = (struct Curl_easy *)user_data;
-  struct async_ares_ctx *ares = &data->state.async.ares;
-
-  (void)timeouts; /* ignored */
-
-  if(ARES_EDESTRUCTION == status)
-    return;
-
-  if(ARES_SUCCESS == status) {
-    ares->ares_status = status; /* one success overrules any error */
-    async_addr_concat(&ares->temp_ai,
-                      Curl_he2ai(hostent, data->state.async.port));
-  }
-  else if(ares->ares_status != ARES_SUCCESS) {
-    /* no success so far, remember last error */
-    ares->ares_status = status;
-  }
-
-  ares->num_pending--;
-
-  CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, "
-               "addr=%sfound",
-               status, ares->num_pending, ares->temp_ai ? "" : "not ");
-  /* If there are responses still pending, we presume they must be the
-     complementary IPv4 or IPv6 lookups that we started in parallel in
-     Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a
-     "definitive" response from one of a set of parallel queries, we need to
-     think about how long we are willing to wait for more responses. */
-  if(ares->num_pending
-     /* Only these c-ares status values count as "definitive" for these
-        purposes. For example, ARES_ENODATA is what we expect when there is
-        no IPv6 entry for a domain name, and that is not a reason to get more
-        aggressive in our timeouts for the other response. Other errors are
-        either a result of bad input (which should affect all parallel
-        requests), local or network conditions, non-definitive server
-        responses, or us cancelling the request. */
-     && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
-    /* Right now, there can only be up to two parallel queries, so do not
-       bother handling any other cases. */
-    DEBUGASSERT(ares->num_pending == 1);
-
-    /* it is possible that one of these parallel queries could succeed
-       quickly, but the other could always fail or timeout (when we are
-       talking to a pool of DNS servers that can only successfully resolve
-       IPv4 address, for example).
-
-       it is also possible that the other request could always take longer
-       because it needs more time or only the second DNS server can fulfill it
-       successfully. But, to align with the philosophy of Happy Eyeballs, we
-       do not want to wait _too_ long or users will think requests are slow
-       when IPv6 lookups do not actually work (but IPv4 ones do).
-
-       So, now that we have a usable answer (some IPv4 addresses, some IPv6
-       addresses, or "no such domain"), we start a timeout for the remaining
-       pending responses. Even though it is typical that this resolved
-       request came back quickly, that need not be the case. It might be that
-       this completing request did not get a result from the first DNS
-       server or even the first round of the whole DNS server pool. So it
-       could already be a long time after we issued the DNS queries in
-       the first place. Without modifying c-ares, we cannot know exactly
-       where in its retry cycle we are. We could guess based on how much
-       time has gone by, but it does not really matter. Happy Eyeballs tells
-       us that, given usable information in hand, we do not want to
-       wait "too much longer" after we get a result.
-
-       We wait an additional amount of time equal to the default c-ares
-       query timeout. That is enough time for a typical parallel response to
-       arrive without being "too long". Even on a network where one of the two
-       types of queries is failing or timing out constantly, this will usually
-       mean we wait a total of the default c-ares timeout (5 seconds) plus the
-       round trip time for the successful request, which seems bearable. The
-       downside is that c-ares might race with us to issue one more retry
-       before we give up, but it seems better to "waste" that request instead
-       of trying to guess the perfect timeout to prevent it. After all, we do
-       not even know where in the c-ares retry cycle each request is.
-    */
-    ares->happy_eyeballs_dns_time = *Curl_pgrs_now(data);
-    Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS);
-  }
-}
-
-#else
-/* c-ares 1.16.0 or later */
-
 /*
  * async_ares_node2addr() converts an address list provided by c-ares
  * to an internal libcurl compatible list.
  */
-static struct Curl_addrinfo *
-async_ares_node2addr(struct ares_addrinfo_node *node)
+static struct Curl_addrinfo *async_ares_node2addr(
+  struct ares_addrinfo_node *node)
 {
   /* traverse the ares_addrinfo_node list */
   struct ares_addrinfo_node *ai;
@@ -660,46 +551,70 @@
   return cafirst;
 }
 
-static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
-                                   struct ares_addrinfo *ares_ai)
+static void async_ares_A_cb(void *user_data, int status, int timeouts,
+                            struct ares_addrinfo *ares_ai)
 {
-  struct Curl_easy *data = (struct Curl_easy *)user_data;
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct Curl_resolv_async *async = user_data;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
+
   (void)timeouts;
-  if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
-    ares->ares_status = status;
+  if(!async)
+    return;
+
+  async->dns_responses |= CURL_DNSQ_A;
+  async->queries_ongoing--;
+  async->done = !async->queries_ongoing;
   if(status == ARES_SUCCESS) {
-    ares->temp_ai = async_ares_node2addr(ares_ai->nodes);
+    ares->ares_status = ARES_SUCCESS;
+    ares->res_A = async_ares_node2addr(ares_ai->nodes);
     ares_freeaddrinfo(ares_ai);
   }
-  ares->num_pending--;
-  CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, "
-               "overall status=%d, pending=%d, addr=%sfound",
-               status, ares->ares_status, ares->num_pending,
-               ares->temp_ai ? "" : "not ");
+  else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
+    ares->ares_status = status;
 }
 
-#endif
+#ifdef CURLRES_IPV6
+static void async_ares_AAAA_cb(void *user_data, int status, int timeouts,
+                               struct ares_addrinfo *ares_ai)
+{
+  struct Curl_resolv_async *async = user_data;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
+
+  (void)timeouts;
+  if(!async)
+    return;
+
+  async->dns_responses |= CURL_DNSQ_AAAA;
+  async->queries_ongoing--;
+  async->done = !async->queries_ongoing;
+  if(status == ARES_SUCCESS) {
+    ares->ares_status = ARES_SUCCESS;
+    ares->res_AAAA = async_ares_node2addr(ares_ai->nodes);
+    ares_freeaddrinfo(ares_ai);
+  }
+  else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
+    ares->ares_status = status;
+}
+#endif /* CURLRES_IPV6 */
 
 #ifdef USE_HTTPSRR
 static void async_ares_rr_done(void *user_data, ares_status_t status,
                                size_t timeouts,
                                const ares_dns_record_t *dnsrec)
 {
-  struct Curl_easy *data = user_data;
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct Curl_resolv_async *async = user_data;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
+
+  if(!async)
+    return;
 
   (void)timeouts;
-  --ares->num_pending;
-  CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
-               "dnsres=%sfound",
-               status, ares->num_pending,
-               (dnsrec &&
-                ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
-                "" : "not ");
+  async->dns_responses |= CURL_DNSQ_HTTPS;
+  async->queries_ongoing--;
+  async->done = !async->queries_ongoing;
   if((ARES_SUCCESS != status) || !dnsrec)
     return;
-  ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
+  ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo);
 }
 #endif /* USE_HTTPSRR */
 
@@ -708,115 +623,92 @@
  *
  * Starts a name resolve for the given hostname and port number.
  */
-CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
-                                int port, int ip_version)
+CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
+                                struct Curl_resolv_async *async)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
-#ifdef USE_HTTPSRR
-  char *rrname = NULL;
-#endif
+  struct async_ares_ctx *ares = &async->ares;
+  char service[12];
+  int socktype;
+  CURLcode result = CURLE_OK;
 
-  if(async_ares_init_lazy(data))
-    return CURLE_FAILED_INIT;
-
-  data->state.async.done = FALSE;   /* not done */
-  data->state.async.dns = NULL;     /* clear */
-  data->state.async.port = port;
-  data->state.async.ip_version = ip_version;
-  data->state.async.hostname = curlx_strdup(hostname);
-  if(!data->state.async.hostname)
-    return CURLE_OUT_OF_MEMORY;
-#ifdef USE_HTTPSRR
-  if(port != 443) {
-    rrname = curl_maprintf("_%d_.https.%s", port, hostname);
-    if(!rrname)
-      return CURLE_OUT_OF_MEMORY;
+  if(ares->channel) {
+    DEBUGASSERT(0);
+    result = CURLE_FAILED_INIT;
+    goto out;
   }
-#endif
 
-  /* initial status - failed */
-  ares->ares_status = ARES_ENOTFOUND;
-  ares->result = CURLE_OK;
+  result = async_ares_init(data, async);
+  if(result)
+    goto out;
+
+  result = Curl_resolv_announce_start(data, ares->channel);
+  if(result)
+    goto out;
 
 #if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */
   if(CURL_TRC_DNS_is_verbose(data)) {
     char *csv = ares_get_servers_csv(ares->channel);
-    CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv);
+    CURL_TRC_DNS(data, "ares: servers=%s", csv);
     ares_free_string(csv);
   }
 #endif
 
-#ifdef HAVE_CARES_GETADDRINFO
-  {
-    struct ares_addrinfo_hints hints;
-    char service[12];
-    int pf = PF_INET;
-    memset(&hints, 0, sizeof(hints));
+  curl_msnprintf(service, sizeof(service), "%d", async->port);
+  socktype =
+    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
+    SOCK_STREAM : SOCK_DGRAM;
+
 #ifdef CURLRES_IPV6
-    if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
-      /* The stack seems to be IPv6-enabled */
-      if(ip_version == CURL_IPRESOLVE_V6)
-        pf = PF_INET6;
-      else
-        pf = PF_UNSPEC;
-    }
-#endif /* CURLRES_IPV6 */
-    CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
-                 (pf == PF_UNSPEC) ? "A+AAAA" :
-                 ((pf == PF_INET) ? "A" : "AAAA"));
-    hints.ai_family = pf;
-    hints.ai_socktype =
-      (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
-      SOCK_STREAM : SOCK_DGRAM;
-    /* Since the service is a numerical one, set the hint flags
-     * accordingly to save a call to getservbyname in inside C-Ares
-     */
+  if(async->dns_queries & CURL_DNSQ_AAAA) {
+    struct ares_addrinfo_hints hints;
+
+    memset(&hints, 0, sizeof(hints));
+    CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname);
+    hints.ai_family = PF_INET6;
+    hints.ai_socktype = socktype;
     hints.ai_flags = ARES_AI_NUMERICSERV;
-    curl_msnprintf(service, sizeof(service), "%d", port);
-    ares->num_pending = 1;
-    ares_getaddrinfo(ares->channel, data->state.async.hostname,
-                     service, &hints, async_ares_addrinfo_cb, data);
+    async->queries_ongoing++;
+    ares_getaddrinfo(ares->channel, async->hostname,
+                     service, &hints, async_ares_AAAA_cb, async);
   }
-#else
+#endif /* CURLRES_IPV6 */
 
-#if ARES_VERSION >= 0x010601  /* IPv6 supported since 1.6.1 */
-  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
-    /* The stack seems to be IPv6-enabled */
-    /* areschannel is already setup in the Curl_open() function */
-    CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
-    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
-                       async_ares_hostbyname_cb, data);
-    CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
-    ares->num_pending = 2;
-    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
-                       async_ares_hostbyname_cb, data);
+  if(async->dns_queries & CURL_DNSQ_A) {
+    struct ares_addrinfo_hints hints;
+
+    memset(&hints, 0, sizeof(hints));
+    CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname);
+    hints.ai_family = PF_INET;
+    hints.ai_socktype = socktype;
+    hints.ai_flags = ARES_AI_NUMERICSERV;
+    async->queries_ongoing++;
+    ares_getaddrinfo(ares->channel, async->hostname,
+                     service, &hints, async_ares_A_cb, async);
   }
-  else
-#endif
-  {
-    /* areschannel is already setup in the Curl_open() function */
-    CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
-    ares->num_pending = 1;
-    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
-                       async_ares_hostbyname_cb, data);
-  }
-#endif
+
 #ifdef USE_HTTPSRR
-  {
-    CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s",
-                 rrname ? rrname : data->state.async.hostname);
-    memset(&ares->hinfo, 0, sizeof(ares->hinfo));
-    ares->hinfo.port = -1;
+  memset(&ares->hinfo, 0, sizeof(ares->hinfo));
+  if(async->dns_queries & CURL_DNSQ_HTTPS) {
+    char *rrname = NULL;
+    if(async->port != 443) {
+      rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
+      if(!rrname)
+        return CURLE_OUT_OF_MEMORY;
+    }
+    CURL_TRC_DNS(data, "ares: query HTTPS records for %s",
+                 rrname ? rrname : async->hostname);
     ares->hinfo.rrname = rrname;
-    ares->num_pending++; /* one more */
+    async->queries_ongoing++;
     ares_query_dnsrec(ares->channel,
-                      rrname ? rrname : data->state.async.hostname,
+                      rrname ? rrname : async->hostname,
                       ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
-                      async_ares_rr_done, data, NULL);
+                      async_ares_rr_done, async, NULL);
   }
-#endif
+#endif /* USE_HTTPSRR */
 
-  return CURLE_OK;
+out:
+  ares->result = result;
+  return result ? result : (async->queries_ongoing ? CURLE_AGAIN : CURLE_OK);
 }
 
 /* Set what DNS server are is to use. This is called in 2 situations:
@@ -826,33 +718,24 @@
  * 2. When we lazy init the ares channel and NULL means that there
  *    are no preferences and we do not reset any existing channel. */
 static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
-                                           bool reset_on_null)
+                                           struct Curl_resolv_async *async)
 {
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
   CURLcode result = CURLE_NOT_BUILT_IN;
   const char *servers = data->set.str[STRING_DNS_SERVERS];
   int ares_result = ARES_SUCCESS;
 
-#if defined(DEBUGBUILD) && defined(HAVE_CARES_SERVERS_CSV)
+#ifdef DEBUGBUILD
   if(getenv("CURL_DNS_SERVER"))
     servers = getenv("CURL_DNS_SERVER");
 #endif
 
-  if(!servers) {
-    if(reset_on_null) {
-      Curl_async_destroy(data);
-    }
+  if(!servers)
     return CURLE_OK;
-  }
 
-#ifdef HAVE_CARES_SERVERS_CSV
   /* if channel is not there, this is a parameter check */
-  if(ares->channel)
-#ifdef HAVE_CARES_PORTS_CSV
+  if(ares && ares->channel)
     ares_result = ares_set_servers_ports_csv(ares->channel, servers);
-#else
-    ares_result = ares_set_servers_csv(ares->channel, servers);
-#endif
   switch(ares_result) {
   case ARES_SUCCESS:
     result = CURLE_OK;
@@ -868,43 +751,29 @@
     result = CURLE_BAD_FUNCTION_ARGUMENT;
     break;
   }
-#else /* c-ares version too old! */
-  (void)data;
-  (void)(ares_result);
-#endif
   return result;
 }
 
-CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
+static CURLcode async_ares_set_dns_interface(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async)
 {
-  return async_ares_set_dns_servers(data, TRUE);
-}
-
-CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
-{
-#ifdef HAVE_CARES_LOCAL_DEV
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
   const char *interf = data->set.str[STRING_DNS_INTERFACE];
 
   if(!interf)
     interf = "";
 
   /* if channel is not there, this is a parameter check */
-  if(ares->channel)
+  if(ares && ares->channel)
     ares_set_local_dev(ares->channel, interf);
 
   return CURLE_OK;
-#else /* c-ares version too old! */
-  (void)data;
-  (void)interf;
-  return CURLE_NOT_BUILT_IN;
-#endif
 }
 
-CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
+static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async)
 {
-#ifdef HAVE_CARES_SET_LOCAL
-  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
   struct in_addr a4;
   const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
 
@@ -919,21 +788,17 @@
   }
 
   /* if channel is not there yet, this is a parameter check */
-  if(ares->channel)
+  if(ares && ares->channel)
     ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
 
   return CURLE_OK;
-#else /* c-ares version too old! */
-  (void)data;
-  (void)local_ip4;
-  return CURLE_NOT_BUILT_IN;
-#endif
 }
 
-CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
+static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data,
+                                             struct Curl_resolv_async *async)
 {
-#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
-  struct async_ares_ctx *ares = &data->state.async.ares;
+#ifdef USE_IPV6
+  struct async_ares_ctx *ares = async ? &async->ares : NULL;
   unsigned char a6[INET6_ADDRSTRLEN];
   const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
 
@@ -949,14 +814,15 @@
   }
 
   /* if channel is not there, this is a parameter check */
-  if(ares->channel)
+  if(ares && ares->channel)
     ares_set_local_ip6(ares->channel, a6);
 
   return CURLE_OK;
-#else /* c-ares version too old! */
+#else /* no IPv6 support */
   (void)data;
+  (void)async;
   return CURLE_NOT_BUILT_IN;
 #endif
 }
 
-#endif /* CURLRES_ARES */
+#endif /* USE_RESOLV_ARES */
diff --git a/Utilities/cmcurl/lib/asyn-base.c b/Utilities/cmcurl/lib/asyn-base.c
index 44bf98c..62cb0ef 100644
--- a/Utilities/cmcurl/lib/asyn-base.c
+++ b/Utilities/cmcurl/lib/asyn-base.c
@@ -39,12 +39,14 @@
 
 #ifdef USE_ARES
 #include <ares.h>
-#include <ares_version.h> /* really old c-ares did not include it by itself */
 #endif
 
 #include "urldata.h"
+#include "connect.h"
+#include "curl_trc.h"
 #include "hostip.h"
 #include "multiif.h"
+#include "progress.h"
 #include "select.h"
 #include "url.h"
 
@@ -53,10 +55,21 @@
  **********************************************************************/
 #ifdef CURLRES_ASYNCH
 
+timediff_t Curl_async_timeleft_ms(struct Curl_easy *data,
+                                  struct Curl_resolv_async *async)
+{
+  if(async->timeout_ms) {
+    timediff_t elapsed_ms =
+      curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
+    return async->timeout_ms - elapsed_ms;
+  }
+  return Curl_timeleft_ms(data);
+}
+
 #ifdef USE_ARES
 
-#if ARES_VERSION < 0x010600
-#error "requires c-ares 1.6.0 or newer"
+#if ARES_VERSION < 0x011000
+#error "requires c-ares 1.16.0 or newer"
 #endif
 
 /*
@@ -71,12 +84,8 @@
                            ares_channel channel,
                            struct easy_pollset *ps)
 {
-  struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
-  struct timeval timebuf;
   curl_socket_t sockets[16];  /* ARES documented limit */
   unsigned int bitmap, i;
-  struct timeval *timeout;
-  timediff_t milli;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(channel);
@@ -97,15 +106,33 @@
     if(result)
       return result;
   }
-
-  timeout = ares_timeout(channel, &maxtime, &timebuf);
-  if(!timeout)
-    timeout = &maxtime;
-  milli = curlx_tvtoms(timeout);
-  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
   return result;
 }
 
+timediff_t Curl_ares_timeout_ms(struct Curl_easy *data,
+                                struct Curl_resolv_async *async,
+                                ares_channel channel)
+{
+  timediff_t async_timeout_ms;
+
+  DEBUGASSERT(channel);
+  if(!channel)
+    return -1;
+
+  async_timeout_ms = Curl_async_timeleft_ms(data, async);
+  if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) {
+    struct timeval timebuf;
+    struct timeval *timeout;
+    struct timeval end = { (int)async_timeout_ms / 1000,
+                           ((int)async_timeout_ms % 1000) * 1000 };
+
+    timeout = ares_timeout(channel, &end, &timebuf);
+    if(timeout)
+      return curlx_tvtoms(timeout);
+  }
+  return async_timeout_ms;
+}
+
 /*
  * Curl_ares_perform()
  *
@@ -178,32 +205,61 @@
 
 #include "doh.h"
 
-void Curl_async_shutdown(struct Curl_easy *data)
+void Curl_async_shutdown(struct Curl_easy *data,
+                         struct Curl_resolv_async *async)
 {
-#ifdef CURLRES_ARES
-  Curl_async_ares_shutdown(data);
+  if(async) {
+    CURL_TRC_DNS(data, "[%u] shutdown async", async->id);
+    async->shutdown = TRUE;
+#ifdef USE_RESOLV_ARES
+    Curl_async_ares_shutdown(data, async);
 #endif
-#ifdef CURLRES_THREADED
-  Curl_async_thrdd_shutdown(data);
+#ifdef USE_RESOLV_THREADED
+    Curl_async_thrdd_shutdown(data, async);
 #endif
 #ifndef CURL_DISABLE_DOH
-  Curl_doh_cleanup(data);
+    Curl_doh_cleanup(data, async);
 #endif
-  Curl_safefree(data->state.async.hostname);
+  }
 }
 
-void Curl_async_destroy(struct Curl_easy *data)
+void Curl_async_destroy(struct Curl_easy *data,
+                        struct Curl_resolv_async *async)
 {
-#ifdef CURLRES_ARES
-  Curl_async_ares_destroy(data);
+  if(async) {
+    CURL_TRC_DNS(data, "[%u] destroy async", async->id);
+    async->shutdown = TRUE;
+#ifdef USE_RESOLV_ARES
+    Curl_async_ares_destroy(data, async);
 #endif
-#ifdef CURLRES_THREADED
-  Curl_async_thrdd_destroy(data);
+#ifdef USE_RESOLV_THREADED
+    Curl_async_thrdd_destroy(data, async);
 #endif
 #ifndef CURL_DISABLE_DOH
-  Curl_doh_cleanup(data);
+    Curl_doh_cleanup(data, async);
 #endif
-  Curl_safefree(data->state.async.hostname);
+    curlx_safefree(async);
+  }
+}
+
+CURLcode Curl_async_failed(struct Curl_easy *data,
+                           struct Curl_resolv_async *async,
+                           const char *detail)
+{
+  const char *host_or_proxy = "host";
+  CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
+
+#ifndef CURL_DISABLE_PROXY
+  if(async->for_proxy) {
+    host_or_proxy = "proxy";
+    result = CURLE_COULDNT_RESOLVE_PROXY;
+  }
+#endif
+
+  failf(data, "Could not resolve %s: %s%s%s%s",
+        host_or_proxy, async->hostname,
+        detail ? " (" : "", detail ? detail : "", detail ? ")" : "");
+  return result;
 }
 
 #endif /* USE_CURL_ASYNC */
diff --git a/Utilities/cmcurl/lib/asyn-thrdd.c b/Utilities/cmcurl/lib/asyn-thrdd.c
index c3ed8ea..90f055c 100644
--- a/Utilities/cmcurl/lib/asyn-thrdd.c
+++ b/Utilities/cmcurl/lib/asyn-thrdd.c
@@ -26,9 +26,7 @@
 /***********************************************************************
  * Only for threaded name resolves builds
  **********************************************************************/
-#ifdef CURLRES_THREADED
-
-#include "socketpair.h"
+#ifdef USE_RESOLV_THREADED
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -44,10 +42,6 @@
 #include <inet.h>
 #endif
 
-#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-#include <pthread.h>
-#endif
-
 #ifdef HAVE_GETADDRINFO
 #define RESOLVER_ENOMEM  EAI_MEMORY  /* = WSA_NOT_ENOUGH_MEMORY on Windows */
 #else
@@ -64,7 +58,11 @@
 #include "multiif.h"
 #include "curl_threads.h"
 #include "progress.h"
+#include "rand.h"
 #include "select.h"
+#include "thrdqueue.h"
+#include "curlx/strparse.h"
+#include "curlx/wait.h"
 
 #ifdef USE_ARES
 #include <ares.h>
@@ -101,229 +99,93 @@
 #endif
 }
 
-static void async_thrdd_destroy(struct Curl_easy *data);
-static void async_thrdd_shutdown(struct Curl_easy *data);
+#ifdef CURLVERBOSE
+#define CURL_ASYN_ITEM_DESC_LEN   64
+#define async_item_description(x)   (x)->description
+#else
+#define async_item_description(x)   NULL
+#endif
 
-CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
-{
-  (void)data;
-  *impl = NULL;
-  return CURLE_OK;
-}
+struct async_thrdd_item {
+  struct Curl_addrinfo *res;
+#ifdef CURLVERBOSE
+  char description[CURL_ASYN_ITEM_DESC_LEN];
+#endif
+  int sock_error;
+  uint32_t mid;
+  uint32_t resolv_id;
+  uint16_t port;
+  uint8_t transport;
+  uint8_t dns_queries;
+#ifdef DEBUGBUILD
+  uint32_t delay_ms;
+  uint32_t delay_fail_ms;
+#endif
+  char hostname[1];
+};
 
 /* Give up reference to add_ctx */
-static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
-                            struct Curl_easy *data)
+static void async_thrdd_item_destroy(struct async_thrdd_item *item)
 {
-  struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
-  bool destroy;
-
-  if(!addr_ctx)
-    return;
-
-  Curl_mutex_acquire(&addr_ctx->mutx);
-  if(!data)  /* called by resolving thread */
-    addr_ctx->thrd_done = TRUE;
-
-  DEBUGASSERT(addr_ctx->ref_count);
-  --addr_ctx->ref_count;
-  destroy = !addr_ctx->ref_count;
-  Curl_mutex_release(&addr_ctx->mutx);
-
-  if(destroy) {
-    Curl_mutex_destroy(&addr_ctx->mutx);
-    curlx_free(addr_ctx->hostname);
-    if(addr_ctx->res)
-      Curl_freeaddrinfo(addr_ctx->res);
-    Curl_wakeup_destroy(addr_ctx->sock_pair);
-    curlx_free(addr_ctx);
+  if(item) {
+    if(item->res)
+      Curl_freeaddrinfo(item->res);
+    curlx_free(item);
   }
-  *paddr_ctx = NULL;
 }
 
 /* Initialize context for threaded resolver */
-static struct async_thrdd_addr_ctx *
-addr_ctx_create(struct Curl_easy *data,
-                const char *hostname, int port,
-                const struct addrinfo *hints)
+static struct async_thrdd_item *async_thrdd_item_create(
+  struct Curl_easy *data,
+  uint32_t resolv_id, uint8_t dns_queries,
+  const char *hostname, uint16_t port,
+  uint8_t transport)
 {
-  struct async_thrdd_addr_ctx *addr_ctx = curlx_calloc(1, sizeof(*addr_ctx));
-  if(!addr_ctx)
+  size_t hostlen = strlen(hostname);
+  struct async_thrdd_item *item;
+
+  item = curlx_calloc(1, sizeof(*item) + hostlen);
+  if(!item)
     return NULL;
 
-  addr_ctx->thread_hnd = curl_thread_t_null;
-  addr_ctx->port = port;
-  addr_ctx->ref_count = 1;
+  if(hostlen) /* NUL byte of name already in struct size */
+    memcpy(item->hostname, hostname, hostlen);
+  item->mid = data->mid;
+  item->resolv_id = resolv_id;
+  item->dns_queries = dns_queries;
+  item->port = port;
+  item->transport = transport;
 
-#ifdef HAVE_GETADDRINFO
-  DEBUGASSERT(hints);
-  addr_ctx->hints = *hints;
-#else
-  (void)hints;
+#ifdef CURLVERBOSE
+  curl_msnprintf(item->description, sizeof(item->description),
+                 "[%" FMT_OFF_T "/%u] %s %s:%u",
+                 data->id, item->resolv_id,
+                 Curl_resolv_query_str(dns_queries),
+                 item->hostname, item->port);
 #endif
 
-  Curl_mutex_init(&addr_ctx->mutx);
-
-#ifndef CURL_DISABLE_SOCKETPAIR
-  /* create socket pair or pipe */
-  if(Curl_wakeup_init(addr_ctx->sock_pair, FALSE) < 0) {
-    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
-    addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
-    goto err_exit;
-  }
-#endif
-  addr_ctx->sock_error = 0;
-
-  /* Copying hostname string because original can be destroyed by parent
-   * thread during gethostbyname execution.
-   */
-  addr_ctx->hostname = curlx_strdup(hostname);
-  if(!addr_ctx->hostname)
-    goto err_exit;
-
-  return addr_ctx;
-
-err_exit:
-  addr_ctx_unlink(&addr_ctx, data);
-  return NULL;
-}
-
-#ifdef HAVE_GETADDRINFO
-
-/*
- * getaddrinfo_thread() resolves a name and then exits.
- *
- * For builds without ARES, but with USE_IPV6, create a resolver thread
- * and wait on it.
- */
-static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
-{
-  struct async_thrdd_addr_ctx *addr_ctx = arg;
-  curl_bit do_abort;
-
-  Curl_mutex_acquire(&addr_ctx->mutx);
-  do_abort = addr_ctx->do_abort;
-  Curl_mutex_release(&addr_ctx->mutx);
-
-  if(!do_abort) {
-    char service[12];
-    int rc;
-
-    curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port);
-
-    rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
-                             &addr_ctx->hints, &addr_ctx->res);
-
-    if(rc) {
-      addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
-      if(addr_ctx->sock_error == 0)
-        addr_ctx->sock_error = RESOLVER_ENOMEM;
-    }
-    else {
-      Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
-    }
-
-    Curl_mutex_acquire(&addr_ctx->mutx);
-    do_abort = addr_ctx->do_abort;
-    Curl_mutex_release(&addr_ctx->mutx);
-#ifndef CURL_DISABLE_SOCKETPAIR
-    if(!do_abort) {
-      /* Thread is done, notify transfer */
-      int err = Curl_wakeup_signal(addr_ctx->sock_pair);
-      if(err) {
-        /* update sock_error to errno */
-        addr_ctx->sock_error = err;
+#ifdef DEBUGBUILD
+  {
+    const char *p = getenv("CURL_DBG_RESOLV_DELAY");
+    if(p) {
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
+        item->delay_ms = (uint32_t)l;
       }
     }
-#endif
-  }
-
-  addr_ctx_unlink(&addr_ctx, NULL);
-  return 0;
-}
-
-#else /* HAVE_GETADDRINFO */
-
-/*
- * gethostbyname_thread() resolves a name and then exits.
- */
-static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
-{
-  struct async_thrdd_addr_ctx *addr_ctx = arg;
-  bool do_abort;
-
-  Curl_mutex_acquire(&addr_ctx->mutx);
-  do_abort = addr_ctx->do_abort;
-  Curl_mutex_release(&addr_ctx->mutx);
-
-  if(!do_abort) {
-    addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
-    if(!addr_ctx->res) {
-      addr_ctx->sock_error = SOCKERRNO;
-      if(addr_ctx->sock_error == 0)
-        addr_ctx->sock_error = RESOLVER_ENOMEM;
-    }
-
-    Curl_mutex_acquire(&addr_ctx->mutx);
-    do_abort = addr_ctx->do_abort;
-    Curl_mutex_release(&addr_ctx->mutx);
-#ifndef CURL_DISABLE_SOCKETPAIR
-    if(!do_abort) {
-      int err = Curl_wakeup_signal(addr_ctx->sock_pair);
-      if(err) {
-        /* update sock_error to errno */
-        addr_ctx->sock_error = err;
+    p = getenv("CURL_DBG_RESOLV_FAIL_DELAY");
+    if(p) {
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
+        unsigned char c = 0;
+        Curl_rand_bytes(data, FALSE, &c, 1);
+        item->delay_fail_ms = (uint32_t)l + c;
       }
     }
-#endif
   }
-
-  addr_ctx_unlink(&addr_ctx, NULL);
-  return 0;
-}
-
-#endif /* HAVE_GETADDRINFO */
-
-/*
- * async_thrdd_destroy() cleans up async resolver data and thread handle.
- */
-static void async_thrdd_destroy(struct Curl_easy *data)
-{
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  struct async_thrdd_addr_ctx *addr = thrdd->addr;
-
-#ifdef USE_HTTPSRR_ARES
-  if(thrdd->rr.channel) {
-    ares_destroy(thrdd->rr.channel);
-    thrdd->rr.channel = NULL;
-  }
-  Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
 #endif
 
-  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
-    curl_bit done;
-
-    Curl_mutex_acquire(&addr->mutx);
-#ifndef CURL_DISABLE_SOCKETPAIR
-    if(!addr->do_abort)
-      Curl_multi_will_close(data, addr->sock_pair[0]);
-#endif
-    addr->do_abort = TRUE;
-    done = addr->thrd_done;
-    Curl_mutex_release(&addr->mutx);
-
-    if(done) {
-      Curl_thread_join(&addr->thread_hnd);
-      CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
-    }
-    else {
-      /* thread is still running. Detach it. */
-      Curl_thread_destroy(&addr->thread_hnd);
-      CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
-    }
-  }
-  /* release our reference to the shared context */
-  addr_ctx_unlink(&thrdd->addr, data);
+  return item;
 }
 
 #ifdef USE_HTTPSRR_ARES
@@ -332,25 +194,30 @@
                                 size_t timeouts,
                                 const ares_dns_record_t *dnsrec)
 {
-  struct Curl_easy *data = user_data;
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  struct Curl_resolv_async *async = user_data;
+  struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
 
   (void)timeouts;
-  thrdd->rr.done = TRUE;
-  if((ARES_SUCCESS != status) || !dnsrec)
+  if(!thrdd)
     return;
-  thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
+
+  async->dns_responses |= CURL_DNSQ_HTTPS;
+  async->queries_ongoing--;
+  async->done = !async->queries_ongoing;
+  if((ARES_SUCCESS == status) && dnsrec)
+    async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo);
 }
 
-static CURLcode async_rr_start(struct Curl_easy *data, int port)
+static CURLcode async_rr_start(struct Curl_easy *data,
+                               struct Curl_resolv_async *async)
 {
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  struct async_thrdd_ctx *thrdd = &async->thrdd;
   int status;
   char *rrname = NULL;
 
   DEBUGASSERT(!thrdd->rr.channel);
-  if(port != 443) {
-    rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name);
+  if(async->port != 443) {
+    rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname);
     if(!rrname)
       return CURLE_OUT_OF_MEMORY;
   }
@@ -372,327 +239,82 @@
 #endif
 
   memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
-  thrdd->rr.hinfo.port = -1;
   thrdd->rr.hinfo.rrname = rrname;
+  async->queries_ongoing++;
   ares_query_dnsrec(thrdd->rr.channel,
-                    rrname ? rrname : data->conn->host.name, ARES_CLASS_IN,
+                    rrname ? rrname : async->hostname, ARES_CLASS_IN,
                     ARES_REC_TYPE_HTTPS,
-                    async_thrdd_rr_done, data, NULL);
-  CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
+                    async_thrdd_rr_done, async, NULL);
+  CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s",
+               rrname ? rrname : async->hostname);
   return CURLE_OK;
 }
 #endif
 
-/*
- * async_thrdd_init() starts a new thread that performs the actual
- * resolve. This function returns before the resolve is done.
- *
- * Returns FALSE in case of failure, otherwise TRUE.
- */
-static bool async_thrdd_init(struct Curl_easy *data,
-                             const char *hostname, int port, int ip_version,
-                             const struct addrinfo *hints)
+void Curl_async_thrdd_shutdown(struct Curl_easy *data,
+                               struct Curl_resolv_async *async)
 {
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  struct async_thrdd_addr_ctx *addr_ctx;
+  Curl_async_thrdd_destroy(data, async);
+}
 
-  /* !checksrc! disable ERRNOVAR 1 */
-  int err = ENOMEM;
+struct async_thrdd_match_ctx {
+  uint32_t mid;
+  uint32_t resolv_id;
+};
 
-  if(thrdd->addr
+static bool async_thrdd_match_item(void *qitem, void *match_data)
+{
+  const struct async_thrdd_match_ctx *ctx = match_data;
+  struct async_thrdd_item *item = qitem;
+  return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id);
+}
+
+void Curl_async_thrdd_destroy(struct Curl_easy *data,
+                              struct Curl_resolv_async *async)
+{
+  (void)data;
+  if(async->queries_ongoing && !async->done &&
+     data->multi && data->multi->resolv_thrdq) {
+    /* Remove any resolve items still queued */
+    struct async_thrdd_match_ctx mctx;
+    mctx.mid = data->mid;
+    mctx.resolv_id = async->id;
+    Curl_thrdq_clear(data->multi->resolv_thrdq,
+                     async_thrdd_match_item, &mctx);
+  }
 #ifdef USE_HTTPSRR_ARES
-     || thrdd->rr.channel
-#endif
-     ) {
-    CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
-    async_thrdd_destroy(data);
-    DEBUGASSERT(!thrdd->addr);
-#ifdef USE_HTTPSRR_ARES
-    DEBUGASSERT(!thrdd->rr.channel);
-#endif
+  if(async->thrdd.rr.channel) {
+    ares_destroy(async->thrdd.rr.channel);
+    async->thrdd.rr.channel = NULL;
   }
-
-  data->state.async.dns = NULL;
-  data->state.async.done = FALSE;
-  data->state.async.port = port;
-  data->state.async.ip_version = ip_version;
-  curlx_free(data->state.async.hostname);
-  data->state.async.hostname = curlx_strdup(hostname);
-  if(!data->state.async.hostname)
-    goto err_exit;
-
-  addr_ctx = addr_ctx_create(data, hostname, port, hints);
-  if(!addr_ctx)
-    goto err_exit;
-  thrdd->addr = addr_ctx;
-
-  /* passing addr_ctx to the thread adds a reference */
-  addr_ctx->ref_count = 2;
-  addr_ctx->start = *Curl_pgrs_now(data);
-
-#ifdef HAVE_GETADDRINFO
-  addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
-#else
-  addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
+  Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo);
 #endif
-
-  if(addr_ctx->thread_hnd == curl_thread_t_null) {
-    /* The thread never started */
-    addr_ctx->ref_count = 1;
-    addr_ctx->thrd_done = TRUE;
-    err = errno;
-    goto err_exit;
-  }
-
-#ifdef USE_HTTPSRR_ARES
-  if(async_rr_start(data, port))
-    infof(data, "Failed HTTPS RR operation");
-#endif
-  CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
-  return TRUE;
-
-err_exit:
-  CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
-  async_thrdd_destroy(data);
-  errno = err;
-  return FALSE;
-}
-
-static void async_thrdd_shutdown(struct Curl_easy *data)
-{
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
-  curl_bit done;
-
-  if(!addr_ctx)
-    return;
-  if(addr_ctx->thread_hnd == curl_thread_t_null)
-    return;
-
-  Curl_mutex_acquire(&addr_ctx->mutx);
-#ifndef CURL_DISABLE_SOCKETPAIR
-  if(!addr_ctx->do_abort)
-    Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
-#endif
-  addr_ctx->do_abort = TRUE;
-  done = addr_ctx->thrd_done;
-  Curl_mutex_release(&addr_ctx->mutx);
-
-  /* Wait for the thread to terminate if it is already marked done. If it is
-     not done yet we cannot do anything here. We had tried pthread_cancel but
-     it caused hanging and resource leaks (#18532). */
-  if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
-    Curl_thread_join(&addr_ctx->thread_hnd);
-    CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined");
-  }
+  async_thrdd_item_destroy(async->thrdd.res_A);
+  async->thrdd.res_A = NULL;
+  async_thrdd_item_destroy(async->thrdd.res_AAAA);
+  async->thrdd.res_AAAA = NULL;
 }
 
 /*
- * 'entry' may be NULL and then no data is returned
- */
-static CURLcode asyn_thrdd_await(struct Curl_easy *data,
-                                 struct async_thrdd_addr_ctx *addr_ctx,
-                                 struct Curl_dns_entry **entry)
-{
-  CURLcode result = CURLE_OK;
-
-  if(addr_ctx->thread_hnd != curl_thread_t_null) {
-    /* not interested in result? cancel, if still running... */
-    if(!entry)
-      async_thrdd_shutdown(data);
-
-    if(addr_ctx->thread_hnd != curl_thread_t_null) {
-      CURL_TRC_DNS(data, "resolve, wait for thread to finish");
-      if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
-        DEBUGASSERT(0);
-      }
-    }
-
-    if(entry)
-      result = Curl_async_is_resolved(data, entry);
-  }
-
-  data->state.async.done = TRUE;
-  if(entry)
-    *entry = data->state.async.dns;
-
-  return result;
-}
-
-/*
- * Until we gain a way to signal the resolver threads to stop early, we must
- * wait for them and ignore their results.
- */
-void Curl_async_thrdd_shutdown(struct Curl_easy *data)
-{
-  async_thrdd_shutdown(data);
-}
-
-void Curl_async_thrdd_destroy(struct Curl_easy *data)
-{
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-
-  if(thrdd->addr && !data->set.quick_exit) {
-    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
-  }
-  async_thrdd_destroy(data);
-}
-
-/*
- * Curl_async_await()
- *
  * Waits for a resolve to finish. This function should be avoided since using
  * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
- *
- * This is the version for resolves-in-a-thread.
  */
-CURLcode Curl_async_await(struct Curl_easy *data,
-                          struct Curl_dns_entry **dns)
+CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
+                          struct Curl_dns_entry **pdns)
 {
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  if(thrdd->addr)
-    return asyn_thrdd_await(data, thrdd->addr, dns);
-  return CURLE_FAILED_INIT;
-}
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL;
+  timediff_t milli, ms;
 
-/*
- * Curl_async_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- */
-CURLcode Curl_async_is_resolved(struct Curl_easy *data,
-                                struct Curl_dns_entry **dns)
-{
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  curl_bit done = FALSE;
-
-  DEBUGASSERT(dns);
-  *dns = NULL;
-
-  if(data->state.async.done) {
-    *dns = data->state.async.dns;
-    CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
-                 *dns ? "" : "not ");
-    return CURLE_OK;
-  }
-
-#ifdef USE_HTTPSRR_ARES
-  /* best effort, ignore errors */
-  if(thrdd->rr.channel)
-    (void)Curl_ares_perform(thrdd->rr.channel, 0);
-#endif
-
-  DEBUGASSERT(thrdd->addr);
-  if(!thrdd->addr)
+  if(!thrdd)
     return CURLE_FAILED_INIT;
 
-  Curl_mutex_acquire(&thrdd->addr->mutx);
-  done = thrdd->addr->thrd_done;
-  Curl_mutex_release(&thrdd->addr->mutx);
+  while(async->queries_ongoing && !async->done) {
+    Curl_async_thrdd_multi_process(data->multi);
+    if(async->done)
+      break;
 
-  if(done) {
-    CURLcode result = CURLE_OK;
-
-    data->state.async.done = TRUE;
-    Curl_resolv_unlink(data, &data->state.async.dns);
-    Curl_expire_done(data, EXPIRE_ASYNC_NAME);
-
-    if(thrdd->addr->res) {
-      data->state.async.dns =
-        Curl_dnscache_mk_entry(data, &thrdd->addr->res,
-                               data->state.async.hostname, 0,
-                               data->state.async.port, FALSE);
-      if(!data->state.async.dns)
-        result = CURLE_OUT_OF_MEMORY;
-
-#ifdef USE_HTTPSRR_ARES
-      if(thrdd->rr.channel) {
-        result = thrdd->rr.result;
-        if(!result) {
-          struct Curl_https_rrinfo *lhrr;
-          lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
-          if(!lhrr)
-            result = CURLE_OUT_OF_MEMORY;
-          else
-            data->state.async.dns->hinfo = lhrr;
-        }
-      }
-#endif
-      if(!result && data->state.async.dns)
-        result = Curl_dnscache_add(data, data->state.async.dns);
-    }
-
-    if(!result && !data->state.async.dns)
-      result = Curl_resolver_error(data, NULL);
-    if(result)
-      Curl_resolv_unlink(data, &data->state.async.dns);
-    *dns = data->state.async.dns;
-    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
-                 result, *dns ? "" : "not ");
-    async_thrdd_shutdown(data);
-    return result;
-  }
-  else {
-    /* poll for name lookup done with exponential backoff up to 250ms */
-    /* should be fine even if this converts to 32-bit */
-    timediff_t elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data),
-                                            &data->progress.t_startsingle);
-    if(elapsed < 0)
-      elapsed = 0;
-
-    if(thrdd->addr->poll_interval == 0)
-      /* Start at 1ms poll interval */
-      thrdd->addr->poll_interval = 1;
-    else if(elapsed >= thrdd->addr->interval_end)
-      /* Back-off exponentially if last interval expired */
-      thrdd->addr->poll_interval *= 2;
-
-    if(thrdd->addr->poll_interval > 250)
-      thrdd->addr->poll_interval = 250;
-
-    thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
-    Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
-    return CURLE_OK;
-  }
-}
-
-CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
-{
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  CURLcode result = CURLE_OK;
-  curl_bit thrd_done;
-
-#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR)
-  (void)ps;
-#endif
-
-#ifdef USE_HTTPSRR_ARES
-  if(thrdd->rr.channel) {
-    result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
-    if(result)
-      return result;
-  }
-#endif
-  if(!thrdd->addr)
-    return result;
-
-  Curl_mutex_acquire(&thrdd->addr->mutx);
-  thrd_done = thrdd->addr->thrd_done;
-  Curl_mutex_release(&thrdd->addr->mutx);
-
-  if(!thrd_done) {
-#ifndef CURL_DISABLE_SOCKETPAIR
-    /* return read fd to client for polling the DNS resolution status */
-    result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
-#else
-    timediff_t milli;
-    timediff_t ms =
-      curlx_ptimediff_ms(Curl_pgrs_now(data), &thrdd->addr->start);
+    ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
     if(ms < 3)
       milli = 0;
     else if(ms <= 50)
@@ -701,68 +323,506 @@
       milli = 50;
     else
       milli = 200;
-    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+    CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli);
+    curlx_wait_ms(milli);
+  }
+  return Curl_async_take_result(data, async, pdns);
+}
+
+#ifdef HAVE_GETADDRINFO
+
+/* Process the item, using Curl_getaddrinfo_ex() */
+static void async_thrdd_item_process(void *arg)
+{
+  struct async_thrdd_item *item = arg;
+  struct addrinfo hints;
+  char service[12];
+  int pf = PF_INET;
+  int rc;
+
+#ifdef DEBUGBUILD
+  if(item->delay_ms) {
+    curlx_wait_ms(item->delay_ms);
+  }
+  if(item->delay_fail_ms) {
+    curlx_wait_ms(item->delay_fail_ms);
+    return;
+  }
 #endif
+
+  memset(&hints, 0, sizeof(hints));
+#ifdef CURLRES_IPV6
+  if(item->dns_queries & CURL_DNSQ_AAAA) {
+    pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6;
+  }
+#endif
+  hints.ai_family = pf;
+  hints.ai_socktype = Curl_socktype_for_transport(item->transport);
+  hints.ai_protocol = Curl_protocol_for_transport(item->transport);
+#ifdef __APPLE__
+  /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED
+   * when doing AAAA queries. We do not want this "help". */
+  hints.ai_flags = AI_ADDRCONFIG;
+#endif
+
+  curl_msnprintf(service, sizeof(service), "%u", item->port);
+#ifdef AI_NUMERICSERV
+  hints.ai_flags |= AI_NUMERICSERV;
+#endif
+
+  rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
+  if(rc) {
+    item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
+    if(item->sock_error == 0)
+      item->sock_error = RESOLVER_ENOMEM;
+  }
+  else {
+    Curl_addrinfo_set_port(item->res, item->port);
+  }
+}
+
+#else /* HAVE_GETADDRINFO */
+
+/* Process the item, using Curl_ipv4_resolve_r() */
+static void async_thrdd_item_process(void *arg)
+{
+  struct async_thrdd_item *item = arg;
+
+#ifdef DEBUGBUILD
+  if(item->delay_ms) {
+    curlx_wait_ms(item->delay_ms);
+  }
+  if(item->delay_fail_ms) {
+    curlx_wait_ms(item->delay_fail_ms);
+    return;
+  }
+#endif
+  item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
+  if(!item->res) {
+    item->sock_error = SOCKERRNO;
+    if(item->sock_error == 0)
+      item->sock_error = RESOLVER_ENOMEM;
+  }
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+#ifdef ENABLE_WAKEUP
+static void async_thrdd_event(const struct curl_thrdq *tqueue,
+                              Curl_thrdq_event ev,
+                              void *user_data)
+{
+  struct Curl_multi *multi = user_data;
+  (void)tqueue;
+  switch(ev) {
+  case CURL_THRDQ_EV_ITEM_DONE:
+    (void)curl_multi_wakeup(multi);
+    break;
+  default:
+    break;
+  }
+}
+#else
+#define async_thrdd_event   NULL
+#endif
+
+static void async_thrdd_item_free(void *item)
+{
+  async_thrdd_item_destroy(item);
+}
+
+/* Create a thread queue for processing resolv items */
+CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
+                                     uint32_t min_threads,
+                                     uint32_t max_threads,
+                                     uint32_t idle_time_ms)
+{
+  CURLcode result;
+  DEBUGASSERT(!multi->resolv_thrdq);
+  result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0,
+                             min_threads, max_threads, idle_time_ms,
+                             async_thrdd_item_free,
+                             async_thrdd_item_process,
+                             async_thrdd_event,
+                             multi);
+#ifdef DEBUGBUILD
+  if(!result) {
+    const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS");
+    if(p) {
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, UINT32_MAX)) {
+        result = Curl_async_thrdd_multi_set_props(
+          multi, min_threads, (uint32_t)l, idle_time_ms);
+      }
+    }
+  }
+#endif
+  return result;
+}
+
+/* Tear down the thread queue, joining active threads or detaching them */
+void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join)
+{
+  if(multi->resolv_thrdq) {
+#ifdef CURLVERBOSE
+    CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join);
+    Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
+#endif
+    Curl_thrdq_destroy(multi->resolv_thrdq, join);
+    multi->resolv_thrdq = NULL;
+  }
+}
+
+#ifdef CURLVERBOSE
+static void async_thrdd_report_item(struct Curl_easy *data,
+                                    struct async_thrdd_item *item)
+{
+  char buf[MAX_IPADR_LEN];
+  struct dynbuf tmp;
+  const char *sep = "";
+  const struct Curl_addrinfo *ai = item->res;
+  int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
+  CURLcode result;
+
+  if(!CURL_TRC_DNS_is_verbose(data))
+    return;
+
+  curlx_dyn_init(&tmp, 1024);
+  for(; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family) {
+      Curl_printable_address(ai, buf, sizeof(buf));
+      result = curlx_dyn_addf(&tmp, "%s%s", sep, buf);
+      if(result) {
+        CURL_TRC_DNS(data, "too many IP, cannot show");
+        goto out;
+      }
+      sep = ", ";
+    }
+  }
+
+  CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s",
+               item->hostname, item->port,
+               (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4',
+               (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)"));
+out:
+  curlx_dyn_free(&tmp);
+}
+#endif /* CURLVERBOSE */
+
+/* Process the receiving end of the thread queue, dispatching
+ * processed items to their transfer when it can still be found
+ * and has an `async` state present. Otherwise, destroy the item. */
+void Curl_async_thrdd_multi_process(struct Curl_multi *multi)
+{
+  struct Curl_easy *data;
+  void *qitem;
+
+  while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) {
+    /* dispatch resolve result */
+    struct async_thrdd_item *item = qitem;
+    struct Curl_resolv_async *async = NULL;
+
+    data = Curl_multi_get_easy(multi, item->mid);
+    if(data)
+      async = Curl_async_get(data, item->resolv_id);
+    if(async) {
+      struct async_thrdd_item **pdest = &async->thrdd.res_A;
+
+      async->dns_responses |= item->dns_queries;
+      --async->queries_ongoing;
+      async->done = !async->queries_ongoing;
+
+#ifdef CURLRES_IPV6
+      if(item->dns_queries & CURL_DNSQ_AAAA)
+        pdest = &async->thrdd.res_AAAA;
+#endif
+      if(!*pdest) {
+        VERBOSE(async_thrdd_report_item(data, item));
+        *pdest = item;
+        item = NULL;
+      }
+      else
+        DEBUGASSERT(0); /* should not receive duplicates here */
+      Curl_multi_mark_dirty(data);
+    }
+    async_thrdd_item_free(item);
+  }
+#ifdef CURLVERBOSE
+  Curl_thrdq_trace(multi->resolv_thrdq, multi->admin);
+#endif
+}
+
+CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
+                                          uint32_t min_threads,
+                                          uint32_t max_threads,
+                                          uint32_t idle_time_ms)
+{
+  return Curl_thrdq_set_props(multi->resolv_thrdq, 0,
+                              min_threads, max_threads, idle_time_ms);
+}
+
+static CURLcode async_thrdd_query(struct Curl_easy *data,
+                                  struct Curl_resolv_async *async,
+                                  uint8_t dns_queries)
+{
+  struct async_thrdd_item *item;
+  CURLcode result;
+
+  item = async_thrdd_item_create(data, async->id, dns_queries,
+                                 async->hostname, async->port,
+                                 async->transport);
+  if(!item) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+  CURL_TRC_DNS(data, "queueing query %s", item->description);
+  result = Curl_thrdq_send(data->multi->resolv_thrdq, item,
+                           async_item_description(item), async->timeout_ms);
+  if(result)
+    goto out;
+  item = NULL;
+  async->queries_ongoing++;
+
+out:
+  if(item)
+    async_thrdd_item_free(item);
+  return result;
+}
+
+CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
+                                struct Curl_resolv_async *async)
+{
+  CURLcode result = CURLE_FAILED_INIT;
+  void *resolver = NULL;
+
+  if(async->queries_ongoing || async->done)
+    return CURLE_FAILED_INIT;
+
+#ifdef USE_HTTPSRR_ARES
+  DEBUGASSERT(!async->thrdd.rr.channel);
+  if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) {
+    result = async_rr_start(data, async);
+    if(result)
+      goto out;
+    resolver = async->thrdd.rr.channel;
+  }
+#endif
+
+  result = Curl_resolv_announce_start(data, resolver);
+  if(result)
+    return result;
+
+#ifdef CURLRES_IPV6
+  /* Do not start an AAAA query for an ipv4 address when
+   * we will start an A query for it. */
+  if((async->dns_queries & CURL_DNSQ_AAAA) &&
+     !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) {
+    result = async_thrdd_query(data, async, CURL_DNSQ_AAAA);
+    if(result)
+      goto out;
+  }
+#endif
+  if(async->dns_queries & CURL_DNSQ_A) {
+    result = async_thrdd_query(data, async, CURL_DNSQ_A);
+    if(result)
+      goto out;
+  }
+
+#ifdef CURLVERBOSE
+  Curl_thrdq_trace(data->multi->resolv_thrdq, data);
+#endif
+
+out:
+  if(result)
+    CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
+                 async->hostname, async->port, result);
+  return result;
+}
+
+CURLcode Curl_async_pollset(struct Curl_easy *data,
+                            struct Curl_resolv_async *async,
+                            struct easy_pollset *ps)
+{
+  timediff_t timeout_ms;
+
+  timeout_ms = Curl_async_timeleft_ms(data, async);
+#ifdef USE_HTTPSRR_ARES
+  if(async->thrdd.rr.channel) {
+    CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps);
+    if(result)
+      return result;
+    timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel);
+  }
+#else
+  (void)ps;
+#endif
+
+  if(!async->done) {
+#ifndef ENABLE_WAKEUP
+    timediff_t stutter_ms, elapsed_ms;
+    elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
+    if(elapsed_ms < 3)
+      stutter_ms = 1;
+    else if(elapsed_ms <= 50)
+      stutter_ms = elapsed_ms / 3;
+    else if(elapsed_ms <= 250)
+      stutter_ms = 50;
+    else
+      stutter_ms = 200;
+    timeout_ms = CURLMIN(stutter_ms, timeout_ms);
+#endif
+    Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME);
+  }
+  return CURLE_OK;
+}
+
+/*
+ * Curl_async_take_result() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ */
+CURLcode Curl_async_take_result(struct Curl_easy *data,
+                                struct Curl_resolv_async *async,
+                                struct Curl_dns_entry **pdns)
+{
+  struct async_thrdd_ctx *thrdd = &async->thrdd;
+  struct Curl_dns_entry *dns = NULL;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(pdns);
+  *pdns = NULL;
+  if(!async->queries_ongoing && !async->done) {
+    DEBUGASSERT(0);
+    return CURLE_FAILED_INIT;
+  }
+
+#ifdef USE_HTTPSRR_ARES
+  /* best effort, ignore errors */
+  if(thrdd->rr.channel)
+    (void)Curl_ares_perform(thrdd->rr.channel, 0);
+#endif
+
+  if(!async->done)
+    return CURLE_AGAIN;
+
+  Curl_expire_done(data, EXPIRE_ASYNC_NAME);
+  if(async->result) {
+    result = async->result;
+    goto out;
+  }
+
+  if((thrdd->res_A && thrdd->res_A->res) ||
+     (thrdd->res_AAAA && thrdd->res_AAAA->res)) {
+    dns = Curl_dnscache_mk_entry2(
+      data, async->dns_queries,
+      thrdd->res_A ? &thrdd->res_A->res : NULL,
+      thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL,
+      async->hostname, async->port);
+    if(!dns) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
+
+#ifdef USE_HTTPSRR_ARES
+    if(thrdd->rr.channel) {
+      struct Curl_https_rrinfo *lhrr = NULL;
+      if(thrdd->rr.hinfo.complete) {
+        lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
+        if(!lhrr) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
+        }
+      }
+      Curl_httpsrr_trace(data, lhrr);
+      Curl_dns_entry_set_https_rr(dns, lhrr);
+    }
+#endif
+  }
+
+  if(dns) {
+    *pdns = dns;
+    dns = NULL;
+  }
+#ifdef CURLVERBOSE
+  Curl_thrdq_trace(data->multi->resolv_thrdq, data);
+#endif
+
+out:
+  Curl_dns_entry_unlink(data, &dns);
+  Curl_async_thrdd_shutdown(data, async);
+  if(!result && !*pdns)
+    result = Curl_async_failed(data, async, NULL);
+  if(result &&
+     (result != CURLE_COULDNT_RESOLVE_HOST) &&
+     (result != CURLE_COULDNT_RESOLVE_PROXY)) {
+    CURL_TRC_DNS(data, "Error %d resolving %s:%d",
+                 result, async->hostname, async->port);
   }
   return result;
 }
 
-#ifndef HAVE_GETADDRINFO
-/*
- * Curl_async_getaddrinfo() - for platforms without getaddrinfo
- */
-CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
-                                int port, int ip_version)
+static const struct Curl_addrinfo *async_thrdd_get_ai(
+  const struct Curl_addrinfo *ai,
+  int ai_family, unsigned int index)
 {
-  (void)ip_version;
-
-  /* fire up a new resolver thread! */
-  if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
-    return CURLE_OK;
+  unsigned int i = 0;
+  for(i = 0; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family) {
+      if(i == index)
+        return ai;
+      ++i;
+    }
   }
-
-  failf(data, "getaddrinfo() thread failed");
-  return CURLE_FAILED_INIT;
+  return NULL;
 }
 
-#else /* !HAVE_GETADDRINFO */
-
-/*
- * Curl_async_getaddrinfo() - for getaddrinfo
- */
-CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
-                                int port, int ip_version)
+const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data,
+                                              struct Curl_resolv_async *async,
+                                              int ai_family,
+                                              unsigned int index)
 {
-  struct addrinfo hints;
-  int pf = PF_INET;
+  struct async_thrdd_ctx *thrdd = &async->thrdd;
 
-  CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
-#ifdef CURLRES_IPV6
-  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
-    /* The stack seems to be IPv6-enabled */
-    if(ip_version == CURL_IPRESOLVE_V6)
-      pf = PF_INET6;
-    else
-      pf = PF_UNSPEC;
+  (void)data;
+  switch(ai_family) {
+  case AF_INET:
+    if(thrdd->res_A)
+      return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
+    break;
+  case AF_INET6:
+    if(thrdd->res_AAAA)
+      return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
+    break;
+  default:
+    break;
   }
+  return NULL;
+}
+
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *Curl_async_get_https(
+  struct Curl_easy *data,
+  struct Curl_resolv_async *async)
+{
+#ifdef USE_HTTPSRR_ARES
+  if(Curl_async_knows_https(data, async))
+    return &async->thrdd.rr.hinfo;
 #else
-  (void)ip_version;
-#endif /* CURLRES_IPV6 */
-
-  memset(&hints, 0, sizeof(hints));
-  hints.ai_family = pf;
-  hints.ai_socktype =
-    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
-    SOCK_STREAM : SOCK_DGRAM;
-
-  /* fire up a new resolver thread! */
-  if(async_thrdd_init(data, hostname, port, ip_version, &hints))
-    return CURLE_OK;
-
-  failf(data, "getaddrinfo() thread failed to start");
-  return CURLE_FAILED_INIT;
+  (void)data;
+  (void)async;
+#endif
+  return NULL;
 }
 
-#endif /* !HAVE_GETADDRINFO */
+bool Curl_async_knows_https(struct Curl_easy *data,
+                            struct Curl_resolv_async *async)
+{
+  (void)data;
+  if(async->dns_queries & CURL_DNSQ_HTTPS)
+    return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done);
+  return TRUE; /* we know it will never come */
+}
 
-#endif /* CURLRES_THREADED */
+#endif /* USE_HTTPSRR */
+
+#endif /* USE_RESOLV_THREADED */
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h
index 68628f9..ed50933 100644
--- a/Utilities/cmcurl/lib/asyn.h
+++ b/Utilities/cmcurl/lib/asyn.h
@@ -31,6 +31,9 @@
 
 struct Curl_easy;
 struct Curl_dns_entry;
+struct Curl_resolv_async;
+struct Curl_multi;
+struct easy_pollset;
 
 #ifdef CURLRES_ASYNCH
 
@@ -40,8 +43,8 @@
 struct connectdata;
 struct easy_pollset;
 
-#if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
-#error cannot have both CURLRES_ARES and CURLRES_THREADED defined
+#if defined(USE_RESOLV_ARES) && defined(USE_RESOLV_THREADED)
+#error cannot have both USE_RESOLV_ARES and USE_RESOLV_THREADED defined
 #endif
 
 /*
@@ -66,49 +69,6 @@
 void Curl_async_global_cleanup(void);
 
 /*
- * Curl_async_get_impl()
- * Get the resolver implementation instance (c-ares channel) or NULL
- * for passing to application callback.
- */
-CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl);
-
-/* Curl_async_pollset()
- *
- * This function is called from the Curl_multi_pollset() function.  'sock' is a
- * pointer to an array to hold the file descriptors, with 'numsock' being the
- * size of that array (in number of entries). This function is supposed to
- * return bitmask indicating what file descriptors (referring to array indexes
- * in the 'sock' array) to wait for, read/write.
- */
-CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps);
-
-/*
- * Curl_async_is_resolved()
- *
- * Called repeatedly to check if a previous name resolve request has
- * completed. It should also make sure to time-out if the operation seems to
- * take too long.
- *
- * Returns normal CURLcode errors.
- */
-CURLcode Curl_async_is_resolved(struct Curl_easy *data,
-                                struct Curl_dns_entry **dns);
-
-/*
- * Curl_async_await()
- *
- * Waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
- */
-CURLcode Curl_async_await(struct Curl_easy *data,
-                          struct Curl_dns_entry **dns);
-
-/*
  * Curl_async_getaddrinfo() - when using this resolver
  *
  * Returns name information about the given hostname and port number. If
@@ -119,8 +79,21 @@
  * Each resolver backend must of course make sure to return data in the
  * correct format to comply with this.
  */
-CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname,
-                                int port, int ip_version);
+CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
+                                struct Curl_resolv_async *async);
+
+const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data,
+                                              struct Curl_resolv_async *async,
+                                              int ai_family,
+                                              unsigned int index);
+
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *Curl_async_get_https(
+  struct Curl_easy *data,
+  struct Curl_resolv_async *async);
+bool Curl_async_knows_https(struct Curl_easy *data,
+                            struct Curl_resolv_async *async);
+#endif /* USE_HTTPSRR */
 
 #ifdef USE_ARES
 /* common functions for c-ares and threaded resolver with HTTPSRR */
@@ -130,105 +103,119 @@
                            ares_channel channel,
                            struct easy_pollset *ps);
 
+timediff_t Curl_ares_timeout_ms(struct Curl_easy *data,
+                                struct Curl_resolv_async *async,
+                                ares_channel channel);
+
 int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms);
 #endif
 
-#ifdef CURLRES_ARES
+#ifdef USE_RESOLV_ARES
 /* async resolving implementation using c-ares alone */
 struct async_ares_ctx {
   ares_channel channel;
-  int num_pending;               /* number of outstanding c-ares requests */
-  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
-                                    parts */
+  struct Curl_addrinfo *res_A;
+  struct Curl_addrinfo *res_AAAA;
   int ares_status;               /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */
   CURLcode result;               /* CURLE_OK or error handling response */
-#ifndef HAVE_CARES_GETADDRINFO
   struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
-#endif
 #ifdef USE_HTTPSRR
   struct Curl_https_rrinfo hinfo;
 #endif
 };
 
-void Curl_async_ares_shutdown(struct Curl_easy *data);
-void Curl_async_ares_destroy(struct Curl_easy *data);
+void Curl_async_ares_shutdown(struct Curl_easy *data,
+                              struct Curl_resolv_async *async);
+void Curl_async_ares_destroy(struct Curl_easy *data,
+                             struct Curl_resolv_async *async);
 
-/* Set the DNS server to use by ares, from `data` settings. */
-CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
+#endif /* USE_RESOLV_ARES */
 
-/* Set the DNS interfacer to use by ares, from `data` settings. */
-CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
+#ifdef USE_RESOLV_THREADED
 
-/* Set the local ipv4 address to use by ares, from `data` settings. */
-CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
-
-/* Set the local ipv6 address to use by ares, from `data` settings. */
-CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
-
-#endif /* CURLRES_ARES */
-
-#ifdef CURLRES_THREADED
-/* async resolving implementation using POSIX threads */
-#include "curl_threads.h"
-
-/* Context for threaded address resolver */
-struct async_thrdd_addr_ctx {
-  curl_thread_t thread_hnd;
-  char *hostname;        /* hostname to resolve, Curl_async.hostname
-                            duplicate */
-  curl_mutex_t mutx;
-#ifndef CURL_DISABLE_SOCKETPAIR
-  curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
-#endif
-  struct Curl_addrinfo *res;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo hints;
-#endif
-  struct curltime start;
-  timediff_t interval_end;
-  unsigned int poll_interval;
-  int port;
-  int sock_error;
-  int ref_count;
-  BIT(thrd_done);
-  BIT(do_abort);
-};
+struct async_thrdd_item;
 
 /* Context for threaded resolver */
 struct async_thrdd_ctx {
-  /* `addr` is a pointer since this memory is shared with a started
-   * thread. Since threads cannot be killed, we use reference counting
-   * so that we can "release" our pointer to this memory while the
-   * thread is still running. */
-  struct async_thrdd_addr_ctx *addr;
+  struct async_thrdd_item *res_A; /* ipv4 result */
+  struct async_thrdd_item *res_AAAA; /* ipv6 result */
 #if defined(USE_HTTPSRR) && defined(USE_ARES)
   struct {
     ares_channel channel;
     struct Curl_https_rrinfo hinfo;
-    CURLcode result;
-    BIT(done);
   } rr;
 #endif
 };
 
-void Curl_async_thrdd_shutdown(struct Curl_easy *data);
-void Curl_async_thrdd_destroy(struct Curl_easy *data);
+void Curl_async_thrdd_shutdown(struct Curl_easy *data,
+                               struct Curl_resolv_async *async);
+void Curl_async_thrdd_destroy(struct Curl_easy *data,
+                              struct Curl_resolv_async *async);
 
-#endif /* CURLRES_THREADED */
+CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi,
+                                     uint32_t min_threads,
+                                     uint32_t max_threads,
+                                     uint32_t idle_time_ms);
+void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join);
+void Curl_async_thrdd_multi_process(struct Curl_multi *multi);
+
+CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi,
+                                          uint32_t min_threads,
+                                          uint32_t max_threads,
+                                          uint32_t idle_time_ms);
+
+#endif /* USE_RESOLV_THREADED */
 
 #ifndef CURL_DISABLE_DOH
 struct doh_probes;
 #endif
 
+/*
+ * Curl_async_await()
+ *
+ * Waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * On return 'dns' is assigned the resolved dns (CURLE_OK or NULL otherwise.
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
+ */
+CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id,
+                          struct Curl_dns_entry **pdns);
+
+/*
+ * Take the result of an async resolve operation.
+ * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still
+ * ongoing or an error code for a failed resolve.
+ */
+CURLcode Curl_async_take_result(struct Curl_easy *data,
+                                struct Curl_resolv_async *async,
+                                struct Curl_dns_entry **pdns);
+
+/* Curl_async_pollset()
+ *
+ * This function is called from the Curl_multi_pollset() function.  'sock' is a
+ * pointer to an array to hold the file descriptors, with 'numsock' being the
+ * size of that array (in number of entries). This function is supposed to
+ * return bitmask indicating what file descriptors (referring to array indexes
+ * in the 'sock' array) to wait for, read/write.
+ */
+CURLcode Curl_async_pollset(struct Curl_easy *data,
+                            struct Curl_resolv_async *async,
+                            struct easy_pollset *ps);
+
 #else /* CURLRES_ASYNCH */
 
 /* convert these functions if an asynch resolver is not used */
-#define Curl_async_get_impl(x, y)    (*(y) = NULL, CURLE_OK)
-#define Curl_async_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST
-#define Curl_async_await(x, y)       CURLE_COULDNT_RESOLVE_HOST
-#define Curl_async_global_init()     CURLE_OK
-#define Curl_async_global_cleanup()  Curl_nop_stmt
-
+#define Curl_async_global_init()        CURLE_OK
+#define Curl_async_global_cleanup()     Curl_nop_stmt
+#define Curl_async_get_ai(a, b, c, d)   NULL
+#define Curl_async_await(a, b, c)       CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_pollset(x, y, z)     CURLE_OK
+#define Curl_async_get_https(x, y)      NULL
+#define Curl_async_knows_https(x, y)    TRUE
 #endif /* !CURLRES_ASYNCH */
 
 #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
@@ -236,38 +223,54 @@
 #endif
 
 #ifdef USE_CURL_ASYNC
-struct Curl_async {
-#ifdef CURLRES_ARES
+
+struct Curl_resolv_async {
+  struct Curl_resolv_async *next;
+#ifdef USE_RESOLV_ARES
   struct async_ares_ctx ares;
-#elif defined(CURLRES_THREADED)
+#elif defined(USE_RESOLV_THREADED)
   struct async_thrdd_ctx thrdd;
 #endif
 #ifndef CURL_DISABLE_DOH
   struct doh_probes *doh; /* DoH specific data for this request */
 #endif
-  struct Curl_dns_entry *dns; /* result of resolving on success */
-  char *hostname; /* copy of the params resolv started with */
-  int port;
-  int ip_version;
+  struct curltime start;
+  timediff_t interval_end;
+  timediff_t timeout_ms;
+  CURLcode result;
+  uint32_t poll_interval;
+  uint32_t id; /* unique id per easy handle of the resolve operation */
+  /* what is being resolved */
+  uint16_t port;
+  uint8_t dns_queries; /* what queries are being performed */
+  uint8_t dns_responses; /* what queries had responses so far. */
+  uint8_t transport;
+  uint8_t queries_ongoing;
+  BIT(is_ipaddr);
+  BIT(is_ipv4addr);
+  BIT(for_proxy);
   BIT(done);
+  BIT(shutdown);
+  char hostname[1];
 };
 
-/*
- * Curl_async_shutdown().
- *
- * This shuts down all ongoing operations.
- */
-void Curl_async_shutdown(struct Curl_easy *data);
+timediff_t Curl_async_timeleft_ms(struct Curl_easy *data,
+                                  struct Curl_resolv_async *async);
 
-/*
- * Curl_async_destroy().
- *
- * This frees the resources of any async resolve.
- */
-void Curl_async_destroy(struct Curl_easy *data);
+/* Shut down the given async resolve. */
+void Curl_async_shutdown(struct Curl_easy *data,
+                         struct Curl_resolv_async *async);
+
+/* Frees the resources of the given async resolve and the struct itself. */
+void Curl_async_destroy(struct Curl_easy *data,
+                        struct Curl_resolv_async *async);
+
+CURLcode Curl_async_failed(struct Curl_easy *data,
+                           struct Curl_resolv_async *async,
+                           const char *detail);
+
 #else /* !USE_CURL_ASYNC */
-#define Curl_async_shutdown(x) Curl_nop_stmt
-#define Curl_async_destroy(x)  Curl_nop_stmt
+#define Curl_async_shutdown(x, y) Curl_nop_stmt
 #endif /* USE_CURL_ASYNC */
 
 /********** end of generic resolver interface functions *****************/
diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h
index 8dd186f..da411b5 100644
--- a/Utilities/cmcurl/lib/bufq.h
+++ b/Utilities/cmcurl/lib/bufq.h
@@ -132,8 +132,8 @@
                      size_t max_chunks, int opts);
 
 /**
- * Reset the buffer queue to be empty. Will keep any allocated buffer
- * chunks around.
+ * Reset the buffer queue to be empty. Keep any allocated buffer chunks
+ * around.
  */
 void Curl_bufq_reset(struct bufq *q);
 
@@ -243,7 +243,7 @@
 
 /**
  * Write buf to the end of the buffer queue.
- * Will write bufq content or passed `buf` directly using the `writer`
+ * Write bufq content or passed `buf` directly using the `writer`
  * callback when it sees fit. 'buf' might get passed directly
  * on or is placed into the buffer, depending on `len` and current
  * amount buffered, chunk size, etc.
diff --git a/Utilities/cmcurl/lib/cf-dns.c b/Utilities/cmcurl/lib/cf-dns.c
new file mode 100644
index 0000000..e763b8e
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-dns.c
@@ -0,0 +1,667 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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 "urldata.h"
+#include "curl_addrinfo.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "dnscache.h"
+#include "httpsrr.h"
+#include "curl_trc.h"
+#include "progress.h"
+#include "url.h"
+#include "cf-dns.h"
+
+
+struct cf_dns_ctx {
+  struct Curl_dns_entry *dns;
+  CURLcode resolv_result;
+  uint32_t resolv_id;
+  uint16_t port;
+  uint8_t dns_queries;
+  uint8_t transport;
+  BIT(started);
+  BIT(announced);
+  BIT(abstract_unix_socket);
+  BIT(complete_resolve);
+  BIT(for_proxy);
+  char hostname[1];
+};
+
+static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data,
+                                            uint8_t dns_queries,
+                                            const char *hostname,
+                                            uint16_t port, uint8_t transport,
+                                            bool abstract_unix_socket,
+                                            bool for_proxy,
+                                            bool complete_resolve,
+                                            struct Curl_dns_entry *dns)
+{
+  struct cf_dns_ctx *ctx;
+  size_t hlen = strlen(hostname);
+
+  ctx = curlx_calloc(1, sizeof(*ctx) + hlen);
+  if(!ctx)
+    return NULL;
+
+  ctx->port = port;
+  ctx->dns_queries = dns_queries;
+  ctx->transport = transport;
+  ctx->abstract_unix_socket = abstract_unix_socket;
+  ctx->for_proxy = for_proxy;
+  ctx->complete_resolve = complete_resolve;
+  ctx->dns = Curl_dns_entry_link(data, dns);
+  ctx->started = !!ctx->dns;
+  if(hlen)
+    memcpy(ctx->hostname, hostname, hlen);
+
+  CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x",
+               ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries);
+  return ctx;
+}
+
+static void cf_dns_ctx_destroy(struct Curl_easy *data,
+                               struct cf_dns_ctx *ctx)
+{
+  if(ctx) {
+    Curl_dns_entry_unlink(data, &ctx->dns);
+    curlx_free(ctx);
+  }
+}
+
+#ifdef CURLVERBOSE
+static void cf_dns_report_addr(struct Curl_easy *data,
+                               struct dynbuf *tmp,
+                               const char *label,
+                               int ai_family,
+                               const struct Curl_addrinfo *ai)
+{
+  char buf[MAX_IPADR_LEN];
+  const char *sep = "";
+  CURLcode result;
+
+  curlx_dyn_reset(tmp);
+  for(; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family) {
+      Curl_printable_address(ai, buf, sizeof(buf));
+      result = curlx_dyn_addf(tmp, "%s%s", sep, buf);
+      if(result) {
+        infof(data, "too many IP, cannot show");
+        return;
+      }
+      sep = ", ";
+    }
+  }
+
+  infof(data, "%s%s", label,
+        (curlx_dyn_len(tmp) ? curlx_dyn_ptr(tmp) : "(none)"));
+}
+
+static void cf_dns_report(struct Curl_cfilter *cf,
+                          struct Curl_easy *data,
+                          struct Curl_dns_entry *dns)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+  struct dynbuf tmp;
+
+  if(!Curl_trc_is_verbose(data) ||
+     /* ignore no name or numerical IP addresses */
+     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
+    return;
+
+  switch(ctx->transport) {
+  case TRNSPRT_UNIX:
+#ifdef USE_UNIX_SOCKETS
+    CURL_TRC_CF(data, cf, "resolved unix domain %s",
+                Curl_conn_get_unix_path(data->conn));
+#else
+    DEBUGASSERT(0);
+#endif
+    break;
+  default:
+    curlx_dyn_init(&tmp, 1024);
+    infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port);
+#ifdef CURLRES_IPV6
+    cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr);
+#endif
+    cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr);
+#ifdef USE_HTTPSRR
+    if(!dns->hinfo)
+      infof(data, "HTTPS-RR: -");
+    else if(!Curl_httpsrr_applicable(data, dns->hinfo))
+      infof(data, "HTTPS-RR: not applicable");
+    else {
+      CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo);
+      if(!result)
+        infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp));
+      else
+        infof(data, "Error printing HTTPS-RR information");
+    }
+#endif
+    curlx_dyn_free(&tmp);
+    break;
+  }
+}
+#else
+#define cf_dns_report(x, y, z) Curl_nop_stmt
+#endif
+
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode cf_dns_start(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct Curl_dns_entry **pdns)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+  timediff_t timeout_ms = Curl_timeleft_ms(data);
+  CURLcode result;
+
+  *pdns = NULL;
+
+#ifdef USE_UNIX_SOCKETS
+  if(ctx->transport == TRNSPRT_UNIX) {
+    CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname);
+    return Curl_resolv_unix(data, ctx->hostname,
+                            (bool)cf->conn->bits.abstract_unix_socket, pdns);
+  }
+#endif
+
+  /* Resolve target host right on */
+  CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port);
+  if(Curl_is_ipv4addr(ctx->hostname))
+    ctx->dns_queries |= CURL_DNSQ_A;
+#ifdef USE_IPV6
+  else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */
+    ctx->dns_queries |= CURL_DNSQ_AAAA;
+#endif
+  result = Curl_resolv(data, ctx->dns_queries,
+                       ctx->hostname, ctx->port, ctx->transport,
+                       (bool)ctx->for_proxy, timeout_ms,
+                       &ctx->resolv_id, pdns);
+  DEBUGASSERT(!result || !*pdns);
+  if(!result) { /* resolved right away, either sync or from dnscache */
+    DEBUGASSERT(*pdns);
+    return CURLE_OK;
+  }
+  else if(result == CURLE_AGAIN) { /* async resolv in progress */
+    return CURLE_OK;
+  }
+  else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */
+    failf(data, "Failed to resolve '%s' with timeout after %"
+          FMT_TIMEDIFF_T " ms", ctx->hostname,
+          curlx_ptimediff_ms(Curl_pgrs_now(data),
+                             &data->progress.t_startsingle));
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+  else {
+    DEBUGASSERT(result);
+    failf(data, "Could not resolve: %s", ctx->hostname);
+    return result;
+  }
+}
+
+#define CURL_HEV3_RESOLVE_DELAY_MS    50
+
+static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+
+  if(ctx->resolv_result)
+    return TRUE;
+  else if(ctx->dns)
+    return TRUE;
+#ifdef USE_CURL_ASYNC
+  else {
+    /* We want AAAA answer as we prefer ipv6. If a sub-filter desires
+    * HTTPS-RR, we check for that query as well. */
+    uint8_t wanted_answers = CURL_DNSQ_AAAA;
+    if(Curl_conn_cf_wants_httpsrr(cf, data))
+      wanted_answers |= CURL_DNSQ_HTTPS;
+
+    /* Note: if a query was never started, it is considered to have
+     * an answer (e.g. a negative one). */
+    if(Curl_resolv_has_answers(data, ctx->resolv_id, wanted_answers))
+      return TRUE;
+    /* If the wanted answers are not available after a delay,
+     * we let the connect attempts start anyway. */
+    return Curl_resolv_elapsed_ms(data, ctx->resolv_id) >=
+           CURL_HEV3_RESOLVE_DELAY_MS;
+  }
+#else
+  (void)data;
+  DEBUGASSERT(0); /* We should not come here */
+  return FALSE;
+#endif /* USE_CURL_ASYNC */
+}
+
+static CURLcode cf_dns_connect(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               bool *done)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+
+  if(cf->connected) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  *done = FALSE;
+  if(!ctx->started) {
+    ctx->started = TRUE;
+    ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns);
+  }
+
+  if(!ctx->dns && !ctx->resolv_result) {
+    ctx->resolv_result =
+      Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns);
+  }
+
+  if(ctx->resolv_result) {
+    CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result);
+    return ctx->resolv_result;
+  }
+
+  if(ctx->dns && !ctx->announced) {
+    ctx->announced = TRUE;
+    if(cf->sockindex == FIRSTSOCKET) {
+      cf->conn->bits.dns_resolved = TRUE;
+      Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+    }
+    cf_dns_report(cf, data, ctx->dns);
+  }
+
+  if(!cf_dns_ready_to_connect(cf, data)) {
+    return CURLE_OK;
+  }
+
+  if(cf->next && !cf->next->connected) {
+    bool sub_done;
+    CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done);
+    if(result || !sub_done)
+      return result;
+    DEBUGASSERT(sub_done);
+  }
+
+  /* sub filter chain is connected */
+  CURL_TRC_CF(data, cf, "connected filter chain below");
+  if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) {
+    /* This filter only connects when it has resolved everything. */
+    CURL_TRC_CF(data, cf, "delay connect until resolve complete");
+    return CURLE_OK;
+  }
+  *done = TRUE;
+  cf->connected = TRUE;
+  Curl_resolv_destroy(data, ctx->resolv_id);
+  return CURLE_OK;
+}
+
+static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+
+  CURL_TRC_CF(data, cf, "destroy");
+  cf_dns_ctx_destroy(data, ctx);
+}
+
+static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  cf->connected = FALSE;
+  if(cf->next)
+    cf->next->cft->do_close(cf->next, data);
+}
+
+static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct easy_pollset *ps)
+{
+#ifdef USE_CURL_ASYNC
+  if(!cf->connected)
+    return Curl_resolv_pollset(data, ps);
+#else
+  (void)cf;
+  (void)data;
+  (void)ps;
+#endif
+  return CURLE_OK;
+}
+
+static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             int event, int arg1, void *arg2)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  (void)arg1;
+  (void)arg2;
+  switch(event) {
+  case CF_CTRL_DATA_DONE:
+    if(ctx->dns) {
+      /* Should only come here when the connect attempt failed and
+       * `data` is giving up on it. On a successful connect, we already
+       * unlinked the DNS entry. */
+      Curl_dns_entry_unlink(data, &ctx->dns);
+    }
+    break;
+  default:
+    break;
+  }
+  return result;
+}
+
+struct Curl_cftype Curl_cft_dns = {
+  "DNS",
+  CF_TYPE_SETUP,
+  CURL_LOG_LVL_NONE,
+  cf_dns_destroy,
+  cf_dns_connect,
+  cf_dns_close,
+  Curl_cf_def_shutdown,
+  cf_dns_adjust_pollset,
+  Curl_cf_def_data_pending,
+  Curl_cf_def_send,
+  Curl_cf_def_recv,
+  cf_dns_cntrl,
+  Curl_cf_def_conn_is_alive,
+  Curl_cf_def_conn_keep_alive,
+  Curl_cf_def_query,
+};
+
+static CURLcode cf_dns_create(struct Curl_cfilter **pcf,
+                              struct Curl_easy *data,
+                              uint8_t dns_queries,
+                              const char *hostname,
+                              uint16_t port,
+                              uint8_t transport,
+                              bool abstract_unix_socket,
+                              bool for_proxy,
+                              bool complete_resolve,
+                              struct Curl_dns_entry *dns)
+{
+  struct Curl_cfilter *cf = NULL;
+  struct cf_dns_ctx *ctx;
+  CURLcode result = CURLE_OK;
+
+  (void)data;
+  ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport,
+                          abstract_unix_socket, for_proxy,
+                          complete_resolve, dns);
+  if(!ctx) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  result = Curl_cf_create(&cf, &Curl_cft_dns, ctx);
+
+out:
+  *pcf = result ? NULL : cf;
+  if(result)
+    cf_dns_ctx_destroy(data, ctx);
+  return result;
+}
+
+/* Create a "resolv" filter for the transfer's connection. Figures
+ * out the hostname/path and port where to connect to. */
+static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
+                                   struct Curl_easy *data,
+                                   uint8_t dns_queries,
+                                   uint8_t transport,
+                                   bool complete_resolve,
+                                   struct Curl_dns_entry *dns)
+{
+  struct connectdata *conn = data->conn;
+  const char *hostname = NULL;
+  uint16_t port = 0;
+  bool abstract_unix_socket = FALSE, for_proxy = FALSE;
+
+#ifdef USE_UNIX_SOCKETS
+  {
+    const char *unix_path = Curl_conn_get_unix_path(conn);
+    if(unix_path) {
+      DEBUGASSERT(transport == TRNSPRT_UNIX);
+      hostname = unix_path;
+      abstract_unix_socket = (bool)conn->bits.abstract_unix_socket;
+    }
+  }
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+  if(!hostname && conn->bits.proxy) {
+    for_proxy = TRUE;
+    hostname = conn->bits.socksproxy ?
+      conn->socks_proxy.host.name : conn->http_proxy.host.name;
+    port = conn->bits.socksproxy ?
+      conn->socks_proxy.port : conn->http_proxy.port;
+  }
+#endif
+  if(!hostname) {
+    struct hostname *ehost;
+    ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
+    /* If not connecting via a proxy, extract the port from the URL, if it is
+     * there, thus overriding any defaults that might have been set above. */
+    hostname = ehost->name;
+    port = conn->bits.conn_to_port ?
+      conn->conn_to_port : (uint16_t)conn->remote_port;
+  }
+
+  if(!hostname) {
+    DEBUGASSERT(0);
+    return CURLE_FAILED_INIT;
+  }
+  return cf_dns_create(pcf, data, dns_queries,
+                       hostname, port, transport,
+                       abstract_unix_socket, for_proxy,
+                       complete_resolve, dns);
+}
+
+/* Adds a "resolv" filter at the top of the connection's filter chain.
+ * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will
+ * figure out hostname and port to connect to and start the DNS resolve
+ * on the first connect attempt.
+ * For SECONDARYSOCKET, the `dns` parameter must be given.
+ */
+CURLcode Curl_cf_dns_add(struct Curl_easy *data,
+                         struct connectdata *conn,
+                         int sockindex,
+                         uint8_t dns_queries,
+                         uint8_t transport,
+                         struct Curl_dns_entry *dns)
+{
+  struct Curl_cfilter *cf = NULL;
+  CURLcode result;
+
+  DEBUGASSERT(data);
+  if(sockindex == FIRSTSOCKET)
+    result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns);
+  else if(dns) {
+    result = cf_dns_create(&cf, data, dns_queries,
+                           dns->hostname, dns->port, transport,
+                           FALSE, FALSE, FALSE, dns);
+  }
+  else {
+    DEBUGASSERT(0);
+    result = CURLE_FAILED_INIT;
+  }
+  if(result)
+    goto out;
+  Curl_conn_cf_add(data, conn, sockindex, cf);
+out:
+  return result;
+}
+
+/* Insert a new "resolv" filter directly after `cf`. It will
+ * start a DNS resolve for the given hostnmae and port on the
+ * first connect attempt.
+ * See socks.c on how this is used to make a non-blocking DNS
+ * resolve during connect.
+ */
+CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
+                                  struct Curl_easy *data,
+                                  uint8_t dns_queries,
+                                  const char *hostname,
+                                  uint16_t port,
+                                  uint8_t transport,
+                                  bool complete_resolve)
+{
+  struct Curl_cfilter *cf;
+  CURLcode result;
+
+  result = cf_dns_create(&cf, data, dns_queries,
+                         hostname, port, transport,
+                         FALSE, FALSE, complete_resolve, NULL);
+  if(result)
+    return result;
+
+  Curl_conn_cf_insert_after(cf_at, cf);
+  return CURLE_OK;
+}
+
+/* Return the resolv result from the first "resolv" filter, starting
+ * the given filter `cf` downwards.
+ */
+static CURLcode cf_dns_result(struct Curl_cfilter *cf)
+{
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_dns) {
+      struct cf_dns_ctx *ctx = cf->ctx;
+      if(ctx->dns || ctx->resolv_result)
+        return ctx->resolv_result;
+      return CURLE_AGAIN;
+    }
+  }
+  return CURLE_FAILED_INIT;
+}
+
+/* Return the result of the DNS resolution. Searches for a "resolv"
+ * filter from the top of the filter chain down. Returns
+ * - CURLE_AGAIN when not done yet
+ * - CURLE_OK when DNS was successfully resolved
+ * - CURLR_FAILED_INIT when no resolv filter was found
+ * - error returned by the DNS resolv
+ */
+CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex)
+{
+  return cf_dns_result(conn->cfilter[sockindex]);
+}
+
+static const struct Curl_addrinfo *cf_dns_get_nth_ai(
+  struct Curl_cfilter *cf,
+  const struct Curl_addrinfo *ai,
+  int ai_family, unsigned int index)
+{
+  struct cf_dns_ctx *ctx = cf->ctx;
+  unsigned int i = 0;
+
+  if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A))
+    return NULL;
+#ifdef USE_IPV6
+  if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA))
+    return NULL;
+#endif
+  for(i = 0; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family) {
+      if(i == index)
+        return ai;
+      ++i;
+    }
+  }
+  return NULL;
+}
+
+/* Return the addrinfo at `index` for the given `family` from the
+ * first "resolve" filter underneath `cf`. If the DNS resolving is
+ * not done yet or if no address for the family exists, returns NULL.
+ */
+const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
+                                               struct Curl_easy *data,
+                                               int ai_family,
+                                               unsigned int index)
+{
+  (void)data;
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_dns) {
+      struct cf_dns_ctx *ctx = cf->ctx;
+      if(ctx->resolv_result)
+        return NULL;
+      else if(ctx->dns)
+        return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index);
+      else
+        return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index);
+    }
+  }
+  return NULL;
+}
+
+/* Return the addrinfo at `index` for the given `family` from the
+ * first "resolve" filter at the connection. If the DNS resolving is
+ * not done yet or if no address for the family exists, returns NULL.
+ */
+const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data,
+                                                 int sockindex, int ai_family,
+                                                 unsigned int index)
+{
+  struct connectdata *conn = data->conn;
+  return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index);
+}
+
+#ifdef USE_HTTPSRR
+/* Return the HTTPS-RR info from the first "resolve" filter at the
+ * connection. If the DNS resolving is not done yet or if there
+ * is no HTTPS-RR info, returns NULL.
+ */
+const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data,
+                                                        int sockindex)
+{
+  struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_dns) {
+      struct cf_dns_ctx *ctx = cf->ctx;
+      if(ctx->dns)
+        return ctx->dns->hinfo;
+      else
+        return Curl_resolv_get_https(data, ctx->resolv_id);
+    }
+  }
+  return NULL;
+}
+
+bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex)
+{
+  struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+  for(; cf; cf = cf->next) {
+    if(cf->cft == &Curl_cft_dns) {
+      struct cf_dns_ctx *ctx = cf->ctx;
+      if(ctx->dns)
+        return TRUE;
+      else
+        return Curl_resolv_knows_https(data, ctx->resolv_id);
+    }
+  }
+  return FALSE;
+}
+
+#endif /* USE_HTTPSRR */
diff --git a/Utilities/cmcurl/lib/cf-dns.h b/Utilities/cmcurl/lib/cf-dns.h
new file mode 100644
index 0000000..3c46b1b
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-dns.h
@@ -0,0 +1,71 @@
+#ifndef HEADER_CURL_CF_DNS_H
+#define HEADER_CURL_CF_DNS_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"
+
+struct Curl_easy;
+struct connectdata;
+struct Curl_dns_entry;
+struct Curl_addrinfo;
+
+CURLcode Curl_cf_dns_add(struct Curl_easy *data,
+                         struct connectdata *conn,
+                         int sockindex,
+                         uint8_t dns_queries,
+                         uint8_t transport,
+                         struct Curl_dns_entry *dns);
+
+CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
+                                  struct Curl_easy *data,
+                                  uint8_t dns_queries,
+                                  const char *hostname,
+                                  uint16_t port,
+                                  uint8_t transport,
+                                  bool complete_resolve);
+
+CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex);
+
+const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data,
+                                                 int sockindex,
+                                                 int ai_family,
+                                                 unsigned int index);
+
+const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
+                                               struct Curl_easy *data,
+                                               int ai_family,
+                                               unsigned int index);
+
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data,
+                                                        int sockindex);
+bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex);
+#else
+#define Curl_conn_dns_get_https(a, b)        NULL
+#define Curl_conn_dns_resolved_https(a, b)   TRUE
+#endif
+
+extern struct Curl_cftype Curl_cft_dns;
+
+#endif /* HEADER_CURL_CF_DNS_H */
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c
index 69e050b..3c2c837 100644
--- a/Utilities/cmcurl/lib/cf-h1-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -171,8 +171,8 @@
                                 proxy */
     /* If a proxy-authorization header was used for the proxy, then we should
        make sure that it is not accidentally used for the document request
-       after we have connected. So let's free and clear it here. */
-    Curl_safefree(data->state.aptr.proxyuserpwd);
+       after we have connected. Let's thus free and clear it here. */
+    curlx_safefree(data->req.proxyuserpwd);
     break;
   }
 }
@@ -208,7 +208,7 @@
 
   /* This only happens if we have looped here due to authentication reasons,
      and we do not really use the newly cloned URL here then. Free it. */
-  Curl_safefree(data->req.newurl);
+  curlx_safefree(data->req.newurl);
 
   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
   if(result)
@@ -449,7 +449,7 @@
 
     if(!nread) {
       if(data->set.proxyauth && data->state.authproxy.avail &&
-         data->state.aptr.proxyuserpwd) {
+         data->req.proxyuserpwd) {
         /* proxy auth was requested and there was proxy auth available,
            then deem this as "mere" proxy disconnect */
         ts->close_connection = TRUE;
@@ -465,17 +465,7 @@
 
     if(ts->keepon == KEEPON_IGNORE) {
       /* This means we are currently ignoring a response-body */
-
-      if(ts->cl) {
-        /* A Content-Length based body: count down the counter
-           and make sure to break out of the loop when we are done! */
-        ts->cl--;
-        if(ts->cl <= 0) {
-          ts->keepon = KEEPON_DONE;
-          break;
-        }
-      }
-      else if(ts->chunked_encoding) {
+      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 */
         size_t consumed = 0;
@@ -491,6 +481,15 @@
           ts->keepon = KEEPON_DONE;
         }
       }
+      else if(ts->cl) {
+        /* A Content-Length based body: count down the counter
+           and make sure to break out of the loop when we are done! */
+        ts->cl--;
+        if(ts->cl <= 0) {
+          ts->keepon = KEEPON_DONE;
+          break;
+        }
+      }
       continue;
     }
 
@@ -597,6 +596,8 @@
       /* read what is there */
       CURL_TRC_CF(data, cf, "CONNECT receive");
       result = recv_CONNECT_resp(cf, data, ts, &done);
+      if(result)
+        CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result);
       if(!result)
         result = Curl_pgrsUpdate(data);
       /* error or not complete yet. return for more multi-multi */
@@ -642,10 +643,10 @@
   DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
   if(data->info.httpproxycode / 100 != 2) {
     /* a non-2xx response and we have no next URL to try. */
-    Curl_safefree(data->req.newurl);
+    curlx_safefree(data->req.newurl);
     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
-    return CURLE_RECV_ERROR;
+    return CURLE_COULDNT_CONNECT;
   }
   /* 2xx response, SUCCESS! */
   h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
@@ -689,7 +690,7 @@
   result = H1_CONNECT(cf, data, ts);
   if(result)
     goto out;
-  Curl_safefree(data->state.aptr.proxyuserpwd);
+  curlx_safefree(data->req.proxyuserpwd);
 
 out:
   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c
index 4275e1e..2f8cc41 100644
--- a/Utilities/cmcurl/lib/cf-h2-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -80,7 +80,7 @@
                                    struct tunnel_stream *ts)
 {
   const char *hostname;
-  int port;
+  uint16_t port;
   bool ipv6_ip;
 
   ts->state = H2_TUNNEL_INIT;
@@ -92,7 +92,7 @@
   Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
 
   /* host:port with IPv6 support */
-  ts->authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname,
+  ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname,
                                 ipv6_ip ? "]" : "", port);
   if(!ts->authority)
     return CURLE_OUT_OF_MEMORY;
@@ -105,7 +105,7 @@
   Curl_http_resp_free(ts->resp);
   Curl_bufq_free(&ts->recvbuf);
   Curl_bufq_free(&ts->sendbuf);
-  Curl_safefree(ts->authority);
+  curlx_safefree(ts->authority);
   memset(ts, 0, sizeof(*ts));
   ts->state = H2_TUNNEL_INIT;
 }
@@ -157,8 +157,8 @@
     ts->state = new_state;
     /* If a proxy-authorization header was used for the proxy, then we should
        make sure that it is not accidentally used for the document request
-       after we have connected. So let's free and clear it here. */
-    Curl_safefree(data->state.aptr.proxyuserpwd);
+       after we have connected. Let's thus free and clear it here. */
+    curlx_safefree(data->req.proxyuserpwd);
     break;
   }
 }
@@ -477,7 +477,7 @@
        * 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(CURL_WANT_SEND(data)) {
+      if(CURL_REQ_WANT_SEND(data)) {
         drain_tunnel(cf, data, &ctx->tunnel);
       }
       break;
@@ -512,7 +512,7 @@
     }
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if(CURL_WANT_SEND(data)) {
+    if(CURL_REQ_WANT_SEND(data)) {
       drain_tunnel(cf, data, &ctx->tunnel);
     }
     break;
@@ -627,7 +627,7 @@
   if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf))
     *data_flags = NGHTTP2_DATA_FLAG_EOF;
 
-  CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
+  CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zu",
               ts->stream_id, nread);
   return (nread  > SSIZE_MAX) ?
     NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread;
@@ -675,7 +675,7 @@
   if(stream_id != ctx->tunnel.stream_id)
     return 0;
 
-  CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)",
+  CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %u)",
               stream_id, nghttp2_http2_strerror(error_code), error_code);
   ctx->tunnel.closed = TRUE;
   ctx->tunnel.error = error_code;
@@ -685,15 +685,16 @@
   return 0;
 }
 
-static CURLcode proxy_h2_submit(int32_t *pstream_id,
-                                struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                nghttp2_session *h2,
-                                struct httpreq *req,
-                                const nghttp2_priority_spec *pri_spec,
-                                void *stream_user_data,
-                               nghttp2_data_source_read_callback read_callback,
-                                void *read_ctx)
+static CURLcode proxy_h2_submit(
+  int32_t *pstream_id,
+  struct Curl_cfilter *cf,
+  struct Curl_easy *data,
+  nghttp2_session *h2,
+  struct httpreq *req,
+  const nghttp2_priority_spec *pri_spec,
+  void *stream_user_data,
+  nghttp2_data_source_read_callback read_callback,
+  void *read_ctx)
 {
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
@@ -804,14 +805,14 @@
       return result;
     if(data->req.newurl) {
       /* Indicator that we should try again */
-      Curl_safefree(data->req.newurl);
+      curlx_safefree(data->req.newurl);
       h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data);
       return CURLE_OK;
     }
   }
 
   /* Seems to have failed */
-  return CURLE_RECV_ERROR;
+  return CURLE_COULDNT_CONNECT;
 }
 
 static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
@@ -1158,10 +1159,9 @@
 
   *pnread = 0;
   if(ctx->tunnel.error) {
-    failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32
-          " %s)", ctx->tunnel.stream_id,
-           ctx->tunnel.reset ? "server" : "curl",
-           ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error));
+    failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)",
+          ctx->tunnel.stream_id, ctx->tunnel.reset ? "server" : "curl",
+          ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error));
     return CURLE_RECV_ERROR;
   }
 
@@ -1269,7 +1269,7 @@
   }
 
   result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten);
-  CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten);
+  CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten);
   if(result && (result != CURLE_AGAIN))
     goto out;
 
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c
index 782253e..9ee5e79 100644
--- a/Utilities/cmcurl/lib/cf-haproxy.c
+++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -28,6 +28,7 @@
 #include "urldata.h"
 #include "cfilters.h"
 #include "cf-haproxy.h"
+#include "curl_addrinfo.h"
 #include "curl_trc.h"
 #include "select.h"
 
@@ -61,9 +62,16 @@
 static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
+  /* We fake a client connection report to the upstream server
+   * with the HAProxy protocol, reporting the client's source
+   * and destination IP addresses and ports.
+   * addresses: either the ones used to talk to the upstream
+   *            OR the value supplied by the user
+   * ports: the ports used in the upstream connection */
+  const char *client_source_ip;
+  const char *client_dest_ip;
   struct cf_haproxy_ctx *ctx = cf->ctx;
   CURLcode result;
-  const char *client_ip;
   struct ip_quadruple ipquad;
   bool is_ipv6;
 
@@ -79,15 +87,19 @@
   if(result)
     return result;
 
-  /* Emit the correct prefix for IPv6 */
-  if(data->set.str[STRING_HAPROXY_CLIENT_IP])
-    client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
-  else
-    client_ip = ipquad.local_ip;
+  if(data->set.str[STRING_HAPROXY_CLIENT_IP]) {
+    client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
+    client_dest_ip = client_source_ip;
+    is_ipv6 = !Curl_is_ipv4addr(client_source_ip);
+  }
+  else {
+    client_source_ip = ipquad.local_ip;
+    client_dest_ip = ipquad.remote_ip;
+  }
 
   result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
                           is_ipv6 ? "TCP6" : "TCP4",
-                          client_ip, ipquad.remote_ip,
+                          client_source_ip, client_dest_ip,
                           ipquad.local_port, ipquad.remote_port);
 
 #ifdef USE_UNIX_SOCKETS
@@ -185,7 +197,7 @@
 
 struct Curl_cftype Curl_cft_haproxy = {
   "HAPROXY",
-  CF_TYPE_PROXY,
+  CF_TYPE_PROXY | CF_TYPE_SETUP,
   0,
   cf_haproxy_destroy,
   cf_haproxy_connect,
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c
index cabc6ab..d1d8f51 100644
--- a/Utilities/cmcurl/lib/cf-https-connect.c
+++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -28,6 +28,7 @@
 #include "urldata.h"
 #include "curl_trc.h"
 #include "cfilters.h"
+#include "cf-dns.h"
 #include "connect.h"
 #include "hostip.h"
 #include "httpsrr.h"
@@ -39,6 +40,7 @@
 #include "vquic/vquic.h"
 
 typedef enum {
+  CF_HC_RESOLV,
   CF_HC_INIT,
   CF_HC_CONNECT,
   CF_HC_SUCCESS,
@@ -56,19 +58,17 @@
   BIT(shutdown);
 };
 
-static void cf_hc_baller_reset(struct cf_hc_baller *b,
-                               struct Curl_easy *data)
+static void cf_hc_baller_discard(struct cf_hc_baller *b,
+                                 struct Curl_easy *data)
 {
   if(b->cf) {
     Curl_conn_cf_close(b->cf, data);
     Curl_conn_cf_discard_chain(&b->cf, data);
     b->cf = NULL;
   }
-  b->result = CURLE_OK;
-  b->reply_ms = -1;
 }
 
-static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
+static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b)
 {
   return b->cf && !b->result;
 }
@@ -112,18 +112,46 @@
   cf_hc_state state;
   struct curltime started;  /* when connect started */
   CURLcode result;          /* overall result */
+  CURLcode check_h3_result;
   struct cf_hc_baller ballers[2];
   size_t baller_count;
   timediff_t soft_eyeballs_timeout_ms;
   timediff_t hard_eyeballs_timeout_ms;
+  uint8_t def_transport;
+  BIT(httpsrr_resolved);
+  BIT(checked_h3);
+  BIT(ballers_complete);
 };
 
+static void cf_hc_ctx_close(struct Curl_easy *data,
+                            struct cf_hc_ctx *ctx)
+{
+  if(ctx) {
+    size_t i;
+    for(i = 0; i < ctx->baller_count; ++i)
+      cf_hc_baller_discard(&ctx->ballers[i], data);
+  }
+}
+
+static void cf_hc_ctx_destroy(struct Curl_easy *data,
+                              struct cf_hc_ctx *ctx)
+{
+  if(ctx) {
+    cf_hc_ctx_close(data, ctx);
+    curlx_free(ctx);
+  }
+}
+
 static void cf_hc_baller_assign(struct cf_hc_baller *b,
                                 enum alpnid alpn_id,
                                 uint8_t def_transport)
 {
   b->alpn_id = alpn_id;
   b->transport = def_transport;
+  b->cf = NULL;
+  b->result = CURLE_OK;
+  b->reply_ms = -1;
+  b->shutdown = FALSE;
   switch(b->alpn_id) {
   case ALPN_h3:
     b->name = "h3";
@@ -135,6 +163,9 @@
   case ALPN_h1:
     b->name = "h1";
     break;
+  case ALPN_none:
+    b->name = "no-alpn";
+    break;
   default:
     b->result = CURLE_FAILED_INIT;
     break;
@@ -143,24 +174,14 @@
 
 static void cf_hc_baller_init(struct cf_hc_baller *b,
                               struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              uint8_t transport)
+                              struct Curl_easy *data)
 {
   struct Curl_cfilter *save = cf->next;
 
   cf->next = NULL;
   b->started = *Curl_pgrs_now(data);
-  switch(b->alpn_id) {
-  case ALPN_h3:
-    transport = TRNSPRT_QUIC;
-    break;
-  default:
-    break;
-  }
-
-  if(!b->result)
-    b->result = Curl_cf_setup_insert_after(cf, data, transport,
-                                           CURL_CF_SSL_ENABLE);
+  b->result = Curl_cf_setup_insert_after(cf, data, b->transport,
+                                         CURL_CF_SSL_ENABLE);
   b->cf = cf->next;
   cf->next = save;
 }
@@ -179,59 +200,39 @@
   return b->result;
 }
 
-static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_hc_ctx *ctx = cf->ctx;
-  size_t i;
-
-  if(ctx) {
-    for(i = 0; i < ctx->baller_count; ++i)
-      cf_hc_baller_reset(&ctx->ballers[i], data);
-    ctx->state = CF_HC_INIT;
-    ctx->result = CURLE_OK;
-    ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
-    ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
-  }
-}
-
 static CURLcode baller_connected(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  struct cf_hc_baller *winner)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-  int reply_ms;
-  size_t i;
 
-  DEBUGASSERT(winner->cf);
-  for(i = 0; i < ctx->baller_count; ++i)
-    if(winner != &ctx->ballers[i])
-      cf_hc_baller_reset(&ctx->ballers[i], data);
+  /* Make the winner's connection filter out own sub-filter, check, move,
+   * close all remaining. */
+  if(cf->next) {
+    DEBUGASSERT(0);
+    return CURLE_FAILED_INIT;
+  }
+  if(!winner->cf) {
+    DEBUGASSERT(0);
+    return CURLE_FAILED_INIT;
+  }
 
-  reply_ms = cf_hc_baller_reply_ms(winner, data);
-  if(reply_ms >= 0)
-    CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
-                winner->name,
-                (int)curlx_ptimediff_ms(Curl_pgrs_now(data),
-                                        &winner->started), reply_ms);
-  else
-    CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
-                winner->name, (int)curlx_ptimediff_ms(Curl_pgrs_now(data),
-                                                      &winner->started));
-
-  /* install the winning filter below this one. */
   cf->next = winner->cf;
   winner->cf = NULL;
-  /* whatever errors where reported by ballers, clear our errorbuf */
+  ctx->state = CF_HC_SUCCESS;
+  cf->connected = TRUE;
+
+  cf_hc_ctx_close(data, ctx);
+  /* ballers may have failf()'d, the winner resets it, so our
+   * errorbuf is clean again. */
   Curl_reset_fail(data);
 
 #ifdef USE_NGHTTP2
   {
-    /* Using nghttp2, we add the filter "below" us, so when the conn
-     * closes, we tear it down for a fresh reconnect */
+    /* For a negotiated HTTP/2 connection insert the h2 filter. */
     const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
     if(alpn && !strcmp("h2", alpn)) {
-      result = Curl_http2_switch_at(cf, data);
+      CURLcode result = Curl_http2_switch_at(cf, data);
       if(result) {
         ctx->state = CF_HC_FAILURE;
         ctx->result = result;
@@ -240,63 +241,249 @@
     }
   }
 #endif
-
-  ctx->state = CF_HC_SUCCESS;
-  cf->connected = TRUE;
-  return result;
+  return CURLE_OK;
 }
 
-static bool time_to_start_next(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               size_t idx, struct curltime now)
+static bool time_to_start_baller2(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
   timediff_t elapsed_ms;
-  size_t i;
 
-  if(idx >= ctx->baller_count)
+  if(ctx->baller_count < 2)
     return FALSE;
-  if(cf_hc_baller_has_started(&ctx->ballers[idx]))
+  else if(cf_hc_baller_has_started(&ctx->ballers[1]))
     return FALSE;
-  for(i = 0; i < idx; i++) {
-    if(!ctx->ballers[i].result)
-      break;
-  }
-  if(i == idx) {
-    CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s",
-                ctx->ballers[idx].name);
+  else if(ctx->ballers[0].result) {
+    CURL_TRC_CF(data, cf, "%s baller failed, starting %s",
+                ctx->ballers[0].name, ctx->ballers[1].name);
     return TRUE;
   }
-  elapsed_ms = curlx_ptimediff_ms(&now, &ctx->started);
+
+  elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started);
   if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
-    CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, "
-                "starting %s",
-                ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
+    CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", "
+                "starting %s", ctx->ballers[0].name,
+                ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name);
     return TRUE;
   }
-
-  if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
-    if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
-      CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, "
-                  "%s has not seen any data, starting %s",
-                  ctx->soft_eyeballs_timeout_ms,
-                  ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
+  else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
+    if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) {
+      CURL_TRC_CF(data, cf, "%s has not seen any data after %"
+                  FMT_TIMEDIFF_T "ms, starting %s",
+                  ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms,
+                  ctx->ballers[1].name);
       return TRUE;
     }
-    /* set the effective hard timeout again */
-    Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
-                EXPIRE_ALPN_EYEBALLS);
   }
   return FALSE;
 }
 
+static bool cf_hc_may_h3(struct Curl_cfilter *cf,
+                         struct Curl_easy *data)
+{
+  struct cf_hc_ctx *ctx = cf->ctx;
+  if(!ctx->checked_h3) {
+    ctx->check_h3_result =
+      Curl_conn_may_http3(data, cf->conn, ctx->def_transport);
+    ctx->checked_h3 = TRUE;
+  }
+  return !ctx->check_h3_result;
+}
+
+static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          enum alpnid not_this_one)
+{
+#ifdef USE_HTTPSRR
+  /* Is there an HTTPSRR use its ALPNs here.
+   * We are here after having selected a connection to a host+port and
+   * can no longer change that. Any HTTPSRR advice for other hosts and ports
+   * we need to ignore. */
+  const struct Curl_https_rrinfo *rr;
+  size_t i;
+
+  /* Do we have HTTPS-RR information? */
+  rr = Curl_conn_dns_get_https(data, cf->sockindex);
+
+  /* We do not support `rr->no_def_alpn`. */
+  if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) {
+    for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) {
+      enum alpnid alpn_rr = (enum alpnid)rr->alpns[i];
+      if(alpn_rr == not_this_one) /* don't want this one */
+        continue;
+      switch(alpn_rr) {
+      case ALPN_h3:
+        if((data->state.http_neg.allowed & CURL_HTTP_V3x) &&
+           cf_hc_may_h3(cf, data)) {
+          return alpn_rr;
+        }
+        break;
+      case ALPN_h2:
+        if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
+          return alpn_rr;
+        }
+        break;
+      case ALPN_h1:
+        if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
+          return alpn_rr;
+        }
+        break;
+      default: /* ignore */
+        break;
+      }
+    }
+  }
+#else
+  (void)cf;
+  (void)data;
+  (void)not_this_one;
+#endif
+  return ALPN_none;
+}
+
+static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data,
+                                       enum alpnid not_this_one)
+{
+  if((data->state.http_neg.preferred & data->state.http_neg.allowed)) {
+    switch(data->state.http_neg.preferred) {
+    case CURL_HTTP_V3x:
+      if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one))
+        return ALPN_h3;
+      break;
+    case CURL_HTTP_V2x:
+      if(ALPN_h2 != not_this_one)
+        return ALPN_h2;
+      break;
+    case CURL_HTTP_V1x:
+      /* If we are trying h2 already, h1 is already used as fallback */
+      if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one))
+        return ALPN_h1;
+      break;
+    default:
+      break;
+    }
+  }
+  return ALPN_none;
+}
+
+static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        http_majors choices,
+                                        enum alpnid not_this_one)
+{
+  /* When told to not try h2, we also do not try h1 and vice versa */
+  bool allow_h1_or_h2 = (not_this_one != ALPN_h1) &&
+                        (not_this_one != ALPN_h2);
+  if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) &&
+     cf_hc_may_h3(cf, data)) {
+    return ALPN_h3;
+  }
+  if(allow_h1_or_h2 && (choices & CURL_HTTP_V2x)) {
+    return ALPN_h2;
+  }
+  if(allow_h1_or_h2 && (choices & CURL_HTTP_V1x)) {
+    return ALPN_h1;
+  }
+  return ALPN_none;
+}
+
+static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data)
+{
+  struct cf_hc_ctx *ctx = cf->ctx;
+  enum alpnid alpn1 = ALPN_none;
+  VERBOSE(const char *source = "HTTPS-RR");
+
+  DEBUGASSERT(cf->conn->bits.tls_enable_alpn);
+
+  alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none);
+  if(alpn1 == ALPN_none) {
+    /* preference is configured and allowed, can we use it? */
+    VERBOSE(source = "preferred version");
+    alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none);
+  }
+  if(alpn1 == ALPN_none) {
+    VERBOSE(source = "wanted versions");
+    alpn1 = cf_hc_get_first_alpn(cf, data,
+                                 data->state.http_neg.wanted,
+                                 ALPN_none);
+  }
+  if(alpn1 == ALPN_none) {
+    VERBOSE(source = "allowed versions");
+    alpn1 = cf_hc_get_first_alpn(cf, data,
+                                 data->state.http_neg.allowed,
+                                 ALPN_none);
+  }
+
+  if(alpn1 == ALPN_none) {
+    /* None of the wanted/allowed HTTP versions could be chosen */
+    if(ctx->check_h3_result) {
+      CURL_TRC_CF(data, cf, "unable to use HTTP/3");
+      return ctx->check_h3_result;
+    }
+    CURL_TRC_CF(data, cf, "unable to select HTTP version");
+    return CURLE_FAILED_INIT;
+  }
+
+  cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport);
+  ctx->baller_count = 1;
+  CURL_TRC_CF(data, cf, "1st attempt uses %s from %s",
+              ctx->ballers[0].name, source);
+
+  switch(alpn1) {
+  case ALPN_h1:
+    /* We really want h1, switch off h2 to make it disappear in ALPN */
+    data->state.http_neg.wanted &= (uint8_t)~CURL_HTTP_V2x;
+    break;
+  default:
+    break;
+  }
+
+  return CURLE_OK;
+}
+
+static void cf_hc_set_baller2(struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  struct cf_hc_ctx *ctx = cf->ctx;
+  enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id;
+  VERBOSE(const char *source = "HTTPS-RR");
+
+  if(ctx->ballers_complete)
+    return; /* already done */
+  if(!ctx->httpsrr_resolved)
+    return; /* HTTPS-RR pending */
+
+  alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1);
+  if(alpn2 == ALPN_none) {
+    /* preference is configured and allowed, can we use it? */
+    VERBOSE(source = "preferred version");
+    alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1);
+  }
+  if(alpn2 == ALPN_none) {
+    VERBOSE(source = "wanted versions");
+    alpn2 = cf_hc_get_first_alpn(cf, data,
+                                 data->state.http_neg.wanted,
+                                 alpn1);
+  }
+
+  if(alpn2 != ALPN_none) {
+    cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport);
+    ctx->baller_count = 2;
+    CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s",
+                ctx->ballers[1].name, source);
+  }
+  ctx->ballers_complete = TRUE;
+}
+
 static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               bool *done)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
-  size_t i, failed_ballers;
 
   if(cf->connected) {
     *done = TRUE;
@@ -304,24 +491,45 @@
   }
 
   *done = FALSE;
+
+  if(!ctx->httpsrr_resolved) {
+    ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex);
+#ifdef DEBUGBUILD
+    if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) {
+      CURL_TRC_CF(data, cf, "awaiting HTTPS-RR");
+      return CURLE_OK;
+    }
+#endif
+  }
+
   switch(ctx->state) {
+  case CF_HC_RESOLV:
+    ctx->state = CF_HC_INIT;
+    FALLTHROUGH();
+
   case CF_HC_INIT:
     DEBUGASSERT(!cf->next);
-    for(i = 0; i < ctx->baller_count; i++)
-      DEBUGASSERT(!ctx->ballers[i].cf);
     CURL_TRC_CF(data, cf, "connect, init");
+    result = cf_hc_set_baller1(cf, data);
+    if(result) {
+      ctx->result = result;
+      ctx->state = CF_HC_FAILURE;
+      goto out;
+    }
+    cf_hc_set_baller2(cf, data);
     ctx->started = *Curl_pgrs_now(data);
-    cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport);
-    if(ctx->baller_count > 1) {
+    cf_hc_baller_init(&ctx->ballers[0], cf, data);
+    if((ctx->baller_count > 1) || !ctx->ballers_complete) {
       Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
-      CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T
-                  "ms", ctx->soft_eyeballs_timeout_ms);
     }
     ctx->state = CF_HC_CONNECT;
     FALLTHROUGH();
 
   case CF_HC_CONNECT:
-    if(cf_hc_baller_is_active(&ctx->ballers[0])) {
+    if(!ctx->ballers_complete)
+      cf_hc_set_baller2(cf, data);
+
+    if(cf_hc_baller_is_connecting(&ctx->ballers[0])) {
       result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
       if(!result && *done) {
         result = baller_connected(cf, data, &ctx->ballers[0]);
@@ -329,12 +537,11 @@
       }
     }
 
-    if(time_to_start_next(cf, data, 1, *Curl_pgrs_now(data))) {
-      cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport);
+    if(time_to_start_baller2(cf, data)) {
+      cf_hc_baller_init(&ctx->ballers[1], cf, data);
     }
 
-    if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
-      CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
+    if(cf_hc_baller_is_connecting(&ctx->ballers[1])) {
       result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
       if(!result && *done) {
         result = baller_connected(cf, data, &ctx->ballers[1]);
@@ -342,21 +549,12 @@
       }
     }
 
-    failed_ballers = 0;
-    for(i = 0; i < ctx->baller_count; i++) {
-      if(ctx->ballers[i].result)
-        ++failed_ballers;
-    }
-
-    if(failed_ballers == ctx->baller_count) {
+    if(ctx->ballers[0].result &&
+       (ctx->ballers[1].result ||
+        (ctx->ballers_complete && (ctx->baller_count < 2)))) {
       /* all have failed. we give up */
       CURL_TRC_CF(data, cf, "connect, all attempts failed");
-      for(i = 0; i < ctx->baller_count; i++) {
-        if(ctx->ballers[i].result) {
-          result = ctx->ballers[i].result;
-          break;
-        }
-      }
+      ctx->result = result = ctx->ballers[0].result;
       ctx->state = CF_HC_FAILURE;
       goto out;
     }
@@ -400,7 +598,7 @@
   for(i = 0; i < ctx->baller_count; i++) {
     struct cf_hc_baller *b = &ctx->ballers[i];
     bool bdone = FALSE;
-    if(!cf_hc_baller_is_active(b) || b->shutdown)
+    if(!cf_hc_baller_is_connecting(b) || b->shutdown)
       continue;
     b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone);
     if(b->result || bdone)
@@ -433,11 +631,11 @@
 
     for(i = 0; (i < ctx->baller_count) && !result; i++) {
       struct cf_hc_baller *b = &ctx->ballers[i];
-      if(!cf_hc_baller_is_active(b))
+      if(!cf_hc_baller_is_connecting(b))
         continue;
       result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
     }
-    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n);
   }
   return result;
 }
@@ -536,7 +734,7 @@
 static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   CURL_TRC_CF(data, cf, "close");
-  cf_hc_reset(cf, data);
+  cf_hc_ctx_close(data, cf->ctx);
   cf->connected = FALSE;
 
   if(cf->next) {
@@ -550,13 +748,12 @@
   struct cf_hc_ctx *ctx = cf->ctx;
 
   CURL_TRC_CF(data, cf, "destroy");
-  cf_hc_reset(cf, data);
-  Curl_safefree(ctx);
+  cf_hc_ctx_destroy(data, ctx);
 }
 
 struct Curl_cftype Curl_cft_http_connect = {
   "HTTPS-CONNECT",
-  0,
+  CF_TYPE_SETUP | CF_TYPE_HTTPSRR,
   CURL_LOG_LVL_NONE,
   cf_hc_destroy,
   cf_hc_connect,
@@ -574,59 +771,42 @@
 
 static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
-                             enum alpnid *alpnids, size_t alpn_count,
                              uint8_t def_transport)
 {
   struct Curl_cfilter *cf = NULL;
   struct cf_hc_ctx *ctx;
   CURLcode result = CURLE_OK;
-  size_t i;
 
   ctx = curlx_calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
-
-  DEBUGASSERT(alpnids);
-  DEBUGASSERT(alpn_count);
-  DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
-  if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
-    failf(data, "https-connect filter create with unsupported %zu ALPN ids",
-          alpn_count);
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-
-  for(i = 0; i < alpn_count; ++i)
-    cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport);
-  for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
-    ctx->ballers[i].alpn_id = ALPN_none;
-  ctx->baller_count = alpn_count;
+  ctx->def_transport = def_transport;
+  ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
+  ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
 
   result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
   if(result)
     goto out;
   ctx = NULL;
-  cf_hc_reset(cf, data);
 
 out:
   *pcf = result ? NULL : cf;
-  curlx_free(ctx);
+  cf_hc_ctx_destroy(data, ctx);
   return result;
 }
 
-static CURLcode cf_http_connect_add(struct Curl_easy *data,
-                                    struct connectdata *conn,
-                                    int sockindex,
-                                    enum alpnid *alpn_ids, size_t alpn_count,
-                                    uint8_t def_transport)
+static CURLcode cf_hc_add(struct Curl_easy *data,
+                          struct connectdata *conn,
+                          int sockindex,
+                          uint8_t def_transport)
 {
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport);
+  result = cf_hc_create(&cf, data, def_transport);
   if(result)
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -634,138 +814,19 @@
   return result;
 }
 
-static bool cf_https_alpns_contain(enum alpnid id,
-                                   enum alpnid *list, size_t len)
-{
-  size_t i;
-  for(i = 0; i < len; ++i) {
-    if(id == list[i])
-      return TRUE;
-  }
-  return FALSE;
-}
-
 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
                              struct connectdata *conn,
                              int sockindex)
 {
-  enum alpnid alpn_ids[2];
-  size_t alpn_count = 0;
   CURLcode result = CURLE_OK;
-  struct Curl_cfilter cf_fake, *cf = NULL;
 
-  (void)sockindex;
-  /* we want to log for the filter before we create it, fake it. */
-  memset(&cf_fake, 0, sizeof(cf_fake));
-  cf_fake.cft = &Curl_cft_http_connect;
-  cf = &cf_fake;
+  DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS);
 
-  if(conn->bits.tls_enable_alpn) {
-#ifdef USE_HTTPSRR
-    /* Is there an HTTPSRR use its ALPNs here.
-     * We are here after having selected a connection to a host+port and
-     * can no longer change that. Any HTTPSRR advice for other hosts and ports
-     * we need to ignore. */
-    struct Curl_dns_entry *dns = data->state.dns[sockindex];
-    struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL;
-    if(rr && !rr->no_def_alpn &&  /* ALPNs are defaults */
-       (!rr->target ||      /* for same host */
-        !rr->target[0] ||
-        (rr->target[0] == '.' &&
-         !rr->target[1])) &&
-       (rr->port < 0 ||    /* for same port */
-        rr->port == conn->remote_port)) {
-      size_t i;
-      for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
-                 alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
-        enum alpnid alpn = rr->alpns[i];
-        if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
-          continue;
-        switch(alpn) {
-        case ALPN_h3:
-          if(Curl_conn_may_http3(data, conn, conn->transport_wanted))
-            break;  /* not possible */
-          if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
-            CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
-            alpn_ids[alpn_count++] = alpn;
-          }
-          break;
-        case ALPN_h2:
-          if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
-            CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
-            alpn_ids[alpn_count++] = alpn;
-          }
-          break;
-        case ALPN_h1:
-          if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
-            CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
-            alpn_ids[alpn_count++] = alpn;
-          }
-          break;
-        default: /* ignore */
-          break;
-        }
-      }
-    }
-#endif
+  if((conn->scheme->protocol != CURLPROTO_HTTPS) ||
+     !conn->bits.tls_enable_alpn)
+     goto out;
 
-    /* Add preferred HTTP version ALPN first */
-    if(data->state.http_neg.preferred &&
-       (alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
-       (data->state.http_neg.preferred & data->state.http_neg.allowed)) {
-      enum alpnid alpn_pref = ALPN_none;
-      switch(data->state.http_neg.preferred) {
-      case CURL_HTTP_V3x:
-        if(!Curl_conn_may_http3(data, conn, conn->transport_wanted))
-          alpn_pref = ALPN_h3;
-        break;
-      case CURL_HTTP_V2x:
-        alpn_pref = ALPN_h2;
-        break;
-      case CURL_HTTP_V1x:
-        alpn_pref = ALPN_h1;
-        break;
-      default:
-        break;
-      }
-      if(alpn_pref &&
-         !cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) {
-        alpn_ids[alpn_count++] = alpn_pref;
-      }
-    }
-
-    if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
-       (data->state.http_neg.wanted & CURL_HTTP_V3x) &&
-       !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
-      result = Curl_conn_may_http3(data, conn, conn->transport_wanted);
-      if(!result) {
-        CURL_TRC_CF(data, cf, "adding wanted h3");
-        alpn_ids[alpn_count++] = ALPN_h3;
-      }
-      else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
-        goto out; /* only h3 allowed, not possible, error out */
-    }
-    if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
-       (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
-       !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
-      CURL_TRC_CF(data, cf, "adding wanted h2");
-      alpn_ids[alpn_count++] = ALPN_h2;
-    }
-    else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
-            (data->state.http_neg.wanted & CURL_HTTP_V1x) &&
-            !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
-      CURL_TRC_CF(data, cf, "adding wanted h1");
-      alpn_ids[alpn_count++] = ALPN_h1;
-    }
-  }
-
-  /* If we identified ALPNs to use, install our filter. Otherwise,
-   * install nothing, so our call will use a default connect setup. */
-  if(alpn_count) {
-    result = cf_http_connect_add(data, conn, sockindex,
-                                 alpn_ids, alpn_count,
-                                 conn->transport_wanted);
-  }
+  result = cf_hc_add(data, conn, sockindex, conn->transport_wanted);
 
 out:
   return result;
diff --git a/Utilities/cmcurl/lib/cf-https-connect.h b/Utilities/cmcurl/lib/cf-https-connect.h
index df29c8d..3160c03 100644
--- a/Utilities/cmcurl/lib/cf-https-connect.h
+++ b/Utilities/cmcurl/lib/cf-https-connect.h
@@ -31,15 +31,9 @@
 struct Curl_easy;
 struct connectdata;
 struct Curl_cftype;
-struct Curl_dns_entry;
 
 extern struct Curl_cftype Curl_cft_http_connect;
 
-CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
-                                  struct connectdata *conn,
-                                  int sockindex,
-                                  bool try_h3, bool try_h21);
-
 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
                              struct connectdata *conn,
                              int sockindex);
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.c b/Utilities/cmcurl/lib/cf-ip-happy.c
index dfbf4ae..f67273e 100644
--- a/Utilities/cmcurl/lib/cf-ip-happy.c
+++ b/Utilities/cmcurl/lib/cf-ip-happy.c
@@ -49,6 +49,7 @@
 #include "urldata.h"
 #include "connect.h"
 #include "cfilters.h"
+#include "cf-dns.h"
 #include "cf-ip-happy.h"
 #include "curl_addrinfo.h"
 #include "curl_trc.h"
@@ -91,9 +92,11 @@
 }
 
 #ifdef UNITTESTS
-/* used by unit2600.c */
-void Curl_debug_set_transport_provider(uint8_t transport,
-                                       cf_ip_connect_create *cf_create)
+/* @unittest 2600 */
+UNITTEST void debug_set_transport_provider(
+  uint8_t transport, cf_ip_connect_create *cf_create);
+UNITTEST void debug_set_transport_provider(
+  uint8_t transport, cf_ip_connect_create *cf_create)
 {
   size_t i;
   for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
@@ -106,59 +109,46 @@
 #endif /* UNITTESTS */
 
 struct cf_ai_iter {
-  const struct Curl_addrinfo *head;
-  const struct Curl_addrinfo *last;
+  struct Curl_cfilter *cf;
   int ai_family;
-  int n;
+  unsigned int n;
 };
 
 static void cf_ai_iter_init(struct cf_ai_iter *iter,
-                            const struct Curl_addrinfo *list,
+                            struct Curl_cfilter *cf,
                             int ai_family)
 {
-  iter->head = list;
+  iter->cf = cf;
   iter->ai_family = ai_family;
-  iter->last = NULL;
-  iter->n = -1;
+  iter->n = 0;
 }
 
-static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter)
+static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter,
+                                                   struct Curl_easy *data)
 {
   const struct Curl_addrinfo *addr;
-  if(iter->n < 0) {
+
+  if(!iter->cf)
+    return NULL;
+
+  addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex,
+                              iter->ai_family, iter->n);
+  if(addr)
     iter->n++;
-    for(addr = iter->head; addr; addr = addr->ai_next) {
-      if(addr->ai_family == iter->ai_family)
-        break;
-    }
-    iter->last = addr;
-  }
-  else if(iter->last) {
-    iter->n++;
-    for(addr = iter->last->ai_next; addr; addr = addr->ai_next) {
-      if(addr->ai_family == iter->ai_family)
-        break;
-    }
-    iter->last = addr;
-  }
-  return iter->last;
+  return addr;
 }
 
-static bool cf_ai_iter_has_more(struct cf_ai_iter *iter)
+static bool cf_ai_iter_has_more(struct cf_ai_iter *iter,
+                                struct Curl_easy *data)
 {
-  const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next :
-    ((iter->n < 0) ? iter->head : NULL);
-  while(addr) {
-    if(addr->ai_family == iter->ai_family)
-      return TRUE;
-    addr = addr->ai_next;
-  }
-  return FALSE;
+  return (iter->cf &&
+          !!Curl_conn_dns_get_ai(data, iter->cf->sockindex,
+                                 iter->ai_family, iter->n));
 }
 
 struct cf_ip_attempt {
   struct cf_ip_attempt *next;
-  const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
+  struct Curl_sockaddr_ex addr;
   struct Curl_cfilter *cf;           /* current sub-cfilter connecting */
   cf_ip_connect_create *cf_create;
   struct curltime started;           /* start of current attempt */
@@ -185,7 +175,7 @@
 static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
                                   struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  const struct Curl_addrinfo *addr,
+                                  struct Curl_sockaddr_ex *addr,
                                   int ai_family,
                                   uint8_t transport,
                                   cf_ip_connect_create *cf_create)
@@ -199,14 +189,14 @@
   if(!a)
     return CURLE_OUT_OF_MEMORY;
 
-  a->addr = addr;
+  a->addr = *addr;
   a->ai_family = ai_family;
   a->transport = transport;
   a->result = CURLE_OK;
   a->cf_create = cf_create;
   *pa = a;
 
-  result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport);
+  result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
   if(result)
     goto out;
 
@@ -238,8 +228,12 @@
         a->connected = TRUE;
       }
     }
-    else if(a->result == CURLE_WEIRD_SERVER_REPLY)
-      a->inconclusive = TRUE;
+    else {
+      if(a->result == CURLE_WEIRD_SERVER_REPLY)
+        a->inconclusive = TRUE;
+      if(a->cf)
+        Curl_conn_cf_discard_chain(&a->cf, data);
+    }
   }
   return a->result;
 }
@@ -256,6 +250,7 @@
   struct curltime last_attempt_started;
   timediff_t attempt_delay_ms;
   int last_attempt_ai_family;
+  uint32_t max_concurrent;
   uint8_t transport;
 };
 
@@ -263,19 +258,18 @@
                                       struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
-  struct Curl_cfilter *cf_prev = a->cf;
   struct Curl_cfilter *wcf;
   CURLcode result;
 
-  /* When restarting, we tear down and existing filter *after* we
-   * started up the new one. This gives us a new socket number and
-   * probably a new local port. Which may prevent confusion. */
+  if(a->cf)
+    Curl_conn_cf_discard_chain(&a->cf, data);
+
   a->result = CURLE_OK;
   a->connected = FALSE;
   a->inconclusive = FALSE;
   a->cf = NULL;
 
-  result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport);
+  result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
   if(!result) {
     bool dummy;
     /* the new filter might have sub-filters */
@@ -285,8 +279,6 @@
     }
     a->result = cf_ip_attempt_connect(a, data, &dummy);
   }
-  if(cf_prev)
-    Curl_conn_cf_discard_chain(&cf_prev, data);
   return result;
 }
 
@@ -304,54 +296,76 @@
   bs->winner = NULL;
 }
 
-static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
-                                   const struct Curl_addrinfo *addr_list,
+static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs,
+                                   struct Curl_cfilter *cf,
                                    cf_ip_connect_create *cf_create,
                                    uint8_t transport,
-                                   timediff_t attempt_delay_ms)
+                                   timediff_t attempt_delay_ms,
+                                   uint32_t max_concurrent)
 {
   memset(bs, 0, sizeof(*bs));
   bs->cf_create = cf_create;
   bs->transport = transport;
   bs->attempt_delay_ms = attempt_delay_ms;
+  bs->max_concurrent = max_concurrent;
   bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */
 
   if(transport == TRNSPRT_UNIX) {
 #ifdef USE_UNIX_SOCKETS
-    cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX);
+    cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX);
 #else
     return CURLE_UNSUPPORTED_PROTOCOL;
 #endif
   }
   else { /* TCP/UDP/QUIC */
 #ifdef USE_IPV6
-    if(ip_version == CURL_IPRESOLVE_V6)
-      cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET);
-    else
-      cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
-
-    if(ip_version == CURL_IPRESOLVE_V4)
-      cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6);
-    else
-      cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6);
-#else
-    (void)ip_version;
-    cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
+    cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6);
 #endif
+    cf_ai_iter_init(&bs->addr_iter, cf, AF_INET);
   }
   return CURLE_OK;
 }
 
+static void cf_ip_ballers_prune(struct cf_ip_ballers *bs,
+                                struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                uint32_t max_concurrent)
+{
+  struct cf_ip_attempt *a = NULL, **panchor;
+  uint32_t ongoing = 0;
+
+  for(a = bs->running; a; a = a->next) {
+    if(!a->result && !a->connected)
+      ++ongoing;
+  }
+
+  panchor = &bs->running;
+  while(*panchor && (ongoing > max_concurrent)) {
+    a = *panchor;
+    if(!a->result && !a->connected) {
+      *panchor = a->next;
+      a->next = NULL;
+      cf_ip_attempt_free(a, data);
+      --ongoing;
+      CURL_TRC_CF(data, cf, "discarding oldest attempt to keep limit");
+    }
+    else {
+      panchor = &a->next;
+    }
+  }
+}
+
 static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
                                   struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
+                                  bool dns_resolved,
                                   bool *connected)
 {
   CURLcode result = CURLE_OK;
   struct cf_ip_attempt *a = NULL, **panchor;
   bool do_more;
   timediff_t next_expire_ms;
-  int inconclusive, ongoing;
+  uint32_t inconclusive, ongoing;
   VERBOSE(int i);
 
   if(bs->winner)
@@ -389,7 +403,7 @@
   }
   if(bs->running)
     CURL_TRC_CF(data, cf, "checked connect attempts: "
-                "%d ongoing, %d inconclusive", ongoing, inconclusive);
+                "%u ongoing, %u inconclusive", ongoing, inconclusive);
 
   /* no attempt connected yet, start another one? */
   if(!ongoing) {
@@ -398,10 +412,10 @@
     do_more = TRUE;
   }
   else {
-    bool more_possible = cf_ai_iter_has_more(&bs->addr_iter);
+    bool more_possible = cf_ai_iter_has_more(&bs->addr_iter, data);
 #ifdef USE_IPV6
     if(!more_possible)
-      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
+      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data);
 #endif
     do_more = more_possible &&
       (curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started) >=
@@ -414,27 +428,42 @@
   if(do_more) {
     /* start the next attempt if there is another ip address to try.
      * Alternate between address families when possible. */
-    const struct Curl_addrinfo *addr = NULL;
+    const struct Curl_addrinfo *ai = NULL;
     int ai_family = 0;
+    CURL_TRC_CF(data, cf, "want to do more");
 #ifdef USE_IPV6
     if((bs->last_attempt_ai_family == AF_INET) ||
-       !cf_ai_iter_has_more(&bs->addr_iter)) {
-      addr = cf_ai_iter_next(&bs->ipv6_iter);
+       !cf_ai_iter_has_more(&bs->addr_iter, data)) {
+      ai = cf_ai_iter_next(&bs->ipv6_iter, data);
       ai_family = bs->ipv6_iter.ai_family;
+      CURL_TRC_CF(data, cf, "check for next AAAA address: %s",
+                  ai ? "found" : "none");
     }
 #endif
-    if(!addr) {
-      addr = cf_ai_iter_next(&bs->addr_iter);
+    if(!ai) {
+      ai = cf_ai_iter_next(&bs->addr_iter, data);
       ai_family = bs->addr_iter.ai_family;
+      CURL_TRC_CF(data, cf, "check for next A address: %s",
+                  ai ? "found" : "none");
     }
     /* We are (re-)starting attempts. We are not interested in
      * keeping old failure information. The new attempt will either
      * succeed or persist new failure. */
     Curl_reset_fail(data);
 
-    if(addr) {  /* try another address */
-      result = cf_ip_attempt_new(&a, cf, data, addr, ai_family,
-                                bs->transport, bs->cf_create);
+    if(ai) {  /* try another address */
+      struct Curl_sockaddr_ex addr;
+
+      /* Discard oldest to make room for new attempt */
+      if(bs->max_concurrent)
+        cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1);
+
+      result = Curl_socket_addr_from_ai(&addr, ai, bs->transport);
+      if(result)
+        goto out;
+
+      result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family,
+                                 bs->transport, bs->cf_create);
       CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
                   bs->running ? "next" : "first",
                   (ai_family == AF_INET) ? "4" : "6", result);
@@ -483,7 +512,7 @@
       /* attempt timeout for restart has not expired yet */
       goto out;
     }
-    else if(!ongoing) {
+    else if(!ongoing && dns_resolved) {
       /* no more addresses, no inconclusive attempts */
       CURL_TRC_CF(data, cf, "no more attempts to try");
       result = CURLE_COULDNT_CONNECT;
@@ -509,10 +538,10 @@
       return CURLE_OPERATION_TIMEDOUT;
     }
 
-    more_possible = cf_ai_iter_has_more(&bs->addr_iter);
+    more_possible = cf_ai_iter_has_more(&bs->addr_iter, data);
 #ifdef USE_IPV6
     if(!more_possible)
-      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
+      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data);
 #endif
     if(more_possible) {
       timediff_t expire_ms, elapsed_ms;
@@ -543,7 +572,7 @@
   *done = TRUE;
   for(a = bs->running; a; a = a->next) {
     bool bdone = FALSE;
-    if(a->shutdown)
+    if(a->shutdown || !a->cf)
       continue;
     a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone);
     if(a->result || bdone)
@@ -576,7 +605,7 @@
   for(a = bs->running; a; a = a->next) {
     if(a->result)
       continue;
-    if(a->cf->cft->has_data_pending(a->cf, data))
+    if(a->cf && a->cf->cft->has_data_pending(a->cf, data))
       return TRUE;
   }
   return FALSE;
@@ -592,7 +621,7 @@
   memset(&tmax, 0, sizeof(tmax));
   for(a = bs->running; a; a = a->next) {
     memset(&t, 0, sizeof(t));
-    if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) {
+    if(a->cf && !a->cf->cft->query(a->cf, data, query, NULL, &t)) {
       if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0)
         tmax = t;
     }
@@ -607,8 +636,8 @@
   struct cf_ip_attempt *a;
 
   for(a = bs->running; a; a = a->next) {
-    if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS,
-                          &breply_ms, NULL)) {
+    if(a->cf && !a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS,
+                                   &breply_ms, NULL)) {
       if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
         reply_ms = breply_ms;
     }
@@ -628,6 +657,7 @@
   cf_connect_state state;
   struct cf_ip_ballers ballers;
   struct curltime started;
+  BIT(dns_resolved);
 };
 
 static CURLcode is_connected(struct Curl_cfilter *cf,
@@ -638,12 +668,12 @@
   struct connectdata *conn = cf->conn;
   CURLcode result;
 
-  result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected);
+  result = cf_ip_ballers_run(&ctx->ballers, cf, data,
+                             (bool)ctx->dns_resolved, connected);
 
   if(!result)
     return CURLE_OK;
-
-  {
+  else {
     const char *hostname, *proxy_name = NULL;
     char viamsg[160];
 #ifndef CURL_DISABLE_PROXY
@@ -662,14 +692,14 @@
     else
 #endif
     {
-      int port;
+      uint16_t port;
       if(cf->sockindex == SECONDARYSOCKET)
         port = conn->secondary_port;
       else if(cf->conn->bits.conn_to_port)
         port = conn->conn_to_port;
       else
         port = conn->remote_port;
-      curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port);
+      curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port);
     }
 
     failf(data, "Failed to connect to %s %s %s%s%safter "
@@ -681,28 +711,22 @@
           curlx_ptimediff_ms(Curl_pgrs_now(data),
                              &data->progress.t_startsingle),
           curl_easy_strerror(result));
-  }
 
 #ifdef SOCKETIMEDOUT
-  if(SOCKETIMEDOUT == data->state.os_errno)
-    result = CURLE_OPERATION_TIMEDOUT;
+    if(SOCKETIMEDOUT == data->state.os_errno)
+      result = CURLE_OPERATION_TIMEDOUT;
 #endif
 
-  return result;
+    return result;
+  }
 }
 
-/*
- * Connect to the given host with timeout, proxy or remote does not matter.
- * There might be more than one IP address to try out.
- */
-static CURLcode start_connect(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
+#define IP_HE_MAX_CONCURRENT_ATTEMPTS     6
+
+static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data)
 {
   struct cf_ip_happy_ctx *ctx = cf->ctx;
-  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
-
-  if(!dns)
-    return CURLE_FAILED_INIT;
 
   if(Curl_timeleft_ms(data) < 0) {
     /* a precaution, no need to continue if time already is up */
@@ -712,9 +736,10 @@
 
   CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
   ctx->started = *Curl_pgrs_now(data);
-  return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
-                            dns->addr, ctx->cf_create, ctx->transport,
-                            data->set.happy_eyeballs_timeout);
+  return cf_ip_ballers_init(&ctx->ballers, cf,
+                            ctx->cf_create, ctx->transport,
+                            data->set.happy_eyeballs_timeout,
+                            IP_HE_MAX_CONCURRENT_ATTEMPTS);
 }
 
 static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
@@ -727,6 +752,12 @@
   cf_ip_ballers_clear(cf, data, &ctx->ballers);
 }
 
+static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx)
+{
+  if(ctx)
+    curlx_free(ctx);
+}
+
 static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
                                      struct Curl_easy *data,
                                      bool *done)
@@ -754,7 +785,7 @@
 
   if(!cf->connected) {
     result = cf_ip_ballers_pollset(&ctx->ballers, data, ps);
-    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n);
   }
   return result;
 }
@@ -766,6 +797,10 @@
   struct cf_ip_happy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
 
+  /* -Werror=null-dereference finds false positives suddenly. */
+  if(!data)
+    return CURLE_FAILED_INIT;
+
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
@@ -774,13 +809,24 @@
   DEBUGASSERT(ctx);
   *done = FALSE;
 
+  if(!ctx->dns_resolved) {
+    result = Curl_conn_dns_result(cf->conn, cf->sockindex);
+    if(!result)
+      ctx->dns_resolved = TRUE;
+    else if(result == CURLE_AGAIN) {
+      result = CURLE_OK;
+    }
+    else /* real error */
+      goto out;
+  }
+
   switch(ctx->state) {
   case SCFST_INIT:
     DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
     DEBUGASSERT(!cf->connected);
-    result = start_connect(cf, data);
+    result = cf_ip_happy_init(cf, data);
     if(result)
-      return result;
+      goto out;
     ctx->state = SCFST_WAITING;
     FALLTHROUGH();
   case SCFST_WAITING:
@@ -808,8 +854,7 @@
         bool is_ipv6;
         if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
           const char *host;
-          int port;
-          Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
+          Curl_conn_get_current_host(data, cf->sockindex, &host, NULL);
           CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
                       host, ipquad.remote_ip, ipquad.remote_port);
         }
@@ -822,6 +867,7 @@
     *done = TRUE;
     break;
   }
+out:
   return result;
 }
 
@@ -895,14 +941,13 @@
   CURL_TRC_CF(data, cf, "destroy");
   if(ctx) {
     cf_ip_happy_ctx_clear(cf, data);
+    cf_ip_happy_ctx_destroy(ctx);
   }
-  /* release any resources held in state */
-  Curl_safefree(ctx);
 }
 
 struct Curl_cftype Curl_cft_ip_happy = {
   "HAPPY-EYEBALLS",
-  0,
+  CF_TYPE_SETUP,
   CURL_LOG_LVL_NONE,
   cf_ip_happy_destroy,
   cf_ip_happy_connect,
@@ -952,8 +997,8 @@
 
 out:
   if(result) {
-    Curl_safefree(*pcf);
-    curlx_free(ctx);
+    curlx_safefree(*pcf);
+    cf_ip_happy_ctx_destroy(ctx);
   }
   return result;
 }
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.h b/Utilities/cmcurl/lib/cf-ip-happy.h
index 71f2122..547ee4b 100644
--- a/Utilities/cmcurl/lib/cf-ip-happy.h
+++ b/Utilities/cmcurl/lib/cf-ip-happy.h
@@ -25,6 +25,12 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
+struct connectdata;
+struct Curl_addrinfo;
+struct Curl_cfilter;
+struct Curl_easy;
+struct Curl_sockaddr_ex;
+
 /**
  * Create a cfilter for making an "ip" connection to the
  * given address, using parameters from `conn`. The "ip" connection
@@ -39,7 +45,7 @@
 typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
                                       struct Curl_easy *data,
                                       struct connectdata *conn,
-                                      const struct Curl_addrinfo *ai,
+                                      struct Curl_sockaddr_ex *addr,
                                       uint8_t transport);
 
 CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
@@ -48,9 +54,4 @@
 
 extern struct Curl_cftype Curl_cft_ip_happy;
 
-#ifdef UNITTESTS
-void Curl_debug_set_transport_provider(uint8_t transport,
-                                       cf_ip_connect_create *cf_create);
-#endif
-
 #endif /* HEADER_CURL_IP_HAPPY_H */
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
index 2ebbe4d..b99bcde 100644
--- a/Utilities/cmcurl/lib/cf-socket.c
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -71,6 +71,7 @@
 #include "curlx/strdup.h"
 #include "system_win32.h"
 #include "curlx/nonblock.h"
+#include "curlx/strcopy.h"
 #include "curlx/version_win32.h"
 #include "curlx/strerr.h"
 #include "curlx/strparse.h"
@@ -100,9 +101,9 @@
   defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \
   defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
 #if defined(USE_WINSOCK) || \
-   (defined(__sun) && !defined(TCP_KEEPIDLE)) || \
-   (defined(__DragonFly__) && __DragonFly_version < 500702) || \
-   (defined(_WIN32) && !defined(TCP_KEEPIDLE))
+  (defined(__sun) && !defined(TCP_KEEPIDLE)) || \
+  (defined(__DragonFly__) && __DragonFly_version < 500702) || \
+  (defined(_WIN32) && !defined(TCP_KEEPIDLE))
 /* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299
  * use millisecond units. */
 #define KEEPALIVE_FACTOR(x) ((x) *= 1000)
@@ -236,8 +237,15 @@
      * Note that the consequent probes will not be sent
      * at equal intervals on Solaris, but will be sent
      * using the exponential backoff algorithm. */
-    optval = curlx_sltosi(data->set.tcp_keepcnt) *
-             curlx_sltosi(data->set.tcp_keepintvl);
+    {
+      int keepcnt = curlx_sltosi(data->set.tcp_keepcnt);
+      int keepintvl = curlx_sltosi(data->set.tcp_keepintvl);
+
+      if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt))
+        optval = INT_MAX;
+      else
+        optval = keepcnt * keepintvl;
+    }
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
                   (void *)&optval, sizeof(optval)) < 0) {
@@ -258,42 +266,30 @@
 }
 
 /**
- * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
- * set the transport used.
+ * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with
+ * transport determining socktype and protocol.
  */
-static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
-                                 const struct Curl_addrinfo *ai,
-                                 uint8_t transport)
+CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
+                                  const struct Curl_addrinfo *ai,
+                                  uint8_t transport)
 {
   /*
-   * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr
-   * structure with enough space available to directly hold any
-   * protocol-specific address structures. The variable declared here will be
-   * used to pass / receive data to/from the fopensocket callback if this has
-   * been set, before that, it is initialized from parameters.
+   * The Curl_sockaddr_ex structure is libcurl's external API
+   * curl_sockaddr structure with enough space available to directly hold
+   * any protocol-specific address structures. The variable declared here
+   * will be used to pass / receive data to/from the fopensocket callback
+   * if this has been set, before that, it is initialized from parameters.
    */
-  dest->family = ai->ai_family;
-  switch(transport) {
-  case TRNSPRT_TCP:
-    dest->socktype = SOCK_STREAM;
-    dest->protocol = IPPROTO_TCP;
-    break;
-  case TRNSPRT_UNIX:
-    dest->socktype = SOCK_STREAM;
-    dest->protocol = IPPROTO_IP;
-    break;
-  default: /* UDP and QUIC */
-    dest->socktype = SOCK_DGRAM;
-    dest->protocol = IPPROTO_UDP;
-    break;
-  }
-  dest->addrlen = (unsigned int)ai->ai_addrlen;
+  addr->family = ai->ai_family;
+  addr->socktype = Curl_socktype_for_transport(transport);
+  addr->protocol = Curl_protocol_for_transport(transport);
+  addr->addrlen = (unsigned int)ai->ai_addrlen;
 
-  DEBUGASSERT(dest->addrlen <= sizeof(dest->curl_sa_addrbuf));
-  if(dest->addrlen > sizeof(dest->curl_sa_addrbuf))
+  DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf));
+  if(addr->addrlen > sizeof(addr->curl_sa_addrbuf))
     return CURLE_TOO_LARGE;
 
-  memcpy(&dest->curl_sa_addrbuf, ai->ai_addr, dest->addrlen);
+  memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen);
   return CURLE_OK;
 }
 
@@ -405,7 +401,7 @@
     /* if the caller does not want info back, use a local temp copy */
     addr = &dummy;
 
-  result = sock_assign_addr(addr, ai, transport);
+  result = Curl_socket_addr_from_ai(addr, ai, transport);
   if(result)
     return result;
 
@@ -530,7 +526,8 @@
 
 #ifndef CURL_DISABLE_BINDLOCAL
 static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
-                          curl_socket_t sockfd, int af, unsigned int scope)
+                          curl_socket_t sockfd, int af, unsigned int scope,
+                          uint8_t transport)
 {
   struct Curl_sockaddr_storage sa;
   struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
@@ -641,21 +638,21 @@
        * of the connection. The resolve functions should really be changed
        * to take a type parameter instead.
        */
-      int ip_version = (af == AF_INET) ?
-                       CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
+      uint8_t dns_queries = (af == AF_INET) ?
+                            CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
 #ifdef USE_IPV6
       if(af == AF_INET6)
-        ip_version = CURL_IPRESOLVE_V6;
+        dns_queries = CURL_DNSQ_AAAA;
 #endif
 
-      (void)Curl_resolv_blocking(data, host, 80, ip_version, &h);
+      (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
       if(h) {
         int h_af = h->addr->ai_family;
         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
         Curl_printable_address(h->addr, myhost, sizeof(myhost));
         infof(data, "Name '%s' family %i resolved to '%s' family %i",
               host, af, myhost, h_af);
-        Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
+        Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
         if(af != h_af) {
           /* bad IP version combo, signal the caller to try another address
              family if available */
@@ -688,8 +685,8 @@
           if(scope_ptr) {
             /* The "myhost" string either comes from Curl_if2ip or from
                Curl_printable_address. The latter returns only numeric scope
-               IDs and the former returns none at all. So the scope ID, if
-               present, is known to be numeric */
+               IDs and the former returns none at all. Making the scope ID,
+               if present, known to be numeric */
             curl_off_t scope_id;
             if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
                                 &scope_id, UINT_MAX))
@@ -892,18 +889,13 @@
 };
 
 static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
-                                   const struct Curl_addrinfo *ai,
+                                   struct Curl_sockaddr_ex *addr,
                                    uint8_t transport)
 {
-  CURLcode result;
-
   memset(ctx, 0, sizeof(*ctx));
   ctx->sock = CURL_SOCKET_BAD;
   ctx->transport = transport;
-
-  result = sock_assign_addr(&ctx->addr, ai, transport);
-  if(result)
-    return result;
+  ctx->addr = *addr;
 
 #ifdef DEBUGBUILD
   {
@@ -934,7 +926,7 @@
   }
 #endif
 
-  return result;
+  return CURLE_OK;
 }
 
 static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -1143,7 +1135,8 @@
 #endif
     ) {
     result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
-                       Curl_ipv6_scope(&ctx->addr.curl_sa_addr));
+                       Curl_ipv6_scope(&ctx->addr.curl_sa_addr),
+                       ctx->transport);
     if(result) {
       if(result == CURLE_UNSUPPORTED_PROTOCOL) {
         /* The address family is not supported on this interface.
@@ -1313,26 +1306,31 @@
     }
   }
   else if(rc & CURL_CSELECT_ERR) {
+    CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock);
     (void)verifyconnect(ctx->sock, &ctx->error);
     result = CURLE_COULDNT_CONNECT;
   }
 
 out:
   if(result) {
+    VERBOSE(char buffer[STRERROR_LEN]);
+    set_local_ip(cf, data);
     if(ctx->error) {
-      VERBOSE(char buffer[STRERROR_LEN]);
-      set_local_ip(cf, data);
       data->state.os_errno = ctx->error;
       SET_SOCKERRNO(ctx->error);
-      infof(data, "connect to %s port %u from %s port %d failed: %s",
-            ctx->ip.remote_ip, ctx->ip.remote_port,
-            ctx->ip.local_ip, ctx->ip.local_port,
-            curlx_strerror(ctx->error, buffer, sizeof(buffer)));
+      VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer)));
+    }
+    else {
+      VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed")));
     }
     if(ctx->sock != CURL_SOCKET_BAD) {
       socket_close(data, cf->conn, TRUE, ctx->sock);
       ctx->sock = CURL_SOCKET_BAD;
     }
+    infof(data, "connect to %s port %u from %s port %d failed: %s",
+          ctx->ip.remote_ip, ctx->ip.remote_port,
+          ctx->ip.local_ip, ctx->ip.local_port,
+          curlx_strerror(ctx->error, buffer, sizeof(buffer)));
     *done = FALSE;
   }
   return result;
@@ -1713,7 +1711,7 @@
 CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
                             struct Curl_easy *data,
                             struct connectdata *conn,
-                            const struct Curl_addrinfo *ai,
+                            struct Curl_sockaddr_ex *addr,
                             uint8_t transport)
 {
   struct cf_socket_ctx *ctx = NULL;
@@ -1723,7 +1721,7 @@
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_TCP);
-  if(!ai) {
+  if(!addr) {
     result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto out;
   }
@@ -1734,7 +1732,7 @@
     goto out;
   }
 
-  result = cf_socket_ctx_init(ctx, ai, transport);
+  result = cf_socket_ctx_init(ctx, addr, transport);
   if(result)
     goto out;
 
@@ -1743,8 +1741,8 @@
 out:
   *pcf = (!result) ? cf : NULL;
   if(result) {
-    Curl_safefree(cf);
-    Curl_safefree(ctx);
+    curlx_safefree(cf);
+    curlx_safefree(ctx);
   }
 
   return result;
@@ -1879,7 +1877,7 @@
 CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
                             struct Curl_easy *data,
                             struct connectdata *conn,
-                            const struct Curl_addrinfo *ai,
+                            struct Curl_sockaddr_ex *addr,
                             uint8_t transport)
 {
   struct cf_socket_ctx *ctx = NULL;
@@ -1895,7 +1893,7 @@
     goto out;
   }
 
-  result = cf_socket_ctx_init(ctx, ai, transport);
+  result = cf_socket_ctx_init(ctx, addr, transport);
   if(result)
     goto out;
 
@@ -1904,8 +1902,8 @@
 out:
   *pcf = (!result) ? cf : NULL;
   if(result) {
-    Curl_safefree(cf);
-    Curl_safefree(ctx);
+    curlx_safefree(cf);
+    curlx_safefree(ctx);
   }
 
   return result;
@@ -1933,7 +1931,7 @@
 CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
                              struct connectdata *conn,
-                             const struct Curl_addrinfo *ai,
+                             struct Curl_sockaddr_ex *addr,
                              uint8_t transport)
 {
   struct cf_socket_ctx *ctx = NULL;
@@ -1949,7 +1947,7 @@
     goto out;
   }
 
-  result = cf_socket_ctx_init(ctx, ai, transport);
+  result = cf_socket_ctx_init(ctx, addr, transport);
   if(result)
     goto out;
 
@@ -1958,8 +1956,8 @@
 out:
   *pcf = (!result) ? cf : NULL;
   if(result) {
-    Curl_safefree(cf);
-    Curl_safefree(ctx);
+    curlx_safefree(cf);
+    curlx_safefree(ctx);
   }
 
   return result;
@@ -2195,8 +2193,8 @@
 
 out:
   if(result) {
-    Curl_safefree(cf);
-    Curl_safefree(ctx);
+    curlx_safefree(cf);
+    curlx_safefree(ctx);
   }
   return result;
 }
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h
index 014f5f7..40c001c 100644
--- a/Utilities/cmcurl/lib/cf-socket.h
+++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -60,6 +60,10 @@
 CURLcode Curl_parse_interface(const char *input,
                               char **dev, char **iface, char **host);
 
+CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
+                                  const struct Curl_addrinfo *ai,
+                                  uint8_t transport);
+
 /*
  * Create a socket based on info from 'conn' and 'ai'.
  *
@@ -91,7 +95,7 @@
 CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
                             struct Curl_easy *data,
                             struct connectdata *conn,
-                            const struct Curl_addrinfo *ai,
+                            struct Curl_sockaddr_ex *addr,
                             uint8_t transport);
 
 /**
@@ -104,7 +108,7 @@
 CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
                             struct Curl_easy *data,
                             struct connectdata *conn,
-                            const struct Curl_addrinfo *ai,
+                            struct Curl_sockaddr_ex *addr,
                             uint8_t transport);
 
 /**
@@ -117,7 +121,7 @@
 CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
                              struct connectdata *conn,
-                             const struct Curl_addrinfo *ai,
+                             struct Curl_sockaddr_ex *addr,
                              uint8_t transport);
 
 /**
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index 27b0306..6d7d8ef 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -34,8 +34,9 @@
 #include "curlx/strparse.h"
 
 #ifdef UNITTESTS
-/* used by unit2600.c */
-void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+/* @unittest 2600 */
+UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
+UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   cf->connected = FALSE;
   if(cf->next)
@@ -115,6 +116,37 @@
     CURLE_UNKNOWN_OPTION;
 }
 
+#ifdef CURLVERBOSE
+static void conn_trc_filters(struct Curl_easy *data,
+                                  int sockindex,
+                                  const char *info)
+{
+  if(CURL_TRC_M_is_verbose(data) && data->conn) {
+    struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+
+    if(cf) {
+      struct dynbuf msg;
+      CURLcode result = CURLE_OK;
+
+      curlx_dyn_init(&msg, 1024);
+      result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex);
+      for(; cf && !result; cf = cf->next) {
+        result = curlx_dyn_addf(&msg, "[%s%s]",
+                                cf->connected ? "" : "!", cf->cft->name);
+      }
+      if(!result)
+        CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg));
+      else
+        CURL_TRC_M(data, "%s [%d] error %d tracing chain",
+                   info, sockindex, result);
+      curlx_dyn_free(&msg);
+    }
+    else
+      CURL_TRC_M(data, "%s [%d][-]", info, sockindex);
+  }
+}
+#endif /* CURLVERBOSE */
+
 void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
                                 struct Curl_easy *data)
 {
@@ -488,6 +520,24 @@
   }
 }
 
+static void conn_remove_setup_filters(struct Curl_easy *data,
+                                      int sockindex)
+{
+  struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex];
+  while(*anchor) {
+    struct Curl_cfilter *cf = *anchor;
+    if(cf->connected && (cf->cft->flags & CF_TYPE_SETUP)) {
+      *anchor = cf->next;
+      cf->next = NULL;
+      CURL_TRC_CF(data, cf, "removing connected setup filter");
+      cf->cft->destroy(cf, data);
+      curlx_free(cf);
+    }
+    else
+      anchor = &cf->next;
+  }
+}
+
 CURLcode Curl_conn_connect(struct Curl_easy *data,
                            int sockindex,
                            bool blocking,
@@ -505,6 +555,11 @@
   if(!CONN_SOCK_IDX_VALID(sockindex))
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
+  if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
   cf = data->conn->cfilter[sockindex];
   if(!cf) {
     *done = FALSE;
@@ -536,10 +591,14 @@
       conn_report_connect_stats(cf, data);
       data->conn->keepalive = *Curl_pgrs_now(data);
       VERBOSE(result = cf_verboseconnect(data, cf));
+      VERBOSE(conn_trc_filters(data, sockindex, "connected"));
+      conn_remove_setup_filters(data, sockindex);
+      VERBOSE(conn_trc_filters(data, sockindex, "reduced to"));
       goto out;
     }
     else if(result) {
       CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result);
+      VERBOSE(conn_trc_filters(data, sockindex, "failed to connect"));
       conn_report_connect_stats(cf, data);
       goto out;
     }
@@ -649,14 +708,16 @@
 
 bool Curl_conn_get_ssl_info(struct Curl_easy *data,
                             struct connectdata *conn, int sockindex,
+                            int query,
                             struct curl_tlssessioninfo *info)
 {
   if(!CONN_SOCK_IDX_VALID(sockindex))
     return FALSE;
   if(Curl_conn_is_ssl(conn, sockindex)) {
     struct Curl_cfilter *cf = conn->cfilter[sockindex];
-    CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO,
-                               NULL, (void *)info) : CURLE_UNKNOWN_OPTION;
+    CURLcode result = cf ?
+      cf->cft->query(cf, data, query, NULL, (void *)info) :
+      CURLE_UNKNOWN_OPTION;
     return !result;
   }
   return FALSE;
@@ -697,6 +758,41 @@
   return Curl_conn_cf_get_transport(cf, data);
 }
 
+int Curl_socktype_for_transport(uint8_t transport)
+{
+  switch(transport) {
+  case TRNSPRT_TCP:
+    return SOCK_STREAM;
+  case TRNSPRT_UNIX:
+    return SOCK_STREAM;
+  default: /* UDP and QUIC */
+    return SOCK_DGRAM;
+  }
+}
+
+int Curl_protocol_for_transport(uint8_t transport)
+{
+  switch(transport) {
+  case TRNSPRT_TCP:
+    return IPPROTO_TCP;
+  case TRNSPRT_UNIX:
+    return IPPROTO_IP;
+  default: /* UDP and QUIC */
+    return IPPROTO_UDP;
+  }
+}
+
+bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf,
+                                struct Curl_easy *data)
+{
+  (void)data;
+  for(; cf; cf = cf->next) {
+    if(cf->cft->flags & CF_TYPE_HTTPSRR)
+      return TRUE;
+  }
+  return FALSE;
+}
+
 const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
                                           struct connectdata *conn)
 {
@@ -800,12 +896,19 @@
   return result;
 }
 
+/*
+ * Return values:
+ *   -1 = error
+ *    0 = timeout
+ *    N = number of structures with non zero revent fields
+ */
 int Curl_conn_cf_poll(struct Curl_cfilter *cf,
                       struct Curl_easy *data,
                       timediff_t timeout_ms)
 {
   struct easy_pollset ps;
-  int result;
+  int rc;
+  CURLcode result;
 
   DEBUGASSERT(cf);
   DEBUGASSERT(data);
@@ -814,20 +917,24 @@
 
   result = Curl_conn_cf_adjust_pollset(cf, data, &ps);
   if(!result)
-    result = Curl_pollset_poll(data, &ps, timeout_ms);
+    rc = Curl_pollset_poll(data, &ps, timeout_ms);
+  else
+    rc = -1;
   Curl_pollset_cleanup(&ps);
-  return result;
+  return rc;
 }
 
 void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
                                 const char **phost, int *pport)
 {
   struct Curl_cfilter *cf, *cf_proxy = NULL;
+  int portarg = -1;
 
   if(!data->conn) {
     DEBUGASSERT(0);
     *phost = "";
-    *pport = -1;
+    if(pport)
+      *pport = -1;
     return;
   }
 
@@ -843,12 +950,14 @@
    * to an interim host and any authentication or other things apply
    * to this interim host and port. */
   if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT,
-                                       pport, CURL_UNCONST(phost))) {
+                                       &portarg, CURL_UNCONST(phost))) {
     /* Everything connected or query unsuccessful, the overall
      * connection's destination is the answer */
     *phost = data->conn->host.name;
-    *pport = data->conn->remote_port;
+    portarg = data->conn->remote_port;
   }
+  if(pport)
+    *pport = portarg;
 }
 
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
@@ -1029,7 +1138,7 @@
   result = cf ? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
                                &n, NULL) : CURLE_UNKNOWN_OPTION;
   /* If no filter answered the query, the default is a non-multiplexed
-   * connection with limit 1. Otherwise, the the query may return 0
+   * connection with limit 1. Otherwise, the query may return 0
    * for connections that are in shutdown, e.g. server HTTP/2 GOAWAY. */
   return (result || n < 0) ? 1 : (size_t)n;
 }
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h
index c754634..ac56737 100644
--- a/Utilities/cmcurl/lib/cfilters.h
+++ b/Utilities/cmcurl/lib/cfilters.h
@@ -199,12 +199,17 @@
  * CF_TYPE_MULTIPLEX:  provides multiplexing of easy handles
  * CF_TYPE_PROXY       provides proxying
  * CF_TYPE_HTTP        implement a version of the HTTP protocol
+ * CF_TYPE_SETUP       filter is only needed for connection setup and
+ *                     can be removed once connected
+ * CF_TYPE_HTTPSRR     filter that wants HTTPS-RR information
  */
 #define CF_TYPE_IP_CONNECT  (1 << 0)
 #define CF_TYPE_SSL         (1 << 1)
 #define CF_TYPE_MULTIPLEX   (1 << 2)
 #define CF_TYPE_PROXY       (1 << 3)
 #define CF_TYPE_HTTP        (1 << 4)
+#define CF_TYPE_SETUP       (1 << 5)
+#define CF_TYPE_HTTPSRR     (1 << 6)
 
 /* A connection filter type, e.g. specific implementation. */
 struct Curl_cftype {
@@ -345,9 +350,16 @@
 unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
                                          struct Curl_easy *data);
 
+int Curl_socktype_for_transport(uint8_t transport);
+int Curl_protocol_for_transport(uint8_t transport);
+
 const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
                                              struct Curl_easy *data);
 
+/* The filter (or one of its sub-filters) wants HTTPS-RR information. */
+bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf,
+                                struct Curl_easy *data);
+
 #define CURL_CF_SSL_DEFAULT  (-1)
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_ENABLE   1
@@ -388,12 +400,13 @@
 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
 
 /*
- * Fill `info` with information about the TLS instance securing
- * the connection when available, otherwise e.g. when
- * Curl_conn_is_ssl() is FALSE, return FALSE.
+ * Fill `info` with information about the TLS instance securing the connection
+ * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return
+ * FALSE. 'query' should be CF_QUERY_SSL_INFO or CF_QUERY_SSL_CTX_INFO.
  */
 bool Curl_conn_get_ssl_info(struct Curl_easy *data,
                             struct connectdata *conn, int sockindex,
+                            int query,
                             struct curl_tlssessioninfo *info);
 
 CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
@@ -429,7 +442,7 @@
 /**
  * Shutdown the connection at `sockindex` non-blocking, using timeout
  * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS.
- * Will return CURLE_OK and *done == FALSE if not finished.
+ * Return CURLE_OK and *done == FALSE if not finished.
  */
 CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done);
 
@@ -565,10 +578,6 @@
                               struct connectdata *conn,
                               int sockindex);
 
-#ifdef UNITTESTS
-void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
-#endif
-
 /**
  * Get the remote hostname and port that the connection is currently
  * talking to (or will talk to).
@@ -604,14 +613,14 @@
 
 /*
  * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
- * Will return CURLE_AGAIN iff blocked on receiving.
+ * Return CURLE_AGAIN iff blocked on receiving.
  */
 CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
                         char *buf, size_t len, size_t *pnread);
 
 /*
  * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
- * Will return CURLE_AGAIN iff blocked on sending.
+ * Return CURLE_AGAIN iff blocked on sending.
  */
 CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
                         const void *buf, size_t len, bool eos,
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index a865959..33fb68b 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -206,7 +206,7 @@
 
   /* treat the connection as aborted in CONNECT_ONLY situations, we do
    * not know what the APP did with it. */
-  if(conn->connect_only)
+  if(conn->bits.connect_only)
     aborted = TRUE;
   conn->bits.aborted = aborted;
 
@@ -354,7 +354,7 @@
     for(curr = Curl_llist_head(&bundle->conns); curr;
         curr = Curl_node_next(curr)) {
       conn = Curl_node_elem(curr);
-      if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
+      if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only)
         continue;
       /* Set higher score for the age passed since the connection was used */
       score = curlx_ptimediff_ms(pnow, &conn->lastused);
@@ -375,7 +375,7 @@
   size_t dest_limit = 0;
   size_t total_limit = 0;
   size_t shutdowns;
-  int result = CPOOL_LIMIT_OK;
+  int res = CPOOL_LIMIT_OK;
 
   if(!cpool)
     return CPOOL_LIMIT_OK;
@@ -425,7 +425,7 @@
       shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
     }
     if((live + shutdowns) >= dest_limit) {
-      result = CPOOL_LIMIT_DEST;
+      res = CPOOL_LIMIT_DEST;
       goto out;
     }
   }
@@ -453,14 +453,14 @@
       shutdowns = Curl_cshutdn_count(cpool->idata);
     }
     if((cpool->num_conn + shutdowns) >= total_limit) {
-      result = CPOOL_LIMIT_TOTAL;
+      res = CPOOL_LIMIT_TOTAL;
       goto out;
     }
   }
 
 out:
   CPOOL_UNLOCK(cpool, cpool->idata);
-  return result;
+  return res;
 }
 
 CURLcode Curl_cpool_add(struct Curl_easy *data,
@@ -558,7 +558,7 @@
   struct cpool *cpool = cpool_get_instance(data);
   bool kept = TRUE;
 
-  if(!data)
+  if(!data || !data->multi)
     return kept;
 
   if(!data->multi->maxconnects) {
@@ -600,7 +600,7 @@
 {
   struct cpool *cpool = cpool_get_instance(data);
   struct cpool_bundle *bundle;
-  bool result = FALSE;
+  bool found = FALSE;
 
   DEBUGASSERT(cpool);
   DEBUGASSERT(conn_cb);
@@ -619,17 +619,17 @@
       curr = Curl_node_next(curr);
 
       if(conn_cb(conn, userdata)) {
-        result = TRUE;
+        found = TRUE;
         break;
       }
     }
   }
 
   if(done_cb) {
-    result = done_cb(result, userdata);
+    found = done_cb(userdata);
   }
   CPOOL_UNLOCK(cpool, data);
-  return result;
+  return found;
 }
 
 void Curl_conn_terminate(struct Curl_easy *data,
@@ -665,7 +665,7 @@
 
   /* treat the connection as aborted in CONNECT_ONLY situations,
    * so no graceful shutdown is attempted. */
-  if(conn->connect_only)
+  if(conn->bits.connect_only)
     aborted = TRUE;
 
   if(data->multi) {
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h
index d8626ae..7cee4d4 100644
--- a/Utilities/cmcurl/lib/conncache.h
+++ b/Utilities/cmcurl/lib/conncache.h
@@ -84,8 +84,8 @@
 
 /**
  * Return if the pool has reached its configured limits for adding
- * the given connection. Will try to discard the oldest, idle
- * connections to make space.
+ * the given connection. Try to discard the oldest, idle connections
+ * to make space.
  */
 #define CPOOL_LIMIT_OK     0
 #define CPOOL_LIMIT_DEST   1
@@ -98,7 +98,7 @@
                                       void *userdata);
 
 /* Act on the result of the find, may override it. */
-typedef bool Curl_cpool_done_match_cb(bool result, void *userdata);
+typedef bool Curl_cpool_done_match_cb(void *userdata);
 
 /**
  * Find a connection in the pool matching `destination`.
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index 78c9370..b13d496 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -54,6 +54,7 @@
 #include "strerror.h"
 #include "cfilters.h"
 #include "connect.h"
+#include "cf-dns.h"
 #include "cf-haproxy.h"
 #include "cf-https-connect.h"
 #include "cf-ip-happy.h"
@@ -96,14 +97,17 @@
 #endif
 
 /*
- * Curl_timeleft_ms() returns the amount of milliseconds left allowed for the
+ * timeleft_now_ms() returns the amount of milliseconds left allowed for the
  * transfer/connection. If the value is 0, there is no timeout (ie there is
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
- * @unittest: 1303
+ *
+ * @unittest 1303
  */
-timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
-                                const struct curltime *pnow)
+UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data,
+                                    const struct curltime *pnow);
+UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data,
+                                    const struct curltime *pnow)
 {
   timediff_t timeleft_ms = 0;
   timediff_t ctimeleft_ms = 0;
@@ -138,7 +142,7 @@
 
 timediff_t Curl_timeleft_ms(struct Curl_easy *data)
 {
-  return Curl_timeleft_now_ms(data, Curl_pgrs_now(data));
+  return timeleft_now_ms(data, Curl_pgrs_now(data));
 }
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
@@ -343,7 +347,6 @@
 {
   struct cf_setup_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
-  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
 
   if(cf->connected) {
     *done = TRUE;
@@ -352,8 +355,6 @@
 
   /* connect current sub-chain */
 connect_sub_chain:
-  if(!dns)
-    return CURLE_FAILED_INIT;
 
   if(cf->next && !cf->next->connected) {
     result = Curl_conn_cf_connect(cf->next, data, done);
@@ -373,7 +374,27 @@
   /* sub-chain connected, do we need to add more? */
 #ifndef CURL_DISABLE_PROXY
   if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
-    result = Curl_cf_socks_proxy_insert_after(cf, data);
+    /* for the secondary socket (FTP), use the "connect to host"
+     * but ignore the "connect to port" (use the secondary port)
+     */
+    const char *hostname =
+      cf->conn->bits.httpproxy ?
+      cf->conn->http_proxy.host.name :
+      cf->conn->bits.conn_to_host ?
+      cf->conn->conn_to_host.name :
+      cf->sockindex == SECONDARYSOCKET ?
+      cf->conn->secondaryhostname : cf->conn->host.name;
+    uint16_t port =
+      cf->conn->bits.httpproxy ? cf->conn->http_proxy.port :
+      cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port :
+      cf->conn->bits.conn_to_port ? cf->conn->conn_to_port :
+      cf->conn->remote_port;
+    const char *user = cf->conn->socks_proxy.user;
+    const char *passwd = cf->conn->socks_proxy.passwd;
+
+    result = Curl_cf_socks_proxy_insert_after(
+      cf, data, hostname, port, cf->conn->ip_version,
+      cf->conn->socks_proxy.proxytype, user, passwd);
     if(result)
       return result;
     ctx->state = CF_SETUP_CNNCT_SOCKS;
@@ -408,7 +429,7 @@
 #ifndef CURL_DISABLE_PROXY
     if(data->set.haproxyprotocol) {
       if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
-        failf(data, "haproxy protocol not support with SSL "
+        failf(data, "haproxy protocol not supported with SSL "
               "encryption in place (QUIC?)");
         return CURLE_UNSUPPORTED_PROTOCOL;
       }
@@ -464,12 +485,12 @@
   struct cf_setup_ctx *ctx = cf->ctx;
 
   CURL_TRC_CF(data, cf, "destroy");
-  Curl_safefree(ctx);
+  curlx_safefree(ctx);
 }
 
 struct Curl_cftype Curl_cft_setup = {
   "SETUP",
-  0,
+  CF_TYPE_SETUP,
   CURL_LOG_LVL_NONE,
   cf_setup_destroy,
   cf_setup_connect,
@@ -559,13 +580,11 @@
                          int ssl_mode)
 {
   CURLcode result = CURLE_OK;
+  uint8_t dns_queries;
 
   DEBUGASSERT(data);
   DEBUGASSERT(conn->scheme);
-  DEBUGASSERT(dns);
-
-  Curl_resolv_unlink(data, &data->state.dns[sockindex]);
-  data->state.dns[sockindex] = dns;
+  DEBUGASSERT(!conn->cfilter[sockindex]);
 
 #ifndef CURL_DISABLE_HTTP
   if(!conn->cfilter[sockindex] &&
@@ -585,13 +604,34 @@
       goto out;
   }
 
+  dns_queries = Curl_resolv_dns_queries(data, conn->ip_version);
+#ifdef USE_HTTPSRR
+  if(sockindex == FIRSTSOCKET)
+    dns_queries |= CURL_DNSQ_HTTPS;
+#endif
+  result = Curl_cf_dns_add(data, conn, sockindex, dns_queries,
+                           conn->transport_wanted, dns);
   DEBUGASSERT(conn->cfilter[sockindex]);
 out:
-  if(result)
-    Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   return result;
 }
 
+#ifdef USE_UNIX_SOCKETS
+const char *Curl_conn_get_unix_path(struct connectdata *conn)
+{
+  const char *unix_path = conn->unix_domain_socket;
+
+#ifndef CURL_DISABLE_PROXY
+  if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name &&
+     !strncmp(UNIX_SOCKET_PREFIX "/",
+              conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+    unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+#endif
+
+  return unix_path;
+}
+#endif /* USE_UNIX_SOCKETS */
+
 void Curl_conn_set_multiplex(struct connectdata *conn)
 {
   if(!conn->bits.multiplex) {
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h
index 071b432..40f1c9c 100644
--- a/Utilities/cmcurl/lib/connect.h
+++ b/Utilities/cmcurl/lib/connect.h
@@ -25,6 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
+#include "hostip.h"
 #include "curlx/timeval.h"
 
 struct Curl_dns_entry;
@@ -37,8 +38,6 @@
 /* generic function that returns how much time there is left to run, according
    to the timeouts set */
 timediff_t Curl_timeleft_ms(struct Curl_easy *data);
-timediff_t Curl_timeleft_now_ms(struct Curl_easy *data,
-                                const struct curltime *pnow);
 
 #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
 
@@ -127,6 +126,15 @@
 /* Set conn to allow multiplexing. */
 void Curl_conn_set_multiplex(struct connectdata *conn);
 
+#ifdef USE_UNIX_SOCKETS
+#ifndef CURL_DISABLE_PROXY
+#define UNIX_SOCKET_PREFIX "localhost"
+#endif
+const char *Curl_conn_get_unix_path(struct connectdata *conn);
+#else
+#define Curl_conn_get_unix_path(c)      NULL
+#endif
+
 extern struct Curl_cftype Curl_cft_setup;
 
 #endif /* HEADER_CURL_CONNECT_H */
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index b8dbd7d..dea9a0f 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -31,13 +31,13 @@
 #endif
 
 #ifdef HAVE_BROTLI
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 /* Ignore -Wvla warnings in brotli headers */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wvla"
 #endif
 #include <brotli/decode.h>
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic pop
 #endif
 #endif
@@ -751,7 +751,7 @@
       }
 
       if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
-        failf(data, "Reject response due to more than %u content encodings",
+        failf(data, "Reject response due to more than %d content encodings",
               MAX_ENCODE_STACK);
         return CURLE_BAD_CONTENT_ENCODING;
       }
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index 4d53cc4..0f822ea 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -347,7 +347,7 @@
 
   cookie-octet    = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
 
-  But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
+  Yet, Firefox and Chrome as of June 2022 accept space, comma and double-quotes
   fine. The prime reason for filtering out control bytes is that some HTTP
   servers return 400 for requests that contain such.
 */
@@ -423,18 +423,17 @@
 
 /* this function return errors on OOM etc, not on plain cookie format
    problems */
-static CURLcode
-parse_cookie_header(struct Curl_easy *data,
-                    struct Cookie *co,
-                    struct CookieInfo *ci,
-                    bool *okay, /* if the cookie was fine */
-                    const char *ptr,
-                    const char *domain, /* default domain */
-                    const char *path,   /* full path used when this cookie is
-                                           set, used to get default path for
-                                           the cookie unless set */
-                    bool secure)  /* TRUE if connection is over secure
-                                     origin */
+static CURLcode parse_cookie_header(
+  struct Curl_easy *data,
+  struct Cookie *co,
+  struct CookieInfo *ci,
+  bool *okay,         /* if the cookie was fine */
+  const char *ptr,
+  const char *domain, /* default domain */
+  const char *path,   /* full path used when this cookie is
+                         set, used to get default path for
+                         the cookie unless set */
+  bool secure)        /* TRUE if connection is over secure origin */
 {
   /* This line was read off an HTTP-header */
   time_t now = 0;
@@ -461,6 +460,13 @@
         sep = TRUE; /* a '=' was used */
         if(!curlx_str_cspn(&ptr, &val, ";\r\n"))
           curlx_str_trimblanks(&val);
+
+        /* Reject cookies with a TAB inside the value */
+        if(curlx_strlen(&val) &&
+           memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
+          infof(data, "cookie contains TAB, dropping");
+          return CURLE_OK;
+        }
       }
       else
         curlx_str_init(&val);
@@ -489,13 +495,6 @@
           return CURLE_OK;
         }
 
-        /* Reject cookies with a TAB inside the value */
-        if(curlx_strlen(&val) &&
-           memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
-          infof(data, "cookie contains TAB, dropping");
-          return CURLE_OK;
-        }
-
         /* Check if we have a reserved prefix set. */
         if(!strncmp("__Secure-", curlx_str(&name), 9))
           co->prefix_secure = TRUE;
@@ -930,17 +929,16 @@
  * IPv6 address.
  *
  */
-CURLcode
-Curl_cookie_add(struct Curl_easy *data,
-                struct CookieInfo *ci,
-                bool httpheader, /* TRUE if HTTP header-style line */
-                bool noexpire, /* if TRUE, skip remove_expired() */
-                const char *lineptr,   /* first character of the line */
-                const char *domain, /* default domain */
-                const char *path,   /* full path used when this cookie is set,
-                                       used to get default path for the cookie
-                                       unless set */
-                bool secure)  /* TRUE if connection is over secure origin */
+CURLcode Curl_cookie_add(
+  struct Curl_easy *data,
+  struct CookieInfo *ci,
+  bool httpheader,     /* TRUE if HTTP header-style line */
+  bool noexpire,       /* if TRUE, skip remove_expired() */
+  const char *lineptr, /* first character of the line */
+  const char *domain,  /* default domain */
+  const char *path,    /* full path used when this cookie is set, used
+                          to get default path for the cookie unless set */
+  bool secure)         /* TRUE if connection is over secure origin */
 {
   struct Cookie comem;
   struct Cookie *co;
@@ -1106,8 +1104,17 @@
       fp = curlx_fopen(file, "rb");
       if(!fp)
         infof(data, "WARNING: failed to open cookie file \"%s\"", file);
-      else
-        handle = fp;
+      else {
+        curlx_struct_stat stat;
+        if((curlx_fstat(fileno(fp), &stat) != -1) && S_ISDIR(stat.st_mode)) {
+          curlx_fclose(fp);
+          fp = NULL;
+          infof(data, "WARNING: cookie filename points to a directory: \"%s\"",
+                file);
+        }
+        else
+          handle = fp;
+      }
     }
   }
 
@@ -1464,7 +1471,7 @@
   FILE *out = NULL;
   bool use_stdout = FALSE;
   char *tempstore = NULL;
-  CURLcode error = CURLE_OK;
+  CURLcode result = CURLE_OK;
 
   if(!ci)
     /* no cookie engine alive */
@@ -1479,8 +1486,8 @@
     use_stdout = TRUE;
   }
   else {
-    error = Curl_fopen(data, filename, &out, &tempstore);
-    if(error)
+    result = Curl_fopen(data, filename, &out, &tempstore);
+    if(result)
       goto error;
   }
 
@@ -1497,7 +1504,7 @@
 
     array = curlx_calloc(1, sizeof(struct Cookie *) * ci->numcookies);
     if(!array) {
-      error = CURLE_OUT_OF_MEMORY;
+      result = CURLE_OUT_OF_MEMORY;
       goto error;
     }
 
@@ -1517,7 +1524,7 @@
       char *format_ptr = get_netscape_format(array[i]);
       if(!format_ptr) {
         curlx_free(array);
-        error = CURLE_OUT_OF_MEMORY;
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
       curl_mfprintf(out, "%s\n", format_ptr);
@@ -1531,7 +1538,7 @@
     curlx_fclose(out);
     out = NULL;
     if(tempstore && curlx_rename(tempstore, filename)) {
-      error = CURLE_WRITE_ERROR;
+      result = CURLE_WRITE_ERROR;
       goto error;
     }
   }
@@ -1551,7 +1558,7 @@
     unlink(tempstore);
     curlx_free(tempstore);
   }
-  return error;
+  return result;
 }
 
 static struct curl_slist *cookie_list(struct Curl_easy *data)
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index fabd04d..f66e0ef 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -125,11 +125,11 @@
 void Curl_cookie_clearsess(struct CookieInfo *ci);
 
 #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES)
-#define Curl_cookie_list(x) NULL
+#define Curl_cookie_list(x)      NULL
 #define Curl_cookie_loadfiles(x) CURLE_OK
-#define Curl_cookie_init() NULL
-#define Curl_cookie_run(x) Curl_nop_stmt
-#define Curl_cookie_cleanup(x) Curl_nop_stmt
+#define Curl_cookie_init()       NULL
+#define Curl_cookie_run(x)       Curl_nop_stmt
+#define Curl_cookie_cleanup(x)   Curl_nop_stmt
 #define Curl_flush_cookies(x, y) Curl_nop_stmt
 #else
 void Curl_flush_cookies(struct Curl_easy *data, bool cleanup);
diff --git a/Utilities/cmcurl/lib/cshutdn.c b/Utilities/cmcurl/lib/cshutdn.c
index 308f2aa..27b4a9f 100644
--- a/Utilities/cmcurl/lib/cshutdn.c
+++ b/Utilities/cmcurl/lib/cshutdn.c
@@ -87,14 +87,14 @@
     return;
   }
 
-  if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
+  if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
     r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
   else {
     r1 = CURLE_OK;
     done1 = TRUE;
   }
 
-  if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
+  if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
     r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
   else {
     r2 = CURLE_OK;
diff --git a/Utilities/cmcurl/lib/cshutdn.h b/Utilities/cmcurl/lib/cshutdn.h
index 9030474..b2e83f3 100644
--- a/Utilities/cmcurl/lib/cshutdn.h
+++ b/Utilities/cmcurl/lib/cshutdn.h
@@ -33,7 +33,7 @@
 struct Curl_sigpipe_ctx;
 
 /* Run the shutdown of the connection once.
- * Will shortly attach/detach `data` to `conn` while doing so.
+ * Shortly attach/detach `data` to `conn` while doing so.
  * `done` will be set TRUE if any error was encountered or if
  * the connection was shut down completely. */
 void Curl_cshutdn_run_once(struct Curl_easy *data,
@@ -78,7 +78,7 @@
 bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
                                const char *destination);
 
-/* Add a connection to have it shut down. Will terminate the oldest
+/* Add a connection to have it shut down. Terminate the oldest
  * connection when total connection limit of multi is being reached. */
 void Curl_cshutdn_add(struct cshutdn *cshutdn,
                       struct connectdata *conn,
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c
index 43d398d..fd26c5f 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.c
+++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -404,7 +404,7 @@
  * Given an IPv4 or IPv6 dotted string address, this converts it to a proper
  * allocated Curl_addrinfo struct and returns it.
  */
-CURLcode Curl_str2addr(const char *dotted, int port,
+CURLcode Curl_str2addr(const char *dotted, uint16_t port,
                        struct Curl_addrinfo **addrp)
 {
   struct in_addr in;
@@ -422,10 +422,15 @@
   return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */
 }
 
-bool Curl_is_ipaddr(const char *address)
+bool Curl_is_ipv4addr(const char *address)
 {
   struct in_addr in;
-  if(curlx_inet_pton(AF_INET, address, &in) > 0)
+  return (curlx_inet_pton(AF_INET, address, &in) > 0);
+}
+
+bool Curl_is_ipaddr(const char *address)
+{
+  if(Curl_is_ipv4addr(address))
     return TRUE;
 #ifdef USE_IPV6
   {
@@ -442,34 +447,31 @@
 /**
  * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo
  * struct initialized with this path.
- * Set '*longpath' to TRUE if the error is a too long path.
+ * Returns CURLE_TOO_LARGE when path is too long.
  */
-struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
-                                     bool abstract)
+CURLcode Curl_unix2addr(const char *path, bool abstract,
+                        struct Curl_addrinfo **paddr)
 {
   struct Curl_addrinfo *ai;
   struct sockaddr_un *sa_un;
   size_t path_len;
 
-  *longpath = FALSE;
+  *paddr = NULL;
+
+  /* sun_path must be able to store the null-terminated path */
+  path_len = strlen(path) + 1;
+  if(path_len > sizeof(sa_un->sun_path))
+    return CURLE_TOO_LARGE;
 
   ai = curlx_calloc(1,
                     sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un));
   if(!ai)
-    return NULL;
-  ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
+    return CURLE_OUT_OF_MEMORY;
 
+  ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
   sa_un = (void *)ai->ai_addr;
   sa_un->sun_family = AF_UNIX;
 
-  /* sun_path must be able to store the null-terminated path */
-  path_len = strlen(path) + 1;
-  if(path_len > sizeof(sa_un->sun_path)) {
-    curlx_free(ai);
-    *longpath = TRUE;
-    return NULL;
-  }
-
   ai->ai_family = AF_UNIX;
   ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */
   ai->ai_addrlen = (curl_socklen_t)
@@ -481,7 +483,8 @@
   else
     memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */
 
-  return ai;
+  *paddr = ai;
+  return CURLE_OK;
 }
 #endif
 
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h
index f464b5e..da2da87 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.h
+++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -71,13 +71,14 @@
 struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port);
 #endif
 
+bool Curl_is_ipv4addr(const char *address);
 bool Curl_is_ipaddr(const char *address);
-CURLcode Curl_str2addr(const char *dotted, int port,
+CURLcode Curl_str2addr(const char *dotted, uint16_t port,
                        struct Curl_addrinfo **addrp);
 
 #ifdef USE_UNIX_SOCKETS
-struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
-                                     bool abstract);
+CURLcode Curl_unix2addr(const char *path, bool abstract,
+                        struct Curl_addrinfo **paddr);
 #endif
 
 #if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \
diff --git a/Utilities/cmcurl/lib/curl_config-cmake.h.in b/Utilities/cmcurl/lib/curl_config-cmake.h.in
index 4292c2b..37c401f 100644
--- a/Utilities/cmcurl/lib/curl_config-cmake.h.in
+++ b/Utilities/cmcurl/lib/curl_config-cmake.h.in
@@ -111,8 +111,8 @@
 /* disables netrc parser */
 #cmakedefine CURL_DISABLE_NETRC 1
 
-/* disables NTLM support */
-#cmakedefine CURL_DISABLE_NTLM 1
+/* enables NTLM support */
+#cmakedefine CURL_ENABLE_NTLM 1
 
 /* disables date parsing */
 #cmakedefine CURL_DISABLE_PARSEDATE 1
@@ -139,7 +139,7 @@
 #cmakedefine CURL_DISABLE_SHUFFLE_DNS 1
 
 /* disables SMB */
-#cmakedefine CURL_DISABLE_SMB 1
+#cmakedefine CURL_ENABLE_SMB 1
 
 /* disables SMTP */
 #cmakedefine CURL_DISABLE_SMTP 1
@@ -245,9 +245,6 @@
 /* Define to 1 if you have the fseeko declaration. */
 #cmakedefine HAVE_DECL_FSEEKO 1
 
-/* Define to 1 if you have the ftruncate function. */
-#cmakedefine HAVE_FTRUNCATE 1
-
 /* Define to 1 if you have a working getaddrinfo function. */
 #cmakedefine HAVE_GETADDRINFO 1
 
@@ -438,9 +435,6 @@
 /* Define to 1 if you have a working POSIX-style strerror_r function. */
 #cmakedefine HAVE_POSIX_STRERROR_R 1
 
-/* Define to 1 if you have the <pthread.h> header file */
-#cmakedefine HAVE_PTHREAD_H 1
-
 /* Define to 1 if you have the <pwd.h> header file. */
 #cmakedefine HAVE_PWD_H 1
 
@@ -495,9 +489,6 @@
 /* Define to 1 if you have the sigsetjmp function or macro. */
 #cmakedefine HAVE_SIGSETJMP 1
 
-/* Define to 1 if you have the `snprintf' function. */
-#cmakedefine HAVE_SNPRINTF 1
-
 /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
 #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
 
@@ -653,14 +644,17 @@
 /* Define to 1 if you have the ANSI C header files. */
 #cmakedefine STDC_HEADERS 1
 
+/* Define if you have POSIX pthreads */
+#cmakedefine HAVE_THREADS_POSIX 1
+
 /* Define if you want to enable c-ares support */
 #cmakedefine USE_ARES 1
 
-/* Define if you want to enable POSIX threaded DNS lookup */
-#cmakedefine USE_THREADS_POSIX 1
+/* Define if you want to enable c-ares DNS lookup */
+#cmakedefine USE_RESOLV_ARES 1
 
-/* Define if you want to enable Win32 threaded DNS lookup */
-#cmakedefine USE_THREADS_WIN32 1
+/* Define if you want to enable threaded DNS lookup */
+#cmakedefine USE_RESOLV_THREADED 1
 
 /* if GnuTLS is enabled */
 #cmakedefine USE_GNUTLS 1
@@ -686,15 +680,15 @@
 /* if wolfSSL has the wolfSSL_UseALPN function. */
 #cmakedefine HAVE_WOLFSSL_USEALPN 1
 
-/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */
-#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1
-
 /* if wolfSSL has the wolfSSL_BIO_new function. */
 #cmakedefine HAVE_WOLFSSL_BIO_NEW 1
 
 /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */
 #cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1
 
+/* if wolfSSL has the wc_Des_EcbEncrypt function. */
+#cmakedefine HAVE_WC_DES_ECBENCRYPT 1
+
 /* if libssh is in use */
 #cmakedefine USE_LIBSSH 1
 
@@ -713,9 +707,6 @@
 /* if AmiSSL is in use */
 #cmakedefine USE_AMISSL 1
 
-/* if librtmp/rtmpdump is in use */
-#cmakedefine USE_LIBRTMP 1
-
 /* if GSASL is in use */
 #cmakedefine USE_GSASL 1
 
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h
index 153a8f9..f3291ad 100644
--- a/Utilities/cmcurl/lib/curl_ctype.h
+++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -24,8 +24,8 @@
  *
  ***************************************************************************/
 
-#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f'))
-#define ISUPHEXALHA(x)  (((x) >= 'A') && ((x) <= 'F'))
+#define ISLOWHEXALPHA(x) (((x) >= 'a') && ((x) <= 'f'))
+#define ISUPHEXALPHA(x)  (((x) >= 'A') && ((x) <= 'F'))
 
 #define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f)
 #define IS7F(x)       ((x) == 0x7f)
@@ -36,7 +36,7 @@
 #define ISGRAPH(x)  (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e)))
 #define ISCNTRL(x)  (ISLOWCNTRL(x) || IS7F(x))
 #define ISALPHA(x)  (ISLOWER(x) || ISUPPER(x))
-#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x))
+#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALPHA(x) || ISUPHEXALPHA(x))
 #define ISODIGIT(x) (((x) >= '0') && ((x) <= '7'))
 #define ISALNUM(x)  (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x))
 #define ISUPPER(x)  (((x) >= 'A') && ((x) <= 'Z'))
@@ -44,9 +44,9 @@
 #define ISDIGIT(x)  (((x) >= '0') && ((x) <= '9'))
 #define ISBLANK(x)  (((x) == ' ') || ((x) == '\t'))
 #define ISSPACE(x)  (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d)))
-#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \
-                        ((x) == '~'))
+#define ISURLPUNTCS(x)                                           \
+  (((x) == '-') || ((x) == '.') || ((x) == '_') || ((x) == '~'))
 #define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x))
-#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
+#define ISNEWLINE(x)    (((x) == '\n') || (x) == '\r')
 
 #endif /* HEADER_CURL_CTYPE_H */
diff --git a/Utilities/cmcurl/lib/curl_fopen.c b/Utilities/cmcurl/lib/curl_fopen.c
index b7a67aa..cc888f7 100644
--- a/Utilities/cmcurl/lib/curl_fopen.c
+++ b/Utilities/cmcurl/lib/curl_fopen.c
@@ -103,7 +103,14 @@
     return CURLE_OK;
   }
   curlx_fclose(*fh);
+#ifdef HAVE_GETEUID
+  /* If the existing file is not owned by the user, do not inherit
+   * its permissions at the temp file created below. The permissions
+   * might be unsuitable for holding user private data. */
+  if(sb.st_uid != geteuid())
+    sb.st_mode = 0;
 #endif
+#endif /* !_WIN32 */
   *fh = NULL;
 
   result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c
index 85b3525..6fcd043 100644
--- a/Utilities/cmcurl/lib/curl_get_line.c
+++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -40,6 +40,8 @@
   while(1) {
     size_t rlen;
     const char *b = fgets(buffer, sizeof(buffer), input);
+    if(!b && ferror(input))
+      return CURLE_READ_ERROR;
 
     *eof = feof(input);
 
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c
index c0eb1c4..650d190 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.c
+++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -55,7 +55,7 @@
 #define CURL_ALIGN8
 #endif
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -93,20 +93,20 @@
   char creds[250];
 };
 
-static OM_uint32
-stub_gss_init_sec_context(OM_uint32 *min,
-                          gss_cred_id_t initiator_cred_handle,
-                          struct stub_gss_ctx_id_t_desc **context,
-                          gss_name_t target_name,
-                          const gss_OID mech_type,
-                          OM_uint32 req_flags,
-                          OM_uint32 time_req,
-                          const gss_channel_bindings_t input_chan_bindings,
-                          gss_buffer_desc *input_token,
-                          gss_OID *actual_mech_type,
-                          gss_buffer_desc *output_token,
-                          OM_uint32 *ret_flags,
-                          OM_uint32 *time_rec)
+static OM_uint32 stub_gss_init_sec_context(
+  OM_uint32 *min,
+  gss_cred_id_t initiator_cred_handle,
+  struct stub_gss_ctx_id_t_desc **context,
+  gss_name_t target_name,
+  const gss_OID mech_type,
+  OM_uint32 req_flags,
+  OM_uint32 time_req,
+  const gss_channel_bindings_t input_chan_bindings,
+  gss_buffer_desc *input_token,
+  gss_OID *actual_mech_type,
+  gss_buffer_desc *output_token,
+  OM_uint32 *ret_flags,
+  OM_uint32 *time_rec)
 {
   struct stub_gss_ctx_id_t_desc *ctx = NULL;
 
@@ -283,10 +283,10 @@
   return GSS_S_CONTINUE_NEEDED;
 }
 
-static OM_uint32
-stub_gss_delete_sec_context(OM_uint32 *min,
-                            struct stub_gss_ctx_id_t_desc **context,
-                            gss_buffer_t output_token)
+static OM_uint32 stub_gss_delete_sec_context(
+  OM_uint32 *min,
+  struct stub_gss_ctx_id_t_desc **context,
+  gss_buffer_t output_token)
 {
   (void)output_token;
 
@@ -441,7 +441,7 @@
 }
 #endif /* CURLVERBOSE */
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
 
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h
index 0d36097..fc3759e 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.h
+++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -44,7 +44,7 @@
                                     OM_uint32 *ret_flags);
 
 OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
-                                      gss_ctx_id_t *context_handle,
+                                      gss_ctx_id_t *context,
                                       gss_buffer_t output_token);
 
 #ifdef CURLVERBOSE
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h
index b9f218f..301d44f 100644
--- a/Utilities/cmcurl/lib/curl_hmac.h
+++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -57,14 +57,14 @@
 struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams,
                                     const unsigned char *key,
                                     unsigned int keylen);
-int Curl_HMAC_update(struct HMAC_context *ctxt,
-                     const unsigned char *data,
-                     unsigned int len);
+void Curl_HMAC_update(struct HMAC_context *ctxt,
+                      const unsigned char *data,
+                      unsigned int len);
 int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output);
 
 CURLcode Curl_hmacit(const struct HMAC_params *hashparams,
                      const unsigned char *key, const size_t keylen,
-                     const unsigned char *data, const size_t datalen,
+                     const unsigned char *data, size_t datalen,
                      unsigned char *output);
 
 #endif
diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h
index 7c1e1c4..0ccc128 100644
--- a/Utilities/cmcurl/lib/curl_ldap.h
+++ b/Utilities/cmcurl/lib/curl_ldap.h
@@ -25,9 +25,6 @@
  ***************************************************************************/
 extern const struct Curl_protocol Curl_protocol_ldap;
 
-extern const struct Curl_scheme Curl_scheme_ldap;
-extern const struct Curl_scheme Curl_scheme_ldaps;
-
 void Curl_ldap_version(char *buf, size_t bufsz);
 
 #endif /* HEADER_CURL_LDAP_H */
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h
index 042c5f5..8a0cc26 100644
--- a/Utilities/cmcurl/lib/curl_md5.h
+++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -54,7 +54,7 @@
 extern const struct HMAC_params Curl_HMAC_MD5;
 
 CURLcode Curl_md5it(unsigned char *output, const unsigned char *input,
-                    const size_t len);
+                    size_t len);
 
 struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params);
 CURLcode Curl_MD5_update(struct MD5_context *context,
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index 4a9433b..4b2007b 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -49,59 +49,30 @@
      in NTLM type-3 messages.
  */
 
-#ifdef USE_MBEDTLS
-#include <mbedtls/version.h>
-#if MBEDTLS_VERSION_NUMBER < 0x03020000
-#error "mbedTLS 3.2.0 or later required"
-#endif
-#endif
-
 #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)
-#  define USE_OPENSSL_DES
-#elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)
-#  define USE_OPENSSL_DES
-#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)
-#  define USE_MBEDTLS_DES
-#endif
 
-#ifdef USE_OPENSSL_DES
-
-#ifdef USE_OPENSSL
 #  include <openssl/des.h>
 #  ifdef OPENSSL_IS_AWSLC  /* for versions 1.2.0 to 1.30.1 */
 #    define DES_set_key_unchecked (void)DES_set_key
 #  endif
-#  define DESKEY(x) &x
-#else
+#  define USE_OPENSSL_DES
+
+#elif defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT)
+
 #  include <wolfssl/options.h>
-#  include <wolfssl/openssl/des.h>
-#  include <wolfssl/version.h>
-#  ifdef OPENSSL_COEXIST
-#    define DES_key_schedule      WOLFSSL_DES_key_schedule
-#    define DES_cblock            WOLFSSL_DES_cblock
-#    define DES_set_odd_parity    wolfSSL_DES_set_odd_parity
-#    define DES_set_key           wolfSSL_DES_set_key
-#    define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked
-#    define DES_ecb_encrypt       wolfSSL_DES_ecb_encrypt
-#    define DESKEY(x)             ((WOLFSSL_DES_key_schedule *)(x))
-#    if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX >= 0x05007006
-#      define DES_ENCRYPT WC_DES_ENCRYPT
-#      define DES_DECRYPT WC_DES_DECRYPT
-#    endif
-#  else
-#    define DESKEY(x) &x
-#  endif
-#endif
+#  include <wolfssl/wolfcrypt/des3.h>
+#  define USE_WOLFSSL_DES
 
 #elif defined(USE_GNUTLS)
-
 #  include <nettle/des.h>
 #  define USE_CURL_DES_SET_ODD_PARITY
-
-#elif defined(USE_MBEDTLS_DES)
-
+#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)
+#  include <mbedtls/version.h>
+#  if MBEDTLS_VERSION_NUMBER < 0x03020000
+#  error "mbedTLS 3.2.0 or later required"
+#  endif
 #  include <mbedtls/des.h>
-
+#  define USE_MBEDTLS_DES
 #elif defined(USE_OS400CRYPTO)
 #  include "cipher.mih"  /* mih/cipher */
 #  define USE_CURL_DES_SET_ODD_PARITY
@@ -170,7 +141,7 @@
   key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
   key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
   key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
-  key[7] = (char) ((key_56[6] << 1) & 0xFF);
+  key[7] = (char)((key_56[6] << 1) & 0xFF);
 }
 
 #ifdef USE_OPENSSL_DES
@@ -192,8 +163,19 @@
   DES_set_key_unchecked(&key, ks);
 }
 
-#elif defined(USE_GNUTLS)
+#elif defined(USE_WOLFSSL_DES)
+static void setup_des_key(const unsigned char *key_56, Des *des)
+{
+  byte key[8];
 
+  /* Expand the 56-bit key to 64 bits */
+  extend_key_56_to_64(key_56, (char *)key);
+
+  /* Set the key */
+  wc_Des_SetKey(des, key, NULL, 0);
+}
+
+#elif defined(USE_GNUTLS)
 static void setup_des_key(const unsigned char *key_56, struct des_ctx *des)
 {
   char key[8];
@@ -209,7 +191,6 @@
 }
 
 #elif defined(USE_MBEDTLS_DES)
-
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
                         const unsigned char *key_56)
 {
@@ -229,7 +210,6 @@
 }
 
 #elif defined(USE_OS400CRYPTO)
-
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
                         const unsigned char *key_56)
 {
@@ -253,7 +233,6 @@
 }
 
 #elif defined(USE_WIN32_CRYPTO)
-
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
                         const unsigned char *key_56)
 {
@@ -316,17 +295,25 @@
 #ifdef USE_OPENSSL_DES
   DES_key_schedule ks;
 
-  setup_des_key(keys, DESKEY(ks));
+  setup_des_key(keys, &ks);
   DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext),
-                  (DES_cblock *)results, DESKEY(ks), DES_ENCRYPT);
+                  (DES_cblock *)results, &ks, DES_ENCRYPT);
 
-  setup_des_key(keys + 7, DESKEY(ks));
+  setup_des_key(keys + 7, &ks);
   DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext),
-                  (DES_cblock *)(results + 8), DESKEY(ks), DES_ENCRYPT);
+                  (DES_cblock *)(results + 8), &ks, DES_ENCRYPT);
 
-  setup_des_key(keys + 14, DESKEY(ks));
+  setup_des_key(keys + 14, &ks);
   DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext),
-                  (DES_cblock *)(results + 16), DESKEY(ks), DES_ENCRYPT);
+                  (DES_cblock *)(results + 16), &ks, DES_ENCRYPT);
+#elif defined(USE_WOLFSSL_DES)
+  Des des;
+  setup_des_key(keys, &des);
+  wc_Des_EcbEncrypt(&des, results, plaintext, DES_KEY_SIZE);
+  setup_des_key(keys + 7, &des);
+  wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE);
+  setup_des_key(keys + 14, &des);
+  wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE);
 #elif defined(USE_GNUTLS)
   struct des_ctx des;
   setup_des_key(keys, &des);
@@ -364,17 +351,22 @@
 
   {
     /* Create LanManager hashed password. */
-
 #ifdef USE_OPENSSL_DES
     DES_key_schedule ks;
 
-    setup_des_key(pw, DESKEY(ks));
+    setup_des_key(pw, &ks);
     DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
-                    (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT);
+                    (DES_cblock *)lmbuffer, &ks, DES_ENCRYPT);
 
-    setup_des_key(pw + 7, DESKEY(ks));
+    setup_des_key(pw + 7, &ks);
     DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
-                    (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT);
+                    (DES_cblock *)(lmbuffer + 8), &ks, DES_ENCRYPT);
+#elif defined(USE_WOLFSSL_DES)
+    Des des;
+    setup_des_key(pw, &des);
+    wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE);
+    setup_des_key(pw + 7, &des);
+    wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE);
 #elif defined(USE_GNUTLS)
     struct des_ctx des;
     setup_des_key(pw, &des);
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c
deleted file mode 100644
index 75b8091..0000000
--- a/Utilities/cmcurl/lib/curl_rtmp.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/***************************************************************************
- *                      _   _ ____  _
- *  Project         ___| | | |  _ \| |
- *                 / __| | | | |_) | |
- *                | (__| |_| |  _ <| |___
- *                 \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) Howard Chu, <hyc@highlandsun.com>
- *
- * 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 "curl_rtmp.h"
-#include "urldata.h"
-
-#ifdef USE_LIBRTMP
-
-#include "url.h"
-#include "curlx/nonblock.h"
-#include "progress.h" /* for Curl_pgrsSetUploadSize */
-#include "transfer.h"
-#include "bufref.h"
-
-#include <librtmp/rtmp.h>
-
-#if defined(USE_WINSOCK) || defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
-#define SET_RCVTIMEO(tv, s)  int tv = s * 1000
-#else
-#define SET_RCVTIMEO(tv, s)  struct timeval tv = { s, 0 }
-#endif
-
-#define DEF_BUFTIME    (2 * 60 * 60 * 1000)    /* 2 hours */
-
-/* meta key for storing RTMP* at connection */
-#define CURL_META_RTMP_CONN   "meta:proto:rtmp:conn"
-
-static Curl_recv rtmp_recv;
-static Curl_send rtmp_send;
-
-static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
-{
-  RTMP *r = entry;
-  (void)key;
-  (void)klen;
-  RTMP_Close(r);
-  RTMP_Free(r);
-}
-
-static CURLcode rtmp_setup_connection(struct Curl_easy *data,
-                                      struct connectdata *conn)
-{
-  RTMP *r = RTMP_Alloc();
-  if(!r ||
-    Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
-    return CURLE_OUT_OF_MEMORY;
-
-  RTMP_Init(r);
-  RTMP_SetBufferMS(r, DEF_BUFTIME);
-  if(!RTMP_SetupURL(r, CURL_UNCONST(Curl_bufref_ptr(&data->state.url))))
-    /* rtmp_conn_dtor() performs the cleanup */
-    return CURLE_URL_MALFORMAT;
-  return CURLE_OK;
-}
-
-static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
-{
-  struct connectdata *conn = data->conn;
-  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  SET_RCVTIMEO(tv, 10);
-
-  if(!r)
-    return CURLE_FAILED_INIT;
-
-  if(conn->sock[FIRSTSOCKET] > INT_MAX) {
-    /* The socket value is invalid for rtmp. */
-    return CURLE_FAILED_INIT;
-  }
-
-  r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
-
-  /* We have to know if it is a write before we send the
-   * connect request packet
-   */
-  if(data->state.upload)
-    r->Link.protocol |= RTMP_FEATURE_WRITE;
-
-  /* For plain streams, use the buffer toggle trick to keep data flowing */
-  if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
-     !(r->Link.protocol & RTMP_FEATURE_HTTP))
-    r->Link.lFlags |= RTMP_LF_BUFX;
-
-  (void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
-  setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
-             (char *)&tv, sizeof(tv));
-
-  if(!RTMP_Connect1(r, NULL))
-    return CURLE_FAILED_INIT;
-
-  /* Clients must send a periodic BytesReceived report to the server */
-  r->m_bSendCounter = TRUE;
-
-  *done = TRUE;
-  conn->recv[FIRSTSOCKET] = rtmp_recv;
-  conn->send[FIRSTSOCKET] = rtmp_send;
-  return CURLE_OK;
-}
-
-static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
-{
-  struct connectdata *conn = data->conn;
-  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-
-  if(!r || !RTMP_ConnectStream(r, 0))
-    return CURLE_FAILED_INIT;
-
-  if(data->state.upload) {
-    Curl_pgrsSetUploadSize(data, data->state.infilesize);
-    Curl_xfer_setup_send(data, FIRSTSOCKET);
-  }
-  else
-    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
-  *done = TRUE;
-  return CURLE_OK;
-}
-
-static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
-                          bool premature)
-{
-  (void)data;
-  (void)status;
-  (void)premature;
-
-  return CURLE_OK;
-}
-
-static CURLcode rtmp_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn,
-                                bool dead_connection)
-{
-  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  (void)data;
-  (void)dead_connection;
-  if(r)
-    Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
-  return CURLE_OK;
-}
-
-static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
-                          size_t len, size_t *pnread)
-{
-  struct connectdata *conn = data->conn;
-  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  CURLcode result = CURLE_OK;
-  ssize_t rv;
-
-  (void)sockindex;
-  *pnread = 0;
-  if(!r)
-    return CURLE_FAILED_INIT;
-
-  rv = RTMP_Read(r, buf, curlx_uztosi(len));
-  if(!curlx_sztouz(rv, pnread)) {
-    if(r->m_read.status == RTMP_READ_COMPLETE ||
-       r->m_read.status == RTMP_READ_EOF) {
-      data->req.size = data->req.bytecount;
-    }
-    else
-      result = CURLE_RECV_ERROR;
-  }
-
-  return result;
-}
-
-static CURLcode rtmp_send(struct Curl_easy *data, int sockindex,
-                          const uint8_t *buf, size_t len, bool eos,
-                          size_t *pnwritten)
-{
-  struct connectdata *conn = data->conn;
-  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  ssize_t rv;
-
-  (void)sockindex;
-  (void)eos;
-  *pnwritten = 0;
-  if(!r)
-    return CURLE_FAILED_INIT;
-
-  rv = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
-  if(!curlx_sztouz(rv, pnwritten))
-    return CURLE_SEND_ERROR;
-
-  return CURLE_OK;
-}
-
-void Curl_rtmp_version(char *version, size_t len)
-{
-  char suff[2];
-  if(RTMP_LIB_VERSION & 0xff) {
-    suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
-    suff[1] = '\0';
-  }
-  else
-    suff[0] = '\0';
-
-  curl_msnprintf(version, len, "librtmp/%d.%d%s",
-                 RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
-                 suff);
-}
-
-/*
- * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/
- */
-
-static const struct Curl_protocol Curl_protocol_rtmp = {
-  rtmp_setup_connection,                /* setup_connection */
-  rtmp_do,                              /* do_it */
-  rtmp_done,                            /* done */
-  ZERO_NULL,                            /* do_more */
-  rtmp_connect,                         /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_pollset */
-  ZERO_NULL,                            /* doing_pollset */
-  ZERO_NULL,                            /* domore_pollset */
-  ZERO_NULL,                            /* perform_pollset */
-  rtmp_disconnect,                      /* disconnect */
-  ZERO_NULL,                            /* write_resp */
-  ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
-  ZERO_NULL,                            /* attach connection */
-  ZERO_NULL,                            /* follow */
-};
-
-#endif /* USE_LIBRTMP */
-
-const struct Curl_scheme Curl_scheme_rtmp = {
-  "rtmp",                               /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMP,                       /* protocol */
-  CURLPROTO_RTMP,                       /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMP,                            /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_rtmpt = {
-  "rtmpt",                              /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMPT,                      /* protocol */
-  CURLPROTO_RTMPT,                      /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMPT,                           /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_rtmpe = {
-  "rtmpe",                              /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMPE,                      /* protocol */
-  CURLPROTO_RTMPE,                      /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMP,                            /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_rtmpte = {
-  "rtmpte",                             /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMPTE,                     /* protocol */
-  CURLPROTO_RTMPTE,                     /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMPT,                           /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_rtmps = {
-  "rtmps",                              /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMPS,                      /* protocol */
-  CURLPROTO_RTMP,                       /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMPS,                           /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_rtmpts = {
-  "rtmpts",                             /* scheme */
-#ifndef USE_LIBRTMP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtmp,
-#endif
-  CURLPROTO_RTMPTS,                     /* protocol */
-  CURLPROTO_RTMPT,                      /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_RTMPS,                           /* defport */
-};
diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h
deleted file mode 100644
index e57d20c..0000000
--- a/Utilities/cmcurl/lib/curl_rtmp.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef HEADER_CURL_RTMP_H
-#define HEADER_CURL_RTMP_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Howard Chu, <hyc@highlandsun.com>
- *
- * 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
- *
- ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_rtmp;
-extern const struct Curl_scheme Curl_scheme_rtmpt;
-extern const struct Curl_scheme Curl_scheme_rtmpe;
-extern const struct Curl_scheme Curl_scheme_rtmpte;
-extern const struct Curl_scheme Curl_scheme_rtmps;
-extern const struct Curl_scheme Curl_scheme_rtmpts;
-#ifdef USE_LIBRTMP
-void Curl_rtmp_version(char *version, size_t len);
-#endif
-
-#endif /* HEADER_CURL_RTMP_H */
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index b2eb1fe..60f0859 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -415,9 +415,8 @@
       data->set.str[STRING_SERVICE_NAME] :
       sctx->sasl->params->service;
     const char *hostname;
-    int port;
 
-    Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
+    Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL);
 
     sctx->mech = SASL_MECH_STRING_NTLM;
     sctx->state1 = SASL_NTLM;
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 90d2ff7..2494152 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -224,7 +224,7 @@
 /* ================================================================ */
 
 /* Give calloc a chance to be dragging in early, so we do not redefine */
-#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+#ifdef HAVE_THREADS_POSIX
 #  include <pthread.h>
 #endif
 
@@ -262,9 +262,6 @@
 #  ifndef CURL_DISABLE_RTSP
 #  define CURL_DISABLE_RTSP
 #  endif
-#  ifndef CURL_DISABLE_SMB
-#  define CURL_DISABLE_SMB
-#  endif
 #  ifndef CURL_DISABLE_SMTP
 #  define CURL_DISABLE_SMTP
 #  endif
@@ -372,16 +369,14 @@
 #endif
 
 /* based on logic in "curl/mprintf.h" */
-#if (defined(__GNUC__) || defined(__clang__) ||                         \
-  defined(__IAR_SYSTEMS_ICC__)) &&                                      \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
+#if (defined(__GNUC__) || defined(__clang__) ||                 \
+     defined(__IAR_SYSTEMS_ICC__)) &&                           \
+  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)))
+#define CURL_PRINTF(fmt, arg) __attribute__((format(gnu_printf, fmt, arg)))
 #else
-#define CURL_PRINTF(fmt, arg) \
-  __attribute__((format(__printf__, fmt, arg)))
+#define CURL_PRINTF(fmt, arg) __attribute__((format(__printf__, fmt, arg)))
 #endif
 #else
 #define CURL_PRINTF(fmt, arg)
@@ -399,7 +394,7 @@
    (defined(__GNUC__) && __GNUC__ <= 14)) &&                \
   defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
   !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__)
-#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__             \
+#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \
   __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
 #endif
 
@@ -446,7 +441,7 @@
 #    if !(defined(__NEWLIB__) || \
           (defined(__CLIB2__) && defined(__THREAD_SAFE)))
        /* disable threaded resolver with clib2 - requires newlib or clib-ts */
-#      undef USE_THREADS_POSIX
+#      undef USE_RESOLV_THREADED
 #    endif
 #  endif
 #  include <exec/types.h>
@@ -700,6 +695,16 @@
 
 #endif /* _WIN32 */
 
+/* We want to use mutex when available. */
+#if defined(HAVE_THREADS_POSIX) || defined(_WIN32)
+#define USE_MUTEX
+#endif
+
+/* threaded resolver is the only feature requiring threads. */
+#ifdef USE_RESOLV_THREADED
+#define USE_THREADS
+#endif
+
 /* ---------------------------------------------------------------- */
 /*             resolver specialty compile-time defines              */
 /*         CURLRES_* defines to use in the host*.c sources          */
@@ -717,12 +722,10 @@
 #  define CURLRES_IPV4
 #endif
 
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#ifdef USE_RESOLV_THREADED
 #  define CURLRES_ASYNCH
-#  define CURLRES_THREADED
-#elif defined(USE_ARES)
+#elif defined(USE_RESOLV_ARES)
 #  define CURLRES_ASYNCH
-#  define CURLRES_ARES
 /* now undef the stock libc functions to avoid them being used */
 #  undef HAVE_GETADDRINFO
 #  undef HAVE_FREEADDRINFO
@@ -743,8 +746,7 @@
 #endif
 
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
-  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
-  defined(USE_RUSTLS)
+  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_RUSTLS)
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 
@@ -760,24 +762,24 @@
 #endif
 
 /* Single point where USE_SPNEGO definition might be defined */
-#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \
-    (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
+#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) &&          \
+  (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
 #define USE_SPNEGO
 #endif
 
 /* Single point where USE_KERBEROS5 definition might be defined */
-#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \
-    (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
+#if !defined(CURL_DISABLE_KERBEROS_AUTH) &&           \
+  (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
 #define USE_KERBEROS5
 #endif
 
 /* Single point where USE_NTLM definition might be defined */
-#ifndef CURL_DISABLE_NTLM
-#  if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) ||        \
-  defined(USE_GNUTLS) ||                                                \
-  (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) ||      \
-  defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
-  (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
+#ifdef CURL_ENABLE_NTLM
+#  if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) ||   \
+  defined(USE_GNUTLS) ||                                           \
+  (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \
+  defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||         \
+  (defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT))
 #    define USE_CURL_NTLM_CORE
 #  endif
 #  if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
@@ -789,6 +791,16 @@
 #define USE_SSH
 #endif
 
+/* GCC <4.6 does not support '#pragma GCC diagnostic push' and does not support
+   'pragma GCC diagnostic' inside functions.
+   Use CURL_HAVE_DIAG to guard the above in the curl codebase, instead of
+   defined(__GNUC__) || defined(__clang__).
+ */
+#if defined(__clang__) || (defined(__GNUC__) && \
+  ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6))))
+#define CURL_HAVE_DIAG
+#endif
+
 /*
  * Provide a mechanism to silence picky compilers, such as gcc 4.6+.
  * Parameters should of course normally not be unused, but for example when
@@ -819,8 +831,8 @@
 /* fallthrough attribute */
 
 #ifndef FALLTHROUGH
-#if (defined(__GNUC__) && __GNUC__ >= 7) || \
-    (defined(__clang__) && __clang_major__ >= 10)
+#if (defined(__GNUC__) && __GNUC__ >= 7) ||     \
+  (defined(__clang__) && __clang_major__ >= 10)
 #  define FALLTHROUGH()  __attribute__((fallthrough))
 #else
 #  define FALLTHROUGH()  do {} while(0)
@@ -1015,7 +1027,7 @@
  * 'bool' stuff compatible with HP-UX headers.
  */
 #if defined(__hpux) && !defined(HAVE_BOOL_T)
-   typedef int bool;
+typedef int bool;
 #  define false 0
 #  define true 1
 #  define HAVE_BOOL_T
@@ -1028,10 +1040,10 @@
  * global namespace though, so use bool_false and bool_true.
  */
 #ifndef HAVE_BOOL_T
-  typedef enum {
-    bool_false = 0,
-    bool_true  = 1
-  } bool;
+typedef enum {
+  bool_false = 0,
+  bool_true = 1
+} bool;
 
 /*
  * Use a define to let 'true' and 'false' use those enums. There
@@ -1322,14 +1334,14 @@
 extern curl_calloc_callback Curl_ccalloc;
 
 /*
- * Curl_safefree defined as a macro to allow MemoryTracking feature
- * to log free() calls at same location where Curl_safefree is used.
+ * curlx_safefree() defined as a macro to allow MemoryTracking feature
+ * to log free() calls at same location where curlx_safefree() is used.
  * This macro also assigns NULL to given pointer when free'd.
  */
-#define Curl_safefree(ptr) \
-  do {                     \
-    curlx_free(ptr);       \
-    (ptr) = NULL;          \
+#define curlx_safefree(ptr) \
+  do {                      \
+    curlx_free(ptr);        \
+    (ptr) = NULL;           \
   } while(0)
 
 #include <curl/curl.h> /* for CURL_EXTERN, curl_socket_t, mprintf.h */
@@ -1390,7 +1402,8 @@
                                       int line, const char *source);
 CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
                                 int line, const char *source);
-CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
+CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s,
+                                          void *saddr, void *saddrlen,
                                           int line, const char *source);
 #ifdef HAVE_ACCEPT4
 CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
@@ -1522,9 +1535,7 @@
 #define USE_HTTP2
 #endif
 
-#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
-  (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \
-  defined(USE_QUICHE)
+#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)
 
 #ifdef CURL_WITH_MULTI_SSL
 #error "MultiSSL combined with QUIC is not supported"
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h
index bc1512e..6c48a8e 100644
--- a/Utilities/cmcurl/lib/curl_sha256.h
+++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -38,7 +38,7 @@
 #endif
 
 CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input,
-                       const size_t len);
+                       size_t len);
 
 #endif
 
diff --git a/Utilities/cmcurl/lib/curl_sha512_256.c b/Utilities/cmcurl/lib/curl_sha512_256.c
index 648e3e3..73b959c 100644
--- a/Utilities/cmcurl/lib/curl_sha512_256.c
+++ b/Utilities/cmcurl/lib/curl_sha512_256.c
@@ -28,12 +28,12 @@
 #include "curl_sha512_256.h"
 
 /* The recommended order of the TLS backends:
- * 1. OpenSSL
- * 2. GnuTLS
- * 3. wolfSSL
- * 4. Schannel SSPI
- * 5. mbedTLS
- * 6. Rustls
+ * 1. USE_OPENSSL
+ * 2. USE_WOLFSSL
+ * 3. USE_GNUTLS
+ * 4. USE_MBEDTLS (TBD)
+ * 5. USE_RUSTLS (TBD)
+ * 6. USE_WIN32_CRYPTO (TBD)
  * Skip the backend if it does not support the required algorithm */
 
 #ifdef USE_OPENSSL
@@ -66,6 +66,14 @@
 #  endif
 #endif /* USE_OPENSSL */
 
+#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_WOLFSSL)
+#  include <wolfssl/options.h>
+#  ifndef WOLFSSL_NOSHA512_256
+#    define USE_WOLFSSL_SHA512_256          1
+#    define HAS_SHA512_256_IMPLEMENTATION   1
+#  endif
+#endif
+
 #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
 #  include <nettle/sha.h>
 #  ifdef SHA512_256_DIGEST_SIZE
@@ -153,26 +161,63 @@
  */
 static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
 {
-  CURLcode ret;
+  CURLcode result;
   Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context;
 
 #ifdef NEED_NETBSD_SHA512_256_WORKAROUND
   /* Use a larger buffer to work around a bug in NetBSD:
      https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
   unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2];
-  ret = EVP_DigestFinal_ex(*ctx,
+  result = EVP_DigestFinal_ex(*ctx,
                            tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
-  if(ret == CURLE_OK)
+  if(result == CURLE_OK)
     memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE);
   explicit_memset(tmp_digest, 0, sizeof(tmp_digest));
 #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */
-  ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
+  result = EVP_DigestFinal_ex(*ctx, digest, NULL) ?
+    CURLE_OK : CURLE_SSL_CIPHER;
 #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */
 
   EVP_MD_CTX_destroy(*ctx);
   *ctx = NULL;
 
-  return ret;
+  return result;
+}
+
+#elif defined(USE_WOLFSSL_SHA512_256)
+#include <wolfssl/wolfcrypt/sha512.h>
+
+#define CURL_SHA512_256_DIGEST_SIZE WC_SHA512_256_DIGEST_SIZE
+#define CURL_SHA512_256_BLOCK_SIZE  WC_SHA512_256_BLOCK_SIZE
+
+typedef struct wc_Sha512 Curl_sha512_256_ctx;
+
+static CURLcode Curl_sha512_256_init(void *ctx)
+{
+  if(wc_InitSha512_256(ctx))
+    return CURLE_FAILED_INIT;
+  return CURLE_OK;
+}
+
+static CURLcode Curl_sha512_256_update(void *ctx,
+                                       const unsigned char *data,
+                                       size_t length)
+{
+  do {
+    word32 ilen = (word32)CURLMIN(length, UINT_MAX);
+    if(wc_Sha512_256Update(ctx, data, ilen))
+      return CURLE_SSL_CIPHER;
+    length -= ilen;
+    data += ilen;
+  } while(length);
+  return CURLE_OK;
+}
+
+static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx)
+{
+  if(wc_Sha512_256Final(ctx, digest))
+    return CURLE_SSL_CIPHER;
+  return CURLE_OK;
 }
 
 #elif defined(USE_GNUTLS_SHA512_256)
@@ -247,10 +292,7 @@
 /* ** This implementation of SHA-512/256 hash calculation was originally ** *
  * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd.          ** *
  * ** The author ported the code to libcurl. The ported code is provided ** *
- * ** under curl license.                                                ** *
- * ** This is a minimal version with minimal optimizations. Performance  ** *
- * ** can be significantly improved. Big-endian store and load macros    ** *
- * ** are obvious targets for optimization.                              ** */
+ * ** under curl license.                                                ** */
 
 #ifdef __GNUC__
 #  if defined(__has_attribute) && defined(__STDC_VERSION__)
@@ -751,17 +793,17 @@
                            size_t input_size)
 {
   Curl_sha512_256_ctx ctx;
-  CURLcode res;
+  CURLcode result;
 
-  res = Curl_sha512_256_init(&ctx);
-  if(res != CURLE_OK)
-    return res;
+  result = Curl_sha512_256_init(&ctx);
+  if(result != CURLE_OK)
+    return result;
 
-  res = Curl_sha512_256_update(&ctx, (const void *)input, input_size);
+  result = Curl_sha512_256_update(&ctx, (const void *)input, input_size);
 
-  if(res != CURLE_OK) {
+  if(result != CURLE_OK) {
     (void)Curl_sha512_256_finish(output, &ctx);
-    return res;
+    return result;
   }
 
   return Curl_sha512_256_finish(output, &ctx);
diff --git a/Utilities/cmcurl/lib/curl_share.c b/Utilities/cmcurl/lib/curl_share.c
index 338727a..386a2b5 100644
--- a/Utilities/cmcurl/lib/curl_share.c
+++ b/Utilities/cmcurl/lib/curl_share.c
@@ -24,22 +24,61 @@
 #include "curl_setup.h"
 
 #include "urldata.h"
+#include "multiif.h"
+#include "curl_threads.h"
 #include "curl_share.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_scache.h"
 #include "hsts.h"
 #include "url.h"
 
+static void share_destroy(struct Curl_share *share)
+{
+  if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
+    Curl_cpool_destroy(&share->cpool);
+  }
+
+  Curl_dnscache_destroy(&share->dnscache);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+  Curl_cookie_cleanup(share->cookies);
+#endif
+
+#ifndef CURL_DISABLE_HSTS
+  Curl_hsts_cleanup(&share->hsts);
+#endif
+
+#ifdef USE_SSL
+  if(share->ssl_scache) {
+    Curl_ssl_scache_destroy(share->ssl_scache);
+    share->ssl_scache = NULL;
+  }
+#endif
+
+  Curl_psl_destroy(&share->psl);
+  Curl_close(&share->admin);
+
+#ifdef USE_MUTEX
+  Curl_mutex_destroy(&share->lock);
+#endif
+  share->magic = 0;
+  curlx_free(share);
+}
+
 CURLSH *curl_share_init(void)
 {
   struct Curl_share *share = curlx_calloc(1, sizeof(struct Curl_share));
   if(share) {
     share->magic = CURL_GOOD_SHARE;
     share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
+#ifdef USE_MUTEX
+    Curl_mutex_init(&share->lock);
+#endif
+    share->ref_count = 1;
     Curl_dnscache_init(&share->dnscache, 23);
     share->admin = curl_easy_init();
     if(!share->admin) {
-      curlx_free(share);
+      share_destroy(share);
       return NULL;
     }
     /* admin handles have mid 0 */
@@ -50,10 +89,104 @@
       share->admin->set.verbose = TRUE;
 #endif
   }
-
   return share;
 }
 
+static uint32_t share_ref_inc(struct Curl_share *share)
+{
+  uint32_t n;
+#ifdef USE_MUTEX
+  Curl_mutex_acquire(&share->lock);
+  n = ++(share->ref_count);
+  share->has_been_shared = TRUE;
+  Curl_mutex_release(&share->lock);
+#else
+  n = ++(share->ref_count);
+  share->has_been_shared = TRUE;
+#endif
+  return n;
+}
+
+static uint32_t share_ref_dec(struct Curl_share *share)
+{
+  uint32_t n;
+#ifdef USE_MUTEX
+  Curl_mutex_acquire(&share->lock);
+  DEBUGASSERT(share->ref_count);
+  n = --(share->ref_count);
+  Curl_mutex_release(&share->lock);
+#else
+  n = --(share->ref_count);
+#endif
+  return n;
+}
+
+static bool share_has_been_shared(struct Curl_share *share)
+{
+  bool was_shared;
+#ifdef USE_MUTEX
+  Curl_mutex_acquire(&share->lock);
+  was_shared = share->has_been_shared;
+  Curl_mutex_release(&share->lock);
+#else
+  was_shared = share->has_been_shared;
+#endif
+  return was_shared;
+}
+
+static bool share_lock_acquire(struct Curl_share *share,
+                               struct Curl_easy *data)
+{
+  if(share->lockfunc && share->unlockfunc &&
+     (data || share_has_been_shared(share))) {
+    share->lockfunc(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
+                    share->clientdata);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static void share_lock_release(struct Curl_share *share,
+                               struct Curl_easy *data,
+                               bool locked)
+{
+  if(locked) {
+    DEBUGASSERT(share->unlockfunc);
+    if(share->unlockfunc)
+      share->unlockfunc(data, CURL_LOCK_DATA_SHARE, share->clientdata);
+  }
+}
+
+static bool share_in_use(struct Curl_share *share)
+{
+  bool in_use;
+#ifdef USE_MUTEX
+  Curl_mutex_acquire(&share->lock);
+  in_use = (share->ref_count > 1);
+  Curl_mutex_release(&share->lock);
+#else
+  bool locked = share_lock_acquire(share, NULL);
+  in_use = (share->ref_count > 1);
+  share_lock_release(share, NULL, locked);
+#endif
+  return in_use;
+}
+
+static void share_unlink(struct Curl_share **pshare,
+                         struct Curl_easy *data,
+                         bool locked)
+{
+  struct Curl_share *share = *pshare;
+  uint32_t n;
+
+  *pshare = NULL;
+  n = share_ref_dec(share);
+  if(locked)
+    share_lock_release(share, data, locked);
+  if(!n)  /* last reference gone */
+    share_destroy(share);
+}
+
 #undef curl_share_setopt
 CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
 {
@@ -68,10 +201,11 @@
   if(!GOOD_SHARE_HANDLE(share))
     return CURLSHE_INVALID;
 
-  if(share->dirty)
+  if(share_in_use(share)) {
     /* do not allow setting options while one or more handles are already
        using this share */
     return CURLSHE_IN_USE;
+  }
 
   va_start(param, option);
 
@@ -114,8 +248,8 @@
         /* There is no way (yet) for the application to configure the
          * session cache size, shared between many transfers. As for curl
          * itself, a high session count will impact startup time. Also, the
-         * scache is not optimized for several hundreds of peers. So,
-         * keep it at a reasonable level. */
+         * scache is not optimized for several hundreds of peers.
+         * Keep it at a reasonable level. */
         if(Curl_ssl_scache_create(25, 2, &share->ssl_scache))
           res = CURLSHE_NOMEM;
       }
@@ -147,7 +281,6 @@
   case CURLSHOPT_UNSHARE:
     /* this is a type this share will no longer share */
     type = va_arg(param, int);
-    share->specifier &= ~(unsigned int)(1 << type);
     switch(type) {
     case CURL_LOCK_DATA_DNS:
       break;
@@ -191,6 +324,8 @@
       res = CURLSHE_BAD_OPTION;
       break;
     }
+    if(!res)
+      share->specifier &= ~(unsigned int)(1 << type);
     break;
 
   case CURLSHOPT_LOCKFUNC:
@@ -221,48 +356,15 @@
 CURLSHcode curl_share_cleanup(CURLSH *sh)
 {
   struct Curl_share *share = sh;
+  bool locked;
   if(!GOOD_SHARE_HANDLE(share))
     return CURLSHE_INVALID;
 
-  if(share->lockfunc)
-    share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE,
-                    share->clientdata);
-
-  if(share->dirty) {
-    if(share->unlockfunc)
-      share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
+  if(share_in_use(share))
     return CURLSHE_IN_USE;
-  }
 
-  if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
-    Curl_cpool_destroy(&share->cpool);
-  }
-
-  Curl_dnscache_destroy(&share->dnscache);
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-  Curl_cookie_cleanup(share->cookies);
-#endif
-
-#ifndef CURL_DISABLE_HSTS
-  Curl_hsts_cleanup(&share->hsts);
-#endif
-
-#ifdef USE_SSL
-  if(share->ssl_scache) {
-    Curl_ssl_scache_destroy(share->ssl_scache);
-    share->ssl_scache = NULL;
-  }
-#endif
-
-  Curl_psl_destroy(&share->psl);
-  Curl_close(&share->admin);
-
-  if(share->unlockfunc)
-    share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
-  share->magic = 0;
-  curlx_free(share);
-
+  locked = share_lock_acquire(share, NULL);
+  share_unlink(&share, NULL, locked);
   return CURLSHE_OK;
 }
 
@@ -297,3 +399,74 @@
 
   return CURLSHE_OK;
 }
+
+CURLcode Curl_share_easy_unlink(struct Curl_easy *data)
+{
+  struct Curl_share *share = data->share;
+
+  if(share) {
+    bool locked = share_lock_acquire(share, data);
+
+    /* If data has a connection from this share, detach it. */
+    if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT)))
+      Curl_detach_connection(data);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+    if(share->cookies == data->cookies)
+      data->cookies = NULL;
+#endif
+
+#ifndef CURL_DISABLE_HSTS
+    if(share->hsts == data->hsts)
+      data->hsts = NULL;
+#endif
+#ifdef USE_LIBPSL
+    if(&share->psl == data->psl)
+      data->psl = data->multi ? &data->multi->psl : NULL;
+#endif
+
+    share_unlink(&data->share, data, locked);
+  }
+  return CURLE_OK;
+}
+
+CURLcode Curl_share_easy_link(struct Curl_easy *data,
+                              struct Curl_share *share)
+{
+  if(data->share) {
+    DEBUGASSERT(0);
+    return CURLE_FAILED_INIT;
+  }
+
+  if(share) {
+    bool locked = share_lock_acquire(share, data);
+
+    share_ref_inc(share);
+    data->share = share;
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
+    if(share->cookies) {
+      /* use shared cookie list, first free own one if any */
+      Curl_cookie_cleanup(data->cookies);
+      /* enable cookies since we now use a share that uses cookies! */
+      data->cookies = share->cookies;
+    }
+#endif /* CURL_DISABLE_HTTP */
+#ifndef CURL_DISABLE_HSTS
+    if(share->hsts) {
+      /* first free the private one if any */
+      Curl_hsts_cleanup(&data->hsts);
+      data->hsts = share->hsts;
+    }
+#endif
+#ifdef USE_LIBPSL
+    if(share->specifier & (1 << CURL_LOCK_DATA_PSL))
+      data->psl = &share->psl;
+#endif
+
+    /* check for host cache not needed,
+     * it will be done by curl_easy_perform */
+    share_lock_release(share, data, locked);
+  }
+  return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/lib/curl_share.h b/Utilities/cmcurl/lib/curl_share.h
index 5e3f82a..69001be 100644
--- a/Utilities/cmcurl/lib/curl_share.h
+++ b/Utilities/cmcurl/lib/curl_share.h
@@ -25,6 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
+#include "curl_threads.h"
 #include "cookie.h"
 #include "psl.h"
 #include "urldata.h"
@@ -43,12 +44,22 @@
 struct Curl_share {
   unsigned int magic; /* CURL_GOOD_SHARE */
   unsigned int specifier;
-  volatile unsigned int dirty;
 
+  uint32_t ref_count;
+#ifdef USE_MUTEX
+   /* do `ref_count` and `has_been_shared` checks using this mutex. */
+  curl_mutex_t lock;
+  int has_been_shared;
+#else
+  /* this only ever goes from FALSE -> TRUE once. We need to check
+   * this without being able to use the `lockfunc`. */
+  volatile int has_been_shared;
+#endif
   curl_lock_function lockfunc;
   curl_unlock_function unlockfunc;
   void *clientdata;
   struct Curl_easy *admin;
+
   struct cpool cpool;
   struct Curl_dnscache dnscache; /* DNS cache */
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
@@ -74,4 +85,8 @@
                                     ((data)->share->specifier &          \
                                      (1 << CURL_LOCK_DATA_SSL_SESSION)))
 
+CURLcode Curl_share_easy_unlink(struct Curl_easy *data);
+CURLcode Curl_share_easy_link(struct Curl_easy *data,
+                              struct Curl_share *share);
+
 #endif /* HEADER_CURL_SHARE_H */
diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c
index 1d4cf92..3ea1762 100644
--- a/Utilities/cmcurl/lib/curl_sspi.c
+++ b/Utilities/cmcurl/lib/curl_sspi.c
@@ -198,9 +198,9 @@
 void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity)
 {
   if(identity) {
-    Curl_safefree(identity->User);
-    Curl_safefree(identity->Password);
-    Curl_safefree(identity->Domain);
+    curlx_safefree(identity->User);
+    curlx_safefree(identity->Password);
+    curlx_safefree(identity->Domain);
   }
 }
 
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c
index f5fea98..01041f6 100644
--- a/Utilities/cmcurl/lib/curl_threads.c
+++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -22,14 +22,12 @@
  *
  ***************************************************************************/
 #include "curl_setup.h"
-
-#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-#include <pthread.h>
-#endif
-
 #include "curl_threads.h"
+#include "curlx/timeval.h"
 
-#ifdef USE_THREADS_POSIX
+#ifdef USE_THREADS
+
+#ifdef HAVE_THREADS_POSIX
 
 struct Curl_actual_call {
   unsigned int (*func)(void *);
@@ -49,8 +47,8 @@
   return 0;
 }
 
-curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
-                                 (CURL_STDCALL *func) (void *), void *arg)
+curl_thread_t Curl_thread_create(
+  CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg)
 {
   curl_thread_t t = curlx_malloc(sizeof(pthread_t));
   struct Curl_actual_call *ac = NULL;
@@ -97,10 +95,10 @@
   return ret;
 }
 
-#elif defined(USE_THREADS_WIN32)
+#elif defined(_WIN32)
 
-curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
-                                 (CURL_STDCALL *func) (void *), void *arg)
+curl_thread_t Curl_thread_create(
+  CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg)
 {
   curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL);
   if(!t) {
@@ -131,4 +129,86 @@
   return ret;
 }
 
-#endif /* USE_THREADS_* */
+#else
+#error neither HAVE_THREADS_POSIX nor _WIN32 defined
+#endif
+#endif /* USE_THREADS */
+
+#ifdef USE_MUTEX
+
+#ifdef HAVE_THREADS_POSIX
+
+void Curl_cond_signal(pthread_cond_t *c)
+{
+  /* return code defined as always 0 */
+  (void)pthread_cond_signal(c);
+}
+
+void Curl_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
+{
+  /* return code defined as always 0 */
+  (void)pthread_cond_wait(c, m);
+}
+
+CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m,
+                             uint32_t timeout_ms)
+{
+  struct curltime now;
+  struct timespec ts;
+  timediff_t usec;
+  int rc;
+
+  /* POSIX expects an "absolute" time until the condition wait ends.
+   * We cannot use `curlx_now()` here that may run on some monotonic clock
+   * that will be most likely in the past, as far as POSIX abstime is
+   * concerned. */
+#ifdef HAVE_GETTIMEOFDAY
+  struct timeval tv;
+  (void)gettimeofday(&tv, NULL);
+  now.tv_sec = tv.tv_sec;
+  now.tv_usec = (int)tv.tv_usec;
+#else
+  now.tv_sec = time(NULL);
+  now.tv_usec = 0;
+#endif
+
+  ts.tv_sec = now.tv_sec + (timeout_ms / 1000);
+  usec = now.tv_usec + ((timeout_ms % 1000) * 1000);
+  if(usec >= 1000000) {
+    ++ts.tv_sec;
+    usec %= 1000000;
+  }
+  ts.tv_nsec = (long)usec * 1000;
+
+  rc = pthread_cond_timedwait(c, m, &ts);
+  if(rc == SOCKETIMEDOUT)
+    return CURLE_OPERATION_TIMEDOUT;
+  return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK;
+}
+
+#elif defined(_WIN32)
+
+void Curl_cond_signal(CONDITION_VARIABLE *c)
+{
+  WakeConditionVariable(c);
+}
+
+void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m)
+{
+  SleepConditionVariableCS(c, m, INFINITE);
+}
+
+CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m,
+                             uint32_t timeout_ms)
+{
+  if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) {
+    DWORD err = GetLastError();
+    return (err == ERROR_TIMEOUT) ?
+           CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL;
+  }
+  return CURLE_OK;
+}
+#else
+#error neither HAVE_THREADS_POSIX nor _WIN32 defined
+#endif
+#endif /* USE_MUTEX */
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h
index 0e067c0..081d155 100644
--- a/Utilities/cmcurl/lib/curl_threads.h
+++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -25,7 +25,8 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#ifdef USE_THREADS_POSIX
+#ifdef USE_MUTEX
+#ifdef HAVE_THREADS_POSIX
 #  define CURL_THREAD_RETURN_T   unsigned int
 #  define CURL_STDCALL
 #  define curl_mutex_t           pthread_mutex_t
@@ -35,7 +36,10 @@
 #  define Curl_mutex_acquire(m)  pthread_mutex_lock(m)
 #  define Curl_mutex_release(m)  pthread_mutex_unlock(m)
 #  define Curl_mutex_destroy(m)  pthread_mutex_destroy(m)
-#elif defined(USE_THREADS_WIN32)
+#  define curl_cond_t            pthread_cond_t
+#  define Curl_cond_init(c)      pthread_cond_init(c, NULL)
+#  define Curl_cond_destroy(c)   pthread_cond_destroy(c)
+#elif defined(_WIN32)
 #  define CURL_THREAD_RETURN_T   DWORD
 #  define CURL_STDCALL           WINAPI
 #  define curl_mutex_t           CRITICAL_SECTION
@@ -45,17 +49,29 @@
 #  define Curl_mutex_acquire(m)  EnterCriticalSection(m)
 #  define Curl_mutex_release(m)  LeaveCriticalSection(m)
 #  define Curl_mutex_destroy(m)  DeleteCriticalSection(m)
+#  define curl_cond_t            CONDITION_VARIABLE
+#  define Curl_cond_init(c)      InitializeConditionVariable(c)
+#  define Curl_cond_destroy(c)   (void)(c)
+#else
+#error neither HAVE_THREADS_POSIX nor _WIN32 defined
 #endif
 
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+void Curl_cond_signal(curl_cond_t *c);
+void Curl_cond_wait(curl_cond_t *c, curl_mutex_t *m);
+/* Returns CURLE_OPERATION_TIMEDOUT on timeout */
+CURLcode Curl_cond_timedwait(curl_cond_t *c, curl_mutex_t *m,
+                             uint32_t timeout_ms);
+#endif /* USE_MUTEX */
 
-curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
-                                 (CURL_STDCALL *func) (void *), void *arg);
+#ifdef USE_THREADS
+
+curl_thread_t Curl_thread_create(
+  CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg);
 
 void Curl_thread_destroy(curl_thread_t *hnd);
 
 int Curl_thread_join(curl_thread_t *hnd);
 
-#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */
+#endif /* USE_THREADS */
 
 #endif /* HEADER_CURL_THREADS_H */
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c
index 4b8734a..c6115cf 100644
--- a/Utilities/cmcurl/lib/curl_trc.c
+++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -28,6 +28,7 @@
 #include "cfilters.h"
 #include "multiif.h"
 
+#include "cf-dns.h"
 #include "cf-socket.h"
 #include "connect.h"
 #include "http2.h"
@@ -222,6 +223,12 @@
   "TIMER",
   CURL_LOG_LVL_NONE,
 };
+#ifdef USE_THREADS
+struct curl_trc_feat Curl_trc_feat_threads = {
+  "THREADS",
+  CURL_LOG_LVL_NONE,
+};
+#endif
 #endif
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -278,6 +285,19 @@
   }
 }
 
+void Curl_trc_feat_infof(struct Curl_easy *data,
+                         struct curl_trc_feat *feat,
+                         const char *fmt, ...)
+{
+  DEBUGASSERT(feat);
+  if(Curl_trc_ft_is_verbose(data, feat)) {
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, feat, NULL, 0, fmt, ap);
+    va_end(ap);
+  }
+}
+
 static const char * const Curl_trc_timer_names[] = {
   "100_TIMEOUT",
   "ASYNC_NAME",
@@ -336,7 +356,6 @@
   "PENDING",
   "SETUP",
   "CONNECT",
-  "RESOLVING",
   "CONNECTING",
   "PROTOCONNECT",
   "PROTOCONNECTING",
@@ -512,6 +531,9 @@
   { &Curl_trc_feat_write,     TRC_CT_NONE },
   { &Curl_trc_feat_dns,       TRC_CT_NETWORK },
   { &Curl_trc_feat_timer,     TRC_CT_NETWORK },
+#ifdef USE_THREADS
+  { &Curl_trc_feat_threads,   TRC_CT_NONE },
+#endif
 #ifndef CURL_DISABLE_FTP
   { &Curl_trc_feat_ftp,       TRC_CT_PROTOCOL },
 #endif
@@ -535,6 +557,7 @@
 };
 
 static struct trc_cft_def trc_cfts[] = {
+  { &Curl_cft_dns,            TRC_CT_NETWORK },
   { &Curl_cft_tcp,            TRC_CT_NETWORK },
   { &Curl_cft_udp,            TRC_CT_NETWORK },
   { &Curl_cft_unix,           TRC_CT_NETWORK },
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h
index a82c945..b4ae8e5 100644
--- a/Utilities/cmcurl/lib/curl_trc.h
+++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -101,6 +101,10 @@
   int log_level;
 };
 
+void Curl_trc_feat_infof(struct Curl_easy *data,
+                         struct curl_trc_feat *feat,
+                         const char *fmt, ...) CURL_PRINTF(3, 4);
+
 #ifndef CURL_DISABLE_FTP
 extern struct curl_trc_feat Curl_trc_feat_ftp;
 void Curl_trc_ftp(struct Curl_easy *data,
@@ -304,6 +308,9 @@
 extern struct curl_trc_feat Curl_trc_feat_write;
 extern struct curl_trc_feat Curl_trc_feat_dns;
 extern struct curl_trc_feat Curl_trc_feat_timer;
+#ifdef USE_THREADS
+extern struct curl_trc_feat Curl_trc_feat_threads;
+#endif
 #endif
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
diff --git a/Utilities/cmcurl/lib/curlx/base64.c b/Utilities/cmcurl/lib/curlx/base64.c
index a7a6703..7f51576 100644
--- a/Utilities/cmcurl/lib/curlx/base64.c
+++ b/Utilities/cmcurl/lib/curlx/base64.c
@@ -149,7 +149,7 @@
     pos += 3 - padding;
   }
 
-  /* Zero terminate */
+  /* Null-terminate */
   *pos = '\0';
 
   /* Return the decoded data */
@@ -213,7 +213,7 @@
     }
   }
 
-  /* Zero terminate */
+  /* Null-terminate */
   *output = '\0';
 
   /* Return the pointer to the new data (allocated memory) */
diff --git a/Utilities/cmcurl/lib/curlx/curlx.h b/Utilities/cmcurl/lib/curlx/curlx.h
deleted file mode 100644
index 79cffc8..0000000
--- a/Utilities/cmcurl/lib/curlx/curlx.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef HEADER_CURL_CURLX_H
-#define HEADER_CURL_CURLX_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
- *
- ***************************************************************************/
-
-/*
- * Defines protos and includes all header files that provide the curlx_*
- * functions. The curlx_* functions are not part of the libcurl API, but are
- * stand-alone functions whose sources can be built and linked by apps if need
- * be.
- */
-
-#include "curlx/base64.h" /* for curlx_base64* */
-#include "curlx/basename.h" /* for curlx_basename() */
-#include "curlx/dynbuf.h" /* for curlx_dyn_*() */
-#include "curlx/fopen.h" /* for curlx_f*() */
-#include "curlx/inet_ntop.h" /* for curlx_inet_ntop() */
-#include "curlx/inet_pton.h" /* for curlx_inet_pton() */
-#include "curlx/multibyte.h" /* for curlx_convert_*() */
-#include "curlx/nonblock.h" /* for curlx_nonblock() */
-#include "curlx/strcopy.h" /* for curlx_strcopy() */
-#include "curlx/strdup.h" /* for curlx_memdup*() and curlx_tcsdup() */
-#include "curlx/strerr.h" /* for curlx_strerror() */
-#include "curlx/strparse.h" /* for curlx_str_* parsing functions */
-#include "curlx/timediff.h" /* for timediff_t type and related functions */
-#include "curlx/timeval.h" /* for curlx_now type and related functions */
-#include "curlx/version_win32.h" /* for curlx_verify_windows_version() */
-#include "curlx/wait.h" /* for curlx_wait_ms() */
-#include "curlx/winapi.h" /* for curlx_winapi_strerror() */
-
-#endif /* HEADER_CURL_CURLX_H */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.c b/Utilities/cmcurl/lib/curlx/dynbuf.c
index 7045a1f..1e5ea51 100644
--- a/Utilities/cmcurl/lib/curlx/dynbuf.c
+++ b/Utilities/cmcurl/lib/curlx/dynbuf.c
@@ -57,7 +57,7 @@
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
-  Curl_safefree(s->bufr);
+  curlx_safefree(s->bufr);
   s->leng = s->allc = 0;
 }
 
@@ -134,7 +134,7 @@
 
 /*
  * Specify the size of the tail to keep (number of bytes from the end of the
- * buffer). The rest will be dropped.
+ * buffer). The rest is dropped.
  */
 CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail)
 {
@@ -205,7 +205,7 @@
 
   if(str) {
     CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str));
-    curlx_free(str);
+    curl_free(str);
     return result;
   }
   /* If we failed, we cleanup the whole buffer and return error */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.h b/Utilities/cmcurl/lib/curlx/dynbuf.h
index 6f09e13..0cf4a2c 100644
--- a/Utilities/cmcurl/lib/curlx/dynbuf.h
+++ b/Utilities/cmcurl/lib/curlx/dynbuf.h
@@ -53,7 +53,7 @@
 
 /* returns 0 on success, -1 on error */
 /* The implementation of this function exists in mprintf.c */
-int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args);
 
 /* Take the buffer out of the dynbuf. Caller has ownership and
  * dynbuf resets to initial state. */
diff --git a/Utilities/cmcurl/lib/curlx/fopen.c b/Utilities/cmcurl/lib/curlx/fopen.c
index 6d0ef7e..6733010 100644
--- a/Utilities/cmcurl/lib/curlx/fopen.c
+++ b/Utilities/cmcurl/lib/curlx/fopen.c
@@ -94,7 +94,7 @@
  * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
  *
  * For example 'in' filename255chars in current directory C:\foo\bar is
- * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
+ * fixed as \\?\C:\foo\bar\filename255chars for 'out' which tells Windows
  * it is ok to access that filename even though the actual full path is longer
  * than 260 chars.
  *
@@ -296,7 +296,7 @@
 int curlx_win32_open(const char *filename, int oflag, ...)
 {
   int pmode = 0;
-  int result = -1;
+  int res = -1;
   TCHAR *fixed = NULL;
   const TCHAR *target = NULL;
 
@@ -316,7 +316,7 @@
       target = fixed;
     else
       target = filename_w;
-    errno = _wsopen_s(&result, target, oflag, _SH_DENYNO, pmode);
+    errno = _wsopen_s(&res, target, oflag, _SH_DENYNO, pmode);
     CURLX_FREE(filename_w);
   }
   else
@@ -327,16 +327,16 @@
     target = fixed;
   else
     target = filename;
-  errno = _sopen_s(&result, target, oflag, _SH_DENYNO, pmode);
+  errno = _sopen_s(&res, target, oflag, _SH_DENYNO, pmode);
 #endif
 
   CURLX_FREE(fixed);
-  return result;
+  return res;
 }
 
 FILE *curlx_win32_fopen(const char *filename, const char *mode)
 {
-  FILE *result = NULL;
+  FILE *file = NULL;
   TCHAR *fixed = NULL;
   const TCHAR *target = NULL;
 
@@ -348,7 +348,7 @@
       target = fixed;
     else
       target = filename_w;
-    result = _wfsopen(target, mode_w, _SH_DENYNO);
+    file = _wfsopen(target, mode_w, _SH_DENYNO);
   }
   else
     /* !checksrc! disable ERRNOVAR 1 */
@@ -360,11 +360,11 @@
     target = fixed;
   else
     target = filename;
-  result = _fsopen(target, mode, _SH_DENYNO);
+  file = _fsopen(target, mode, _SH_DENYNO);
 #endif
 
   CURLX_FREE(fixed);
-  return result;
+  return file;
 }
 
 #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 5)
@@ -374,7 +374,7 @@
 
 FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp)
 {
-  FILE *result = NULL;
+  FILE *file = NULL;
   TCHAR *fixed = NULL;
   const TCHAR *target = NULL;
 
@@ -386,7 +386,7 @@
       target = fixed;
     else
       target = filename_w;
-    errno = _wfreopen_s(&result, target, mode_w, fp);
+    errno = _wfreopen_s(&file, target, mode_w, fp);
   }
   else
     /* !checksrc! disable ERRNOVAR 1 */
@@ -398,16 +398,16 @@
     target = fixed;
   else
     target = filename;
-  errno = freopen_s(&result, target, mode, fp);
+  errno = freopen_s(&file, target, mode, fp);
 #endif
 
   CURLX_FREE(fixed);
-  return result;
+  return file;
 }
 
 int curlx_win32_stat(const char *path, curlx_struct_stat *buffer)
 {
-  int result = -1;
+  int res = -1;
   TCHAR *fixed = NULL;
   const TCHAR *target = NULL;
 
@@ -418,7 +418,7 @@
       target = fixed;
     else
       target = path_w;
-    result = _wstati64(target, buffer);
+    res = _wstati64(target, buffer);
     curlx_free(path_w);
   }
   else
@@ -429,17 +429,17 @@
     target = fixed;
   else
     target = path;
-  result = _stati64(target, buffer);
+  res = _stati64(target, buffer);
 #endif
 
   CURLX_FREE(fixed);
-  return result;
+  return res;
 }
 
 #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES) || \
   !defined(CURL_DISABLE_ALTSVC)
 /* rename() on Windows does not overwrite, so we cannot use it here.
-   MoveFileEx() will overwrite and is usually atomic but fails when there are
+   MoveFileEx() does overwrite and is usually atomic but fails when there are
    open handles to the file. */
 int curlx_win32_rename(const char *oldpath, const char *newpath)
 {
diff --git a/Utilities/cmcurl/lib/curlx/fopen.h b/Utilities/cmcurl/lib/curlx/fopen.h
index d89830e..b64fbf6 100644
--- a/Utilities/cmcurl/lib/curlx/fopen.h
+++ b/Utilities/cmcurl/lib/curlx/fopen.h
@@ -47,7 +47,7 @@
 #define curlx_fstat             _fstati64
 #define curlx_struct_stat       struct _stati64
 FILE *curlx_win32_fopen(const char *filename, const char *mode);
-FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fh);
+FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp);
 int curlx_win32_stat(const char *path, curlx_struct_stat *buffer);
 int curlx_win32_open(const char *filename, int oflag, ...);
 int curlx_win32_rename(const char *oldpath, const char *newpath);
diff --git a/Utilities/cmcurl/lib/curlx/inet_ntop.h b/Utilities/cmcurl/lib/curlx/inet_ntop.h
index 2e3419f..ba8299f 100644
--- a/Utilities/cmcurl/lib/curlx/inet_ntop.h
+++ b/Utilities/cmcurl/lib/curlx/inet_ntop.h
@@ -36,15 +36,15 @@
 #include <arpa/inet.h>
 #endif
 #ifdef __AMIGA__
-#define curlx_inet_ntop(af, addr, buf, size)                            \
-  (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)(buf),     \
+#define curlx_inet_ntop(af, src, buf, size)                            \
+  (char *)inet_ntop(af, CURL_UNCONST(src), (unsigned char *)(buf),     \
                     (curl_socklen_t)(size))
 #else
-#define curlx_inet_ntop(af, addr, buf, size)                            \
-  inet_ntop(af, addr, buf, (curl_socklen_t)(size))
+#define curlx_inet_ntop(af, src, buf, size)                            \
+  inet_ntop(af, src, buf, (curl_socklen_t)(size))
 #endif
 #else
-char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size);
+char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size);
 #endif /* HAVE_INET_NTOP */
 
 #endif /* HEADER_CURL_INET_NTOP_H */
diff --git a/Utilities/cmcurl/lib/curlx/inet_pton.c b/Utilities/cmcurl/lib/curlx/inet_pton.c
index d3c53fb..7994f25 100644
--- a/Utilities/cmcurl/lib/curlx/inet_pton.c
+++ b/Utilities/cmcurl/lib/curlx/inet_pton.c
@@ -170,7 +170,7 @@
   if(colonp) {
     /*
      * Since some memmove()'s erroneously fail to handle
-     * overlapping regions, we will do the shift by hand.
+     * overlapping regions, we do the shift by hand.
      */
     const ssize_t n = tp - colonp;
     ssize_t i;
diff --git a/Utilities/cmcurl/lib/curlx/snprintf.c b/Utilities/cmcurl/lib/curlx/snprintf.c
index c733252..911c42e 100644
--- a/Utilities/cmcurl/lib/curlx/snprintf.c
+++ b/Utilities/cmcurl/lib/curlx/snprintf.c
@@ -34,13 +34,13 @@
   if(!maxlen)
     return;
   va_start(ap, fmt);
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
   /* !checksrc! disable BANNEDFUNC 1 */
   (void)vsnprintf(buf, maxlen, fmt, ap);
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic pop
 #endif
   buf[maxlen - 1] = 0;
diff --git a/Utilities/cmcurl/lib/curlx/strcopy.c b/Utilities/cmcurl/lib/curlx/strcopy.c
index da137c3..3e58ea2 100644
--- a/Utilities/cmcurl/lib/curlx/strcopy.c
+++ b/Utilities/cmcurl/lib/curlx/strcopy.c
@@ -30,10 +30,10 @@
  *
  * Provide the target buffer @dest and size of the target buffer @dsize, If
  * the source string @src with its *string length* @slen fits in the target
- * buffer it will be copied there - including storing a null terminator.
+ * buffer it is copied there - including storing a null terminator.
  *
  * If the target buffer is too small, the copy is not performed but if the
- * target buffer has a non-zero size it will get a null terminator stored.
+ * target buffer has a non-zero size it gets a null terminator stored.
  */
 void curlx_strcopy(char *dest,      /* destination buffer */
                    size_t dsize,    /* size of target buffer */
diff --git a/Utilities/cmcurl/lib/curlx/strerr.c b/Utilities/cmcurl/lib/curlx/strerr.c
index 91a329e..b53173c 100644
--- a/Utilities/cmcurl/lib/curlx/strerr.c
+++ b/Utilities/cmcurl/lib/curlx/strerr.c
@@ -263,7 +263,7 @@
   *buf = '\0';
 
 #ifdef _WIN32
-  if((!strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) &&
+  if((strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) &&
 #ifdef USE_WINSOCK
      !get_winsock_error(err, buf, buflen) &&
 #endif
diff --git a/Utilities/cmcurl/lib/curlx/strparse.c b/Utilities/cmcurl/lib/curlx/strparse.c
index 138b3df..7866b20 100644
--- a/Utilities/cmcurl/lib/curlx/strparse.c
+++ b/Utilities/cmcurl/lib/curlx/strparse.c
@@ -35,6 +35,15 @@
   out->len = len;
 }
 
+/* remove bytes from the end of the string, never remove more bytes than what
+   the string holds! */
+void curlx_str_trim(struct Curl_str *out, size_t len)
+{
+  DEBUGASSERT(out);
+  DEBUGASSERT(out->len >= len);
+  out->len -= len;
+}
+
 /* Get a word until the first DELIM or end of string. At least one byte long.
    return non-zero on error */
 int curlx_str_until(const char **linep, struct Curl_str *out,
diff --git a/Utilities/cmcurl/lib/curlx/strparse.h b/Utilities/cmcurl/lib/curlx/strparse.h
index 514203d..c7801b2 100644
--- a/Utilities/cmcurl/lib/curlx/strparse.h
+++ b/Utilities/cmcurl/lib/curlx/strparse.h
@@ -44,6 +44,7 @@
 
 void curlx_str_init(struct Curl_str *out);
 void curlx_str_assign(struct Curl_str *out, const char *str, size_t len);
+void curlx_str_trim(struct Curl_str *out, size_t len);
 
 #define curlx_str(x)    ((x)->str)
 #define curlx_strlen(x) ((x)->len)
diff --git a/Utilities/cmcurl/lib/curlx/timeval.c b/Utilities/cmcurl/lib/curlx/timeval.c
index 435804c..2363e60 100644
--- a/Utilities/cmcurl/lib/curlx/timeval.c
+++ b/Utilities/cmcurl/lib/curlx/timeval.c
@@ -166,6 +166,8 @@
    */
   pnow->tv_sec = time(NULL);
   pnow->tv_usec = 0;
+  if(!pnow->tv_sec) /* avoid a `now` fully zero */
+    pnow->tv_usec = 1;
 }
 
 #endif
diff --git a/Utilities/cmcurl/lib/curlx/wait.c b/Utilities/cmcurl/lib/curlx/wait.c
index 5278426..e50a0f0 100644
--- a/Utilities/cmcurl/lib/curlx/wait.c
+++ b/Utilities/cmcurl/lib/curlx/wait.c
@@ -46,7 +46,7 @@
  * wait on, being used to delay execution. Winsock select() and poll() timeout
  * mechanisms need a valid socket descriptor in a not null file descriptor set
  * to work. Waiting indefinitely with this function is not allowed, a zero or
- * negative timeout value will return immediately. Timeout resolution,
+ * negative timeout value is returned immediately. Timeout resolution,
  * accuracy, as well as maximum supported value is system dependent, neither
  * factor is a critical issue for the intended use of this function in the
  * library.
diff --git a/Utilities/cmcurl/lib/curlx/winapi.c b/Utilities/cmcurl/lib/curlx/winapi.c
index 3417df2..f025ca4 100644
--- a/Utilities/cmcurl/lib/curlx/winapi.c
+++ b/Utilities/cmcurl/lib/curlx/winapi.c
@@ -44,7 +44,7 @@
     return NULL;
 
   /* We return the local codepage version of the error string because if it is
-     output to the user's terminal it will likely be with functions which
+     output to the user's terminal, it is likely done with functions which
      expect the local codepage (eg fprintf, failf, infof). */
   if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM |
                       FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
diff --git a/Utilities/cmcurl/lib/cw-out.c b/Utilities/cmcurl/lib/cw-out.c
index 554c32e..2af074e 100644
--- a/Utilities/cmcurl/lib/cw-out.c
+++ b/Utilities/cmcurl/lib/cw-out.c
@@ -31,7 +31,7 @@
 #include "transfer.h"
 #include "cw-out.h"
 #include "cw-pause.h"
-
+#include "progress.h"
 
 /**
  * OVERALL DESIGN of this client writer
@@ -211,7 +211,7 @@
   }
   else if(nwritten != blen) {
     failf(data, "Failure writing output to destination, "
-          "passed %zu returned %zd", blen, nwritten);
+          "passed %zu returned %zu", blen, nwritten);
     return CURLE_WRITE_ERROR;
   }
   *pnwritten = nwritten;
@@ -228,8 +228,8 @@
   curl_write_callback wcb = NULL;
   void *wcb_data;
   size_t max_write, min_write;
-  size_t wlen, nwritten;
-  CURLcode result;
+  size_t wlen, nwritten = 0;
+  CURLcode result = CURLE_OK;
 
   /* If we errored once, we do not invoke the client callback again */
   if(ctx->errored)
@@ -253,10 +253,15 @@
       if(!flush_all && blen < min_write)
         break;
       wlen = max_write ? CURLMIN(blen, max_write) : blen;
-      result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
-                               buf, wlen, &nwritten);
+      if(otype == CW_OUT_BODY)
+        result = Curl_pgrs_deliver_check(data, wlen);
+      if(!result)
+        result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
+                                 buf, wlen, &nwritten);
       if(result)
         return result;
+      if(otype == CW_OUT_BODY)
+        Curl_pgrs_deliver_inc(data, nwritten);
       *pconsumed += nwritten;
       blen -= nwritten;
       buf += nwritten;
@@ -350,8 +355,8 @@
   }
 
   /* if we do not have a buffer, or it is of another type, make a new one.
-   * And for CW_OUT_HDS always make a new one, so we "replay" headers
-   * exactly as they came in */
+   * For CW_OUT_HDS always make a new one, so we "replay" headers exactly
+   * as they came in */
   if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) {
     struct cw_out_buf *cwbuf = cw_out_buf_create(otype);
     if(!cwbuf)
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index fcdea91..7b83c6c 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -64,7 +64,6 @@
 #define DICT_DEFINE2 "/D:"
 #define DICT_DEFINE3 "/LOOKUP:"
 
-
 #define DYN_DICT_WORD 10000
 static char *unescape_word(const char *input)
 {
@@ -275,7 +274,7 @@
 /*
  * DICT protocol
  */
-static const struct Curl_protocol Curl_protocol_dict = {
+const struct Curl_protocol Curl_protocol_dict = {
   ZERO_NULL,                            /* setup_connection */
   dict_do,                              /* do_it */
   ZERO_NULL,                            /* done */
@@ -290,25 +289,9 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
 #endif /* CURL_DISABLE_DICT */
-
-/*
- * DICT protocol handler.
- */
-const struct Curl_scheme Curl_scheme_dict = {
-  "dict",                               /* scheme */
-#ifdef CURL_DISABLE_DICT
-  ZERO_NULL,
-#else
-  &Curl_protocol_dict,
-#endif
-  CURLPROTO_DICT,                       /* protocol */
-  CURLPROTO_DICT,                       /* family */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY,    /* flags */
-  PORT_DICT,                            /* defport */
-};
diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h
index b5d6947..5bdfcff 100644
--- a/Utilities/cmcurl/lib/dict.h
+++ b/Utilities/cmcurl/lib/dict.h
@@ -23,6 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_dict;
+#ifndef CURL_DISABLE_DICT
+extern const struct Curl_protocol Curl_protocol_dict;
+#endif
 
 #endif /* HEADER_CURL_DICT_H */
diff --git a/Utilities/cmcurl/lib/dnscache.c b/Utilities/cmcurl/lib/dnscache.c
new file mode 100644
index 0000000..20f6b21
--- /dev/null
+++ b/Utilities/cmcurl/lib/dnscache.c
@@ -0,0 +1,873 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "curl_share.h"
+#include "curl_trc.h"
+#include "dnscache.h"
+#include "hash.h"
+#include "hostip.h"
+#include "httpsrr.h"
+#include "progress.h"
+#include "rand.h"
+#include "strcase.h"
+#include "curlx/inet_ntop.h"
+#include "curlx/inet_pton.h"
+#include "curlx/strcopy.h"
+#include "curlx/strparse.h"
+
+#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
+
+#define MAX_DNS_CACHE_SIZE 29999
+
+static void dnscache_entry_free(struct Curl_dns_entry *dns)
+{
+  Curl_freeaddrinfo(dns->addr);
+#ifdef USE_HTTPSRR
+  if(dns->hinfo) {
+    Curl_httpsrr_cleanup(dns->hinfo);
+    curlx_free(dns->hinfo);
+  }
+#endif
+  curlx_free(dns);
+}
+
+/*
+ * Create a hostcache id string for the provided host + port, to be used by
+ * the DNS caching. Without alloc. Return length of the id string.
+ */
+static size_t create_dnscache_id(const char *name,
+                                 size_t nlen, /* 0 or actual name length */
+                                 uint16_t port, char *ptr, size_t buflen)
+{
+  size_t len = nlen ? nlen : strlen(name);
+  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
+  if(len > (buflen - 7))
+    len = buflen - 7;
+  /* store and lower case the name */
+  Curl_strntolower(ptr, name, len);
+  return curl_msnprintf(&ptr[len], 7, ":%u", port) + len;
+}
+
+struct dnscache_prune_data {
+  struct curltime now;
+  timediff_t oldest_ms; /* oldest time in cache not pruned. */
+  timediff_t max_age_ms;
+};
+
+/*
+ * This function is set as a callback to be called for every entry in the DNS
+ * cache when we want to prune old unused entries.
+ *
+ * Returning non-zero means remove the entry, return 0 to keep it in the
+ * cache.
+ */
+static int dnscache_entry_is_stale(void *datap, void *hc)
+{
+  struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap;
+  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc;
+
+  if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) {
+    /* get age in milliseconds */
+    timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp);
+    if(!dns->addr)
+      age *= 2; /* negative entries age twice as fast */
+    if(age >= prune->max_age_ms)
+      return TRUE;
+    if(age > prune->oldest_ms)
+      prune->oldest_ms = age;
+  }
+  return FALSE;
+}
+
+/*
+ * Prune the DNS cache. This assumes that a lock has already been taken.
+ * Returns the 'age' of the oldest still kept entry - in milliseconds.
+ */
+static timediff_t dnscache_prune(struct Curl_hash *hostcache,
+                                 timediff_t cache_timeout_ms,
+                                 struct curltime now)
+{
+  struct dnscache_prune_data user;
+
+  user.max_age_ms = cache_timeout_ms;
+  user.now = now;
+  user.oldest_ms = 0;
+
+  Curl_hash_clean_with_criterium(hostcache,
+                                 (void *)&user,
+                                 dnscache_entry_is_stale);
+
+  return user.oldest_ms;
+}
+
+static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
+{
+  if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
+    return &data->share->dnscache;
+  if(data->multi)
+    return &data->multi->dnscache;
+  return NULL;
+}
+
+static void dnscache_lock(struct Curl_easy *data,
+                          struct Curl_dnscache *dnscache)
+{
+  if(data->share && dnscache == &data->share->dnscache)
+    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+}
+
+static void dnscache_unlock(struct Curl_easy *data,
+                            struct Curl_dnscache *dnscache)
+{
+  if(data->share && dnscache == &data->share->dnscache)
+    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
+/*
+ * Library-wide function for pruning the DNS cache. This function takes and
+ * returns the appropriate locks.
+ */
+void Curl_dnscache_prune(struct Curl_easy *data)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  /* the timeout may be set -1 (forever) */
+  timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
+
+  if(!dnscache || (timeout_ms == -1))
+    /* NULL hostcache means we cannot do it */
+    return;
+
+  dnscache_lock(data, dnscache);
+
+  do {
+    /* Remove outdated and unused entries from the hostcache */
+    timediff_t oldest_ms =
+      dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data));
+
+    if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
+      /* prune the ones over half this age */
+      timeout_ms = oldest_ms / 2;
+    else
+      break;
+
+    /* if the cache size is still too big, use the oldest age as new prune
+       limit */
+  } while(timeout_ms);
+
+  dnscache_unlock(data, dnscache);
+}
+
+void Curl_dnscache_clear(struct Curl_easy *data)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  if(dnscache) {
+    dnscache_lock(data, dnscache);
+    Curl_hash_clean(&dnscache->entries);
+    dnscache_unlock(data, dnscache);
+  }
+}
+
+/* lookup address, returns entry if found and not stale */
+static CURLcode fetch_addr(struct Curl_easy *data,
+                           struct Curl_dnscache *dnscache,
+                           uint8_t dns_queries,
+                           const char *hostname,
+                           uint16_t port,
+                           struct Curl_dns_entry **pdns)
+{
+  struct Curl_dns_entry *dns = NULL;
+  char entry_id[MAX_HOSTCACHE_LEN];
+  size_t entry_len;
+  CURLcode result = CURLE_OK;
+
+  *pdns = NULL;
+  if(!dnscache)
+    return CURLE_OK;
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_len = create_dnscache_id(hostname, 0, port,
+                                 entry_id, sizeof(entry_id));
+
+  /* See if it is already in our dns cache */
+  dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
+
+  /* No entry found in cache, check if we might have a wildcard entry */
+  if(!dns && data->state.wildcard_resolve) {
+    entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
+
+    /* See if it is already in our dns cache */
+    dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
+  }
+
+  if(dns && (data->set.dns_cache_timeout_ms != -1)) {
+    /* See whether the returned entry is stale. Done before we release lock */
+    struct dnscache_prune_data user;
+
+    user.now = *Curl_pgrs_now(data);
+    user.max_age_ms = data->set.dns_cache_timeout_ms;
+    user.oldest_ms = 0;
+
+    if(dnscache_entry_is_stale(&user, dns)) {
+      infof(data, "Hostname in DNS cache was stale, zapped");
+      dns = NULL; /* the memory deallocation is being handled by the hash */
+      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+    }
+  }
+
+  if(dns) {
+    if((dns->dns_queries & dns_queries) != dns_queries) {
+      /* The entry does not cover all wanted DNS queries, a miss. */
+      dns = NULL;
+    }
+    else if(!(dns->dns_responses & dns_queries)) {
+      /* The entry has no responses for the wanted DNS queries. */
+      CURL_TRC_DNS(data, "cache entry does not have type=%s addresses",
+                   Curl_resolv_query_str(dns_queries));
+      dns = NULL;
+      result = CURLE_COULDNT_RESOLVE_HOST;
+    }
+  }
+
+  if(dns && !dns->addr) { /* negative entry */
+    dns = NULL;
+    result = CURLE_COULDNT_RESOLVE_HOST;
+  }
+  *pdns = dns;
+  return result;
+}
+
+/*
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Curl_resolv() checks initially and multi_runsingle() checks each time
+ * it discovers the handle in the state WAITRESOLVE whether the hostname
+ * has already been resolved and the address has already been stored in
+ * the DNS cache. This short circuits waiting for a lot of pending
+ * lookups for the same hostname requested by different handles.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
+ * use, or we will leak memory!
+ */
+CURLcode Curl_dnscache_get(struct Curl_easy *data,
+                           uint8_t dns_queries,
+                           const char *hostname,
+                           uint16_t port,
+                           struct Curl_dns_entry **pentry)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  struct Curl_dns_entry *dns = NULL;
+  CURLcode result = CURLE_OK;
+
+  dnscache_lock(data, dnscache);
+  result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns);
+  if(!result && dns)
+    dns->refcount++; /* we pass out a reference */
+  else if(result) {
+    DEBUGASSERT(!dns);
+    dns = NULL;
+  }
+  dnscache_unlock(data, dnscache);
+
+  *pentry = dns;
+  return result;
+}
+
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+/*
+ * Return # of addresses in a Curl_addrinfo struct
+ */
+static int num_addresses(const struct Curl_addrinfo *addr)
+{
+  int i = 0;
+  while(addr) {
+    addr = addr->ai_next;
+    i++;
+  }
+  return i;
+}
+
+/*
+ * dns_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
+ * struct by re-linking its linked list.
+ *
+ * The addr argument should be the address of a pointer to the head node of a
+ * `Curl_addrinfo` list and it will be modified to point to the new head after
+ * shuffling.
+ *
+ * Not declared static only to make it easy to use in a unit test!
+ *
+ * @unittest 1608
+ */
+UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data,
+                                   struct Curl_addrinfo **addr);
+UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data,
+                                   struct Curl_addrinfo **addr)
+{
+  CURLcode result = CURLE_OK;
+  const int num_addrs = num_addresses(*addr);
+
+  if(num_addrs > 1) {
+    struct Curl_addrinfo **nodes;
+    CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs);
+
+    nodes = curlx_malloc(num_addrs * sizeof(*nodes));
+    if(nodes) {
+      int i;
+      unsigned int *rnd;
+      const size_t rnd_size = num_addrs * sizeof(*rnd);
+
+      /* build a plain array of Curl_addrinfo pointers */
+      nodes[0] = *addr;
+      for(i = 1; i < num_addrs; i++) {
+        nodes[i] = nodes[i - 1]->ai_next;
+      }
+
+      rnd = curlx_malloc(rnd_size);
+      if(rnd) {
+        /* Fisher-Yates shuffle */
+        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
+          struct Curl_addrinfo *swap_tmp;
+          for(i = num_addrs - 1; i > 0; i--) {
+            swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
+            nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
+            nodes[i] = swap_tmp;
+          }
+
+          /* relink list in the new order */
+          for(i = 1; i < num_addrs; i++) {
+            nodes[i - 1]->ai_next = nodes[i];
+          }
+
+          nodes[num_addrs - 1]->ai_next = NULL;
+          *addr = nodes[0];
+        }
+        curlx_free(rnd);
+      }
+      else
+        result = CURLE_OUT_OF_MEMORY;
+      curlx_free(nodes);
+    }
+    else
+      result = CURLE_OUT_OF_MEMORY;
+  }
+  return result;
+}
+#endif
+
+static bool dnscache_ai_has_family(struct Curl_addrinfo *ai,
+                                   int ai_family)
+{
+  for(; ai; ai = ai->ai_next) {
+    if(ai->ai_family == ai_family)
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static struct Curl_dns_entry *dnscache_entry_create(
+  struct Curl_easy *data,
+  uint8_t dns_queries,
+  struct Curl_addrinfo **paddr1,
+  struct Curl_addrinfo **paddr2,
+  const char *hostname,
+  size_t hostlen,
+  uint16_t port,
+  bool permanent)
+{
+  struct Curl_dns_entry *dns = NULL;
+
+  /* Create a new cache entry, struct already has the hostname NUL */
+  dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
+  if(!dns)
+    goto out;
+
+  dns->refcount = 1; /* the cache has the first reference */
+  dns->dns_queries = dns_queries;
+  dns->port = port;
+  if(hostlen)
+    memcpy(dns->hostname, hostname, hostlen);
+
+  if(permanent) {
+    dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
+    dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
+  }
+  else {
+    dns->timestamp = *Curl_pgrs_now(data);
+  }
+
+  /* Take the given address lists into the entry */
+  if(paddr1 && *paddr1) {
+    dns->addr = *paddr1;
+    *paddr1 = NULL;
+  }
+  if(paddr2 && *paddr2) {
+    struct Curl_addrinfo **phead = &dns->addr;
+    while(*phead)
+      phead = &(*phead)->ai_next;
+    *phead = *paddr2;
+    *paddr2 = NULL;
+  }
+
+  if((dns_queries & CURL_DNSQ_A) &&
+     dnscache_ai_has_family(dns->addr, PF_INET))
+    dns->dns_responses |= CURL_DNSQ_A;
+
+#ifdef USE_IPV6
+  if((dns_queries & CURL_DNSQ_AAAA) &&
+     dnscache_ai_has_family(dns->addr, PF_INET6))
+    dns->dns_responses |= CURL_DNSQ_AAAA;
+#endif /* USE_IPV6 */
+
+#ifndef CURL_DISABLE_SHUFFLE_DNS
+  /* shuffle addresses if requested */
+  if(data->set.dns_shuffle_addresses && dns->addr) {
+    CURLcode result = dns_shuffle_addr(data, &dns->addr);
+    if(result) {
+      /* free without lock, we are the sole owner */
+      dnscache_entry_free(dns);
+      dns = NULL;
+      goto out;
+    }
+  }
+#else
+  (void)data;
+#endif
+
+out:
+  if(paddr1 && *paddr1) {
+    Curl_freeaddrinfo(*paddr1);
+    *paddr1 = NULL;
+  }
+  if(paddr2 && *paddr2) {
+    Curl_freeaddrinfo(*paddr2);
+    *paddr2 = NULL;
+  }
+  return dns;
+}
+
+struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data,
+                                              uint8_t dns_queries,
+                                              struct Curl_addrinfo **paddr,
+                                              const char *hostname,
+                                              uint16_t port)
+{
+  return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname,
+                               hostname ? strlen(hostname) : 0,
+                               port, FALSE);
+}
+
+struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data,
+                                               uint8_t dns_queries,
+                                               struct Curl_addrinfo **paddr1,
+                                               struct Curl_addrinfo **paddr2,
+                                               const char *hostname,
+                                               uint16_t port)
+{
+  return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname,
+                               hostname ? strlen(hostname) : 0,
+                               port, FALSE);
+}
+
+#ifdef USE_HTTPSRR
+void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
+                                 struct Curl_https_rrinfo *hinfo)
+{
+  /* only do this when this is the only reference */
+  DEBUGASSERT(dns->refcount == 1);
+  /* it should have been in the queries */
+  DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS);
+  if(dns->hinfo) {
+    Curl_httpsrr_cleanup(dns->hinfo);
+    curlx_free(dns->hinfo);
+  }
+  dns->hinfo = hinfo;
+  dns->dns_responses |= CURL_DNSQ_HTTPS;
+}
+#endif /* USE_HTTPSRR */
+
+static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data,
+                                                struct Curl_dnscache *dnscache,
+                                                uint8_t dns_queries,
+                                                struct Curl_addrinfo **paddr,
+                                                const char *hostname,
+                                                size_t hlen,
+                                                uint16_t port,
+                                                bool permanent)
+{
+  char entry_id[MAX_HOSTCACHE_LEN];
+  size_t entry_len;
+  struct Curl_dns_entry *dns;
+  struct Curl_dns_entry *dns2;
+
+  dns = dnscache_entry_create(data, dns_queries, paddr, NULL,
+                              hostname, hlen, port, permanent);
+  if(!dns)
+    return NULL;
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_len = create_dnscache_id(hostname, hlen, port,
+                                 entry_id, sizeof(entry_id));
+
+  /* Store the resolved data in our DNS cache. */
+  dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
+                       (void *)dns);
+  if(!dns2) {
+    dnscache_entry_free(dns);
+    return NULL;
+  }
+
+  dns = dns2;
+  dns->refcount++;         /* mark entry as in-use */
+  return dns;
+}
+
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+                           struct Curl_dns_entry *entry)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  char id[MAX_HOSTCACHE_LEN];
+  size_t idlen;
+
+  if(!dnscache)
+    return CURLE_FAILED_INIT;
+  /* Create an entry id, based upon the hostname and port */
+  idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id));
+
+  /* Store the resolved data in our DNS cache and up ref count */
+  dnscache_lock(data, dnscache);
+  if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
+    dnscache_unlock(data, dnscache);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  entry->refcount++;
+  dnscache_unlock(data, dnscache);
+  return CURLE_OK;
+}
+
+CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
+                                    uint8_t dns_queries,
+                                    const char *host,
+                                    uint16_t port)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  struct Curl_dns_entry *dns;
+  DEBUGASSERT(dnscache);
+  if(!dnscache)
+    return CURLE_FAILED_INIT;
+
+  dnscache_lock(data, dnscache);
+
+  /* put this new host in the cache */
+  dns = dnscache_add_addr(data, dnscache, dns_queries, NULL,
+                          host, strlen(host), port, FALSE);
+  if(dns) {
+    /* release the returned reference; the cache itself will keep the
+     * entry alive: */
+    dns->refcount--;
+    dnscache_unlock(data, dnscache);
+    CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s",
+                 host, port, Curl_resolv_query_str(dns_queries));
+    return CURLE_OK;
+  }
+  dnscache_unlock(data, dnscache);
+  return CURLE_OUT_OF_MEMORY;
+}
+
+struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
+                                           struct Curl_dns_entry *dns)
+{
+  if(!dns)
+    return NULL;
+  else {
+    struct Curl_dnscache *dnscache = dnscache_get(data);
+    dnscache_lock(data, dnscache);
+    dns->refcount++;
+    dnscache_unlock(data, dnscache);
+    return dns;
+  }
+}
+
+/*
+ * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry.
+ * When the reference count reaches 0, the entry is destroyed. It is important
+ * that only one unlink is made for each Curl_resolv() call.
+ *
+ * May be called with 'data' == NULL for global cache.
+ */
+void Curl_dns_entry_unlink(struct Curl_easy *data,
+                           struct Curl_dns_entry **pdns)
+{
+  if(*pdns) {
+    struct Curl_dnscache *dnscache = dnscache_get(data);
+    struct Curl_dns_entry *dns = *pdns;
+    *pdns = NULL;
+    dnscache_lock(data, dnscache);
+    dns->refcount--;
+    if(dns->refcount == 0)
+      dnscache_entry_free(dns);
+    dnscache_unlock(data, dnscache);
+  }
+}
+
+static void dnscache_entry_dtor(void *entry)
+{
+  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry;
+  DEBUGASSERT(dns && (dns->refcount > 0));
+  dns->refcount--;
+  if(dns->refcount == 0)
+    dnscache_entry_free(dns);
+}
+
+/*
+ * Curl_dnscache_init() inits a new DNS cache.
+ */
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
+{
+  Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
+                 dnscache_entry_dtor);
+}
+
+void Curl_dnscache_destroy(struct Curl_dnscache *dns)
+{
+  Curl_hash_destroy(&dns->entries);
+}
+
+CURLcode Curl_loadhostpairs(struct Curl_easy *data)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  struct curl_slist *hostp;
+
+  if(!dnscache)
+    return CURLE_FAILED_INIT;
+
+  /* Default is no wildcard found */
+  data->state.wildcard_resolve = FALSE;
+
+  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
+    char entry_id[MAX_HOSTCACHE_LEN];
+    const char *host = hostp->data;
+    struct Curl_str source;
+    if(!host)
+      continue;
+    if(*host == '-') {
+      curl_off_t num = 0;
+      size_t entry_len;
+      host++;
+      if(!curlx_str_single(&host, '[')) {
+        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           curlx_str_single(&host, ']') ||
+           curlx_str_single(&host, ':'))
+          continue;
+      }
+      else {
+        if(curlx_str_until(&host, &source, 4096, ':') ||
+           curlx_str_single(&host, ':')) {
+          continue;
+        }
+      }
+
+      if(!curlx_str_number(&host, &num, 0xffff)) {
+        /* Create an entry id, based upon the hostname and port */
+        entry_len = create_dnscache_id(curlx_str(&source),
+                                       curlx_strlen(&source), (uint16_t)num,
+                                       entry_id, sizeof(entry_id));
+        dnscache_lock(data, dnscache);
+        /* delete entry, ignore if it did not exist */
+        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+        dnscache_unlock(data, dnscache);
+      }
+    }
+    else {
+      struct Curl_dns_entry *dns;
+      struct Curl_addrinfo *head = NULL, *tail = NULL;
+      size_t entry_len;
+      char address[64];
+      curl_off_t tmpofft = 0;
+      uint16_t port = 0;
+      bool permanent = TRUE;
+      bool error = TRUE;
+      VERBOSE(const char *addresses = NULL);
+
+      if(*host == '+') {
+        host++;
+        permanent = FALSE;
+      }
+      if(!curlx_str_single(&host, '[')) {
+        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           curlx_str_single(&host, ']'))
+          continue;
+      }
+      else {
+        if(curlx_str_until(&host, &source, 4096, ':'))
+          continue;
+      }
+      if(curlx_str_single(&host, ':') ||
+         curlx_str_number(&host, &tmpofft, 0xffff) ||
+         curlx_str_single(&host, ':'))
+        goto err;
+      port = (uint16_t)tmpofft;
+
+      VERBOSE(addresses = host);
+
+      /* start the address section */
+      while(*host) {
+        struct Curl_str target;
+        struct Curl_addrinfo *ai;
+        CURLcode result;
+
+        if(!curlx_str_single(&host, '[')) {
+          if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
+             curlx_str_single(&host, ']'))
+            goto err;
+        }
+        else {
+          if(curlx_str_until(&host, &target, 4096, ',')) {
+            if(curlx_str_single(&host, ','))
+              goto err;
+            /* survive nothing but a comma */
+            continue;
+          }
+        }
+#ifndef USE_IPV6
+        if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) {
+          infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.",
+                (int)curlx_strlen(&target), curlx_str(&target));
+          if(curlx_str_single(&host, ','))
+            goto err;
+          continue;
+        }
+#endif
+
+        if(curlx_strlen(&target) >= sizeof(address))
+          goto err;
+
+        memcpy(address, curlx_str(&target), curlx_strlen(&target));
+        address[curlx_strlen(&target)] = '\0';
+
+        result = Curl_str2addr(address, port, &ai);
+        if(result) {
+          infof(data, "Resolve address '%s' found illegal", address);
+          goto err;
+        }
+
+        if(tail) {
+          tail->ai_next = ai;
+          tail = tail->ai_next;
+        }
+        else {
+          head = tail = ai;
+        }
+        if(curlx_str_single(&host, ','))
+          break;
+      }
+
+      if(!head)
+        goto err;
+
+      error = FALSE;
+err:
+      if(error) {
+        failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data);
+        Curl_freeaddrinfo(head);
+        return CURLE_SETOPT_OPTION_SYNTAX;
+      }
+
+      /* Create an entry id, based upon the hostname and port */
+      entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
+                                     port, entry_id, sizeof(entry_id));
+
+      dnscache_lock(data, dnscache);
+
+      /* See if it is already in our dns cache */
+      dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
+
+      if(dns) {
+        infof(data, "RESOLVE %.*s:%u - old addresses discarded",
+              (int)curlx_strlen(&source),
+              curlx_str(&source), port);
+        /* delete old entry, there are two reasons for this
+         1. old entry may have different addresses.
+         2. even if entry with correct addresses is already in the cache,
+            but if it is close to expire, then by the time next http
+            request is made, it can get expired and pruned because old
+            entry is not necessarily marked as permanent.
+         3. when adding a non-permanent entry, we want it to remove and
+            replace an existing permanent entry.
+         4. when adding a non-permanent entry, we want it to get a "fresh"
+            timeout that starts _now_. */
+
+        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+      }
+
+      /* put this new host in the cache, an overridy for ALL dns queries */
+      dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL,
+                              &head, curlx_str(&source),
+                              curlx_strlen(&source), port, permanent);
+      if(dns)
+        /* release the returned reference; the cache itself will keep the
+         * entry alive: */
+        dns->refcount--;
+
+      dnscache_unlock(data, dnscache);
+
+      if(!dns)
+        return CURLE_OUT_OF_MEMORY;
+
+      infof(data, "Added %.*s:%u:%s to DNS cache%s",
+            (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
+            permanent ? "" : " (non-permanent)");
+
+      /* Wildcard hostname */
+      if(curlx_str_casecompare(&source, "*")) {
+        infof(data, "RESOLVE *:%u using wildcard", port);
+        data->state.wildcard_resolve = TRUE;
+      }
+    }
+  }
+  data->state.resolve = NULL; /* dealt with now */
+
+  return CURLE_OK;
+}
diff --git a/Utilities/cmcurl/lib/dnscache.h b/Utilities/cmcurl/lib/dnscache.h
new file mode 100644
index 0000000..ebe25f6
--- /dev/null
+++ b/Utilities/cmcurl/lib/dnscache.h
@@ -0,0 +1,143 @@
+#ifndef HEADER_CURL_DNSCACHE_H
+#define HEADER_CURL_DNSCACHE_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 "hash.h"
+#include "curlx/timeval.h"
+
+struct addrinfo;
+struct hostent;
+struct Curl_easy;
+struct connectdata;
+struct easy_pollset;
+struct Curl_https_rrinfo;
+struct Curl_multi;
+
+struct Curl_dns_entry {
+  struct Curl_addrinfo *addr;
+#ifdef USE_HTTPSRR
+  struct Curl_https_rrinfo *hinfo;
+#endif
+  /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
+  struct curltime timestamp;
+  /* reference counter, entry is freed on reaching 0 */
+  uint32_t refcount;
+  /* hostname port number that resolved to addr. */
+  uint16_t port;
+  uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */
+  uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */
+  /* hostname that resolved to addr. may be NULL (Unix domain sockets). */
+  char hostname[1];
+};
+
+/*
+ * Create a `Curl_dns_entry` with a reference count of 1.
+ * Use `Curl_dns_entry_unlink()` to release your hold on it.
+ *
+ * The call takes ownership of `paddr`, even in case of failure, and always
+ * clears `*paddr`. It makes a copy of `hostname`.
+ *
+ * Returns entry or NULL on OOM.
+ */
+struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data,
+                                              uint8_t dns_queries,
+                                              struct Curl_addrinfo **paddr,
+                                              const char *hostname,
+                                              uint16_t port);
+
+struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data,
+                                               uint8_t dns_queries,
+                                               struct Curl_addrinfo **paddr1,
+                                               struct Curl_addrinfo **paddr2,
+                                               const char *hostname,
+                                               uint16_t port);
+
+#ifdef USE_HTTPSRR
+void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
+                                 struct Curl_https_rrinfo *hinfo);
+#endif /* USE_HTTPSRR */
+
+/* Increase the ref counter and return it for storing in another place.
+ * May be called with NULL, in which case it returns NULL. */
+struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
+                                           struct Curl_dns_entry *dns);
+
+/* unlink a dns entry, frees all resources if it was the last reference.
+ * Always clears `*pdns`` */
+void Curl_dns_entry_unlink(struct Curl_easy *data,
+                           struct Curl_dns_entry **pdns);
+
+struct Curl_dnscache {
+  struct Curl_hash entries;
+};
+
+/* init a new dns cache */
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size);
+
+void Curl_dnscache_destroy(struct Curl_dnscache *dns);
+
+/* prune old entries from the DNS cache */
+void Curl_dnscache_prune(struct Curl_easy *data);
+
+/* clear the DNS cache */
+void Curl_dnscache_clear(struct Curl_easy *data);
+
+/*
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after
+ * use, or we will leak memory!
+ * Returns CURLE_OK or CURLE_COULDNT_RESOLVE_HOST when a negative
+ * entry was in the cache.
+ */
+CURLcode Curl_dnscache_get(struct Curl_easy *data,
+                           uint8_t dns_queries,
+                           const char *hostname,
+                           uint16_t port,
+                           struct Curl_dns_entry **pentry);
+
+/*
+ * Curl_dnscache_addr() adds `entry` to the cache, increasing its
+ * reference count on success.
+ */
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+                           struct Curl_dns_entry *entry);
+
+/* Store a "negative" entry for host:port, e.g. remember that
+ * it could not be resolved. */
+CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
+                                    uint8_t dns_queries,
+                                    const char *host,
+                                    uint16_t port);
+
+/*
+ * Populate the cache with specified entries from CURLOPT_RESOLVE.
+ */
+CURLcode Curl_loadhostpairs(struct Curl_easy *data);
+
+#endif /* HEADER_CURL_DNSCACHE_H */
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index 4488d42..3044135 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -40,6 +40,9 @@
 
 #define DNS_CLASS_IN 0x01
 
+static void doh_close(struct Curl_easy *data,
+                      struct Curl_resolv_async *async);
+
 #ifdef CURLVERBOSE
 static const char * const errors[] = {
   "",
@@ -71,6 +74,11 @@
  */
 UNITTEST DOHcode doh_req_encode(const char *host,
                                 DNStype dnstype,
+                                unsigned char *dnsp,  /* buffer */
+                                size_t len,  /* buffer size */
+                                size_t *olen);  /* output length */
+UNITTEST DOHcode doh_req_encode(const char *host,
+                                DNStype dnstype,
                                 unsigned char *dnsp, /* buffer */
                                 size_t len,   /* buffer size */
                                 size_t *olen) /* output length */
@@ -205,51 +213,62 @@
 #endif
 
 /* called from multi when a sub transfer, e.g. doh probe, is done.
- * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE
+ * This looks up the probe response at its meta CURL_EZM_DOH_PROBE
  * and copies the response body over to the struct at the master's
  * meta at CURL_EZM_DOH_MASTER. */
 static void doh_probe_done(struct Curl_easy *data,
                            struct Curl_easy *doh, CURLcode result)
 {
-  struct doh_probes *dohp = data->state.async.doh;
-  DEBUGASSERT(dohp);
-  if(dohp) {
-    struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
-    int i;
+  struct Curl_resolv_async *async = NULL;
+  struct doh_probes *dohp = NULL;
+  struct doh_request *doh_req = NULL;
+  int i;
 
-    for(i = 0; i < DOH_SLOT_COUNT; ++i) {
-      if(dohp->probe_resp[i].probe_mid == doh->mid)
-        break;
-    }
-    if(i >= DOH_SLOT_COUNT) {
-      failf(data, "unknown sub request done");
-      return;
-    }
+  doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
+  if(!doh_req) {
+    DEBUGASSERT(0);
+    return;
+  }
 
-    dohp->pending--;
-    infof(doh, "a DoH request is completed, %u to go", dohp->pending);
-    dohp->probe_resp[i].result = result;
-    /* We expect either the meta data still to exist or the sub request
-     * to have already failed. */
-    DEBUGASSERT(doh_req || result);
-    if(doh_req) {
-      if(!result) {
-        dohp->probe_resp[i].dnstype = doh_req->dnstype;
-        result = curlx_dyn_addn(&dohp->probe_resp[i].body,
-                                curlx_dyn_ptr(&doh_req->resp_body),
-                                curlx_dyn_len(&doh_req->resp_body));
-        curlx_dyn_free(&doh_req->resp_body);
-      }
-      Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
-    }
+  async = Curl_async_get(data, doh_req->resolv_id);
+  if(!async) {
+    CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response",
+                 doh_req->resolv_id);
+    return;
+  }
+  dohp = async->doh;
 
-    if(result)
-      infof(doh, "DoH request %s", curl_easy_strerror(result));
+  for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+    if(dohp->probe_resp[i].probe_mid == doh->mid)
+      break;
+  }
+  /* We really should have found the slot where to store the response */
+  if(i >= DOH_SLOT_COUNT) {
+    DEBUGASSERT(0);
+    failf(data, "DoH: unknown sub request done");
+    return;
+  }
 
-    if(!dohp->pending) {
-      /* DoH completed, run the transfer picking up the results */
-      Curl_multi_mark_dirty(data);
-    }
+  dohp->pending--;
+  infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+  dohp->probe_resp[i].result = result;
+  /* We expect either the meta data still to exist or the sub request
+   * to have already failed. */
+  if(!result) {
+    dohp->probe_resp[i].dnstype = doh_req->dnstype;
+    result = curlx_dyn_addn(&dohp->probe_resp[i].body,
+                            curlx_dyn_ptr(&doh_req->resp_body),
+                            curlx_dyn_len(&doh_req->resp_body));
+    curlx_dyn_free(&doh_req->resp_body);
+  }
+  Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
+
+  if(result)
+    infof(doh, "DoH request %s", curl_easy_strerror(result));
+
+  if(!dohp->pending) {
+    /* DoH completed, run the transfer picking up the results */
+    Curl_multi_mark_dirty(data);
   }
 }
 
@@ -278,6 +297,7 @@
                               DNStype dnstype,
                               const char *host,
                               const char *url, CURLM *multi,
+                              uint32_t resolv_id,
                               uint32_t *pmid)
 {
   struct Curl_easy *doh = NULL;
@@ -291,6 +311,7 @@
   doh_req = curlx_calloc(1, sizeof(*doh_req));
   if(!doh_req)
     return CURLE_OUT_OF_MEMORY;
+  doh_req->resolv_id = resolv_id;
   doh_req->dnstype = dnstype;
   curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
 
@@ -431,29 +452,22 @@
  * a 'Curl_addrinfo *' with the address information.
  */
 
-CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
-                  int port, int ip_version)
+CURLcode Curl_doh(struct Curl_easy *data,
+                  struct Curl_resolv_async *async)
 {
   CURLcode result = CURLE_OK;
   struct doh_probes *dohp = NULL;
-  struct connectdata *conn = data->conn;
   size_t i;
 
-  DEBUGASSERT(conn);
-  DEBUGASSERT(!data->state.async.doh);
-  DEBUGASSERT(hostname && hostname[0]);
-  if(data->state.async.doh)
-    Curl_doh_cleanup(data);
-
-  data->state.async.done = FALSE;
-  data->state.async.port = port;
-  data->state.async.ip_version = ip_version;
-  data->state.async.hostname = curlx_strdup(hostname);
-  if(!data->state.async.hostname)
-    return CURLE_OUT_OF_MEMORY;
+  DEBUGASSERT(!async->doh);
+  DEBUGASSERT(async->hostname[0]);
+  if(async->doh) {
+    DEBUGASSERT(0); /* should not happen */
+    Curl_doh_cleanup(data, async);
+  }
 
   /* start clean, consider allocating this struct on demand */
-  data->state.async.doh = dohp = curlx_calloc(1, sizeof(struct doh_probes));
+  async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes));
   if(!dohp)
     return CURLE_OUT_OF_MEMORY;
 
@@ -462,28 +476,29 @@
     curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE);
   }
 
-  conn->bits.doh = TRUE;
-  dohp->host = data->state.async.hostname;
-  dohp->port = data->state.async.port;
+  dohp->host = async->hostname;
+  dohp->port = async->port;
   /* We are making sub easy handles and want to be called back when
    * one is done. */
   data->sub_xfer_done = doh_probe_done;
 
   /* create IPv4 DoH request */
-  result = doh_probe_run(data, CURL_DNS_TYPE_A,
-                         hostname, data->set.str[STRING_DOH],
-                         data->multi,
-                         &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
-  if(result)
-    goto error;
-  dohp->pending++;
+  if(async->dns_queries & CURL_DNSQ_A) {
+    result = doh_probe_run(data, CURL_DNS_TYPE_A,
+                           async->hostname, data->set.str[STRING_DOH],
+                           data->multi, async->id,
+                           &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
+    if(result)
+      goto error;
+    dohp->pending++;
+  }
 
 #ifdef USE_IPV6
-  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+  if(async->dns_queries & CURL_DNSQ_AAAA) {
     /* create IPv6 DoH request */
     result = doh_probe_run(data, CURL_DNS_TYPE_AAAA,
-                           hostname, data->set.str[STRING_DOH],
-                           data->multi,
+                           async->hostname, data->set.str[STRING_DOH],
+                           data->multi, async->id,
                            &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
     if(result)
       goto error;
@@ -492,17 +507,17 @@
 #endif
 
 #ifdef USE_HTTPSRR
-  if(conn->scheme->protocol & PROTO_FAMILY_HTTP) {
-    /* Only use HTTPS RR for HTTP(S) transfers */
+  if(async->dns_queries & CURL_DNSQ_HTTPS) {
     char *qname = NULL;
-    if(port != PORT_HTTPS) {
-      qname = curl_maprintf("_%d._https.%s", port, hostname);
+    if(async->port != PORT_HTTPS) {
+      qname = curl_maprintf("_%d._https.%s", async->port, async->hostname);
       if(!qname)
         goto error;
     }
     result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS,
-                           qname ? qname : hostname, data->set.str[STRING_DOH],
-                           data->multi,
+                           qname ? qname : async->hostname,
+                           data->set.str[STRING_DOH], data->multi,
+                           async->id,
                            &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
     curlx_free(qname);
     if(result)
@@ -513,7 +528,7 @@
   return CURLE_OK;
 
 error:
-  Curl_doh_cleanup(data);
+  Curl_doh_cleanup(data, async);
   return result;
 }
 
@@ -698,6 +713,8 @@
   return DOH_OK;
 }
 
+/* @unittest 1655 */
+UNITTEST void de_init(struct dohentry *de);
 UNITTEST void de_init(struct dohentry *de)
 {
   int i;
@@ -707,6 +724,11 @@
     curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME);
 }
 
+/* @unittest 1655 */
+UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
+                                 size_t dohlen,
+                                 DNStype dnstype,
+                                 struct dohentry *d);
 UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
                                  size_t dohlen,
                                  DNStype dnstype,
@@ -838,7 +860,7 @@
   if(index != dohlen)
     return DOH_DNS_MALFORMAT; /* something is wrong */
 
-#ifdef USE_HTTTPS
+#ifdef USE_HTTPSRR
   if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr &&
      !d->numhttps_rrs)
 #else
@@ -1024,6 +1046,8 @@
 }
 #endif
 
+/* @unittest 1655 */
+UNITTEST void de_cleanup(struct dohentry *d);
 UNITTEST void de_cleanup(struct dohentry *d)
 {
   int i = 0;
@@ -1032,7 +1056,7 @@
   }
 #ifdef USE_HTTPSRR
   for(i = 0; i < d->numhttps_rrs; i++)
-    Curl_safefree(d->https_rrs[i].val);
+    curlx_safefree(d->https_rrs[i].val);
 #endif
 }
 
@@ -1049,7 +1073,7 @@
  * https://datatracker.ietf.org/doc/html/rfc1035#section-3.1
  *
  * The input buffer pointer will be modified so it points to after the end of
- * the DNS name encoding on output. (And that is why it is an "unsigned char
+ * the DNS name encoding on output. (that is why it is an "unsigned char
  * **" :-)
  */
 static CURLcode doh_decode_rdata_name(const unsigned char **buf,
@@ -1095,11 +1119,10 @@
   return CURLE_OK;
 }
 
+/* @unittest 1658 */
 UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
                                           const unsigned char *cp, size_t len,
                                           struct Curl_https_rrinfo **hrr);
-
-/* @unittest 1658 */
 UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
                                           const unsigned char *cp, size_t len,
                                           struct Curl_https_rrinfo **hrr)
@@ -1111,6 +1134,7 @@
   CURLcode result = CURLE_OUT_OF_MEMORY;
   size_t olen;
 
+  (void)data;
   *hrr = NULL;
   if(len <= 2)
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1128,7 +1152,6 @@
     result = CURLE_WEIRD_SERVER_REPLY;
     goto err;
   }
-  lhrr->port = -1; /* until set */
   while(len >= 4) {
     pcode = doh_get16bit(cp, 0);
     plen = doh_get16bit(cp, 2);
@@ -1138,9 +1161,10 @@
       result = CURLE_WEIRD_SERVER_REPLY;
       goto err;
     }
-    result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen);
+    result = Curl_httpsrr_set(lhrr, pcode, cp, plen);
     if(result)
       goto err;
+    Curl_httpsrr_trace(data, lhrr);
     cp += plen;
     len -= plen;
     expected_min_pcode = pcode + 1;
@@ -1150,16 +1174,13 @@
   return CURLE_OK;
 err:
   Curl_httpsrr_cleanup(lhrr);
-  Curl_safefree(lhrr);
+  curlx_safefree(lhrr);
   return result;
 }
 
 #if defined(DEBUGBUILD) && defined(CURLVERBOSE)
-UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
-                                struct Curl_https_rrinfo *hrr);
-
-UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
-                                struct Curl_https_rrinfo *hrr)
+static void doh_print_httpsrr(struct Curl_easy *data,
+                              struct Curl_https_rrinfo *hrr)
 {
   DEBUGASSERT(hrr);
   infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target);
@@ -1194,32 +1215,31 @@
 # endif
 #endif
 
-CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
-                              struct Curl_dns_entry **dnsp)
+CURLcode Curl_doh_take_result(struct Curl_easy *data,
+                              struct Curl_resolv_async *async,
+                              struct Curl_dns_entry **pdns)
 {
+  struct doh_probes *dohp = async->doh;
   CURLcode result = CURLE_OK;
-  struct doh_probes *dohp = data->state.async.doh;
   struct dohentry de;
-  *dnsp = NULL; /* defaults to no response */
+
+  *pdns = NULL; /* defaults to no response */
   if(!dohp)
     return CURLE_OUT_OF_MEMORY;
 
   if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX &&
      dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) {
     failf(data, "Could not DoH-resolve: %s", dohp->host);
-    return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
-      CURLE_COULDNT_RESOLVE_HOST;
+    return async->for_proxy ?
+      CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST;
   }
   else if(!dohp->pending) {
     DOHcode rc[DOH_SLOT_COUNT];
     int slot;
 
-    /* Clear any result the might still be there */
-    Curl_resolv_unlink(data, &data->state.async.dns);
-
     memset(rc, 0, sizeof(rc));
     /* remove DoH handles from multi handle and close them */
-    Curl_doh_close(data);
+    doh_close(data, async);
     /* parse the responses, create the struct and return it! */
     de_init(&de);
     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
@@ -1235,7 +1255,6 @@
       }
     } /* next slot */
 
-    result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
     if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) {
       /* we have an address, of one kind or other */
       struct Curl_dns_entry *dns;
@@ -1251,50 +1270,56 @@
         goto error;
 
       /* we got a response, create a dns entry. */
-      dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, 0,
-                                   dohp->port, FALSE);
-      if(dns) {
-        /* Now add and HTTPSRR information if we have */
-#ifdef USE_HTTPSRR
-        if(de.numhttps_rrs > 0 && result == CURLE_OK) {
-          struct Curl_https_rrinfo *hrr = NULL;
-          result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
-                                           de.https_rrs->len, &hrr);
-          if(result) {
-            infof(data, "Failed to decode HTTPS RR");
-            Curl_resolv_unlink(data, &dns);
-            goto error;
-          }
-          infof(data, "Some HTTPS RR to process");
-#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
-          doh_print_httpsrr(data, hrr);
-#endif
-          dns->hinfo = hrr;
-        }
-#endif /* USE_HTTPSRR */
-        /* and add the entry to the cache */
-        data->state.async.dns = dns;
-        result = Curl_dnscache_add(data, dns);
-        *dnsp = data->state.async.dns;
+      dns = Curl_dnscache_mk_entry(data, async->dns_queries,
+                                   &ai, dohp->host, dohp->port);
+      if(!dns) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto error;
       }
-    } /* address processing done */
 
-    /* All done */
-    data->state.async.done = TRUE;
+      /* Now add and HTTPSRR information if we have */
+#ifdef USE_HTTPSRR
+      if(de.numhttps_rrs > 0 && result == CURLE_OK) {
+        struct Curl_https_rrinfo *hrr = NULL;
+        result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
+                                         de.https_rrs->len, &hrr);
+        if(result) {
+          infof(data, "Failed to decode HTTPS RR");
+          Curl_dns_entry_unlink(data, &dns);
+          goto error;
+        }
+        infof(data, "Some HTTPS RR to process");
+#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
+        doh_print_httpsrr(data, hrr);
+#endif
+        Curl_dns_entry_set_https_rr(dns, hrr);
+      }
+#endif /* USE_HTTPSRR */
+
+      /* and add the entry to the cache */
+      result = Curl_dnscache_add(data, dns);
+      *pdns = dns;
+    } /* address processing done */
+    else {
+      result = async->for_proxy ?
+        CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST;
+    }
+
   } /* !dohp->pending */
   else
     /* wait for pending DoH transactions to complete */
-    return CURLE_OK;
+    return CURLE_AGAIN;
 
 error:
   de_cleanup(&de);
-  Curl_doh_cleanup(data);
+  Curl_doh_cleanup(data, async);
   return result;
 }
 
-void Curl_doh_close(struct Curl_easy *data)
+static void doh_close(struct Curl_easy *data,
+                      struct Curl_resolv_async *async)
 {
-  struct doh_probes *doh = data->state.async.doh;
+  struct doh_probes *doh = async ? async->doh : NULL;
   if(doh && data->multi) {
     struct Curl_easy *probe_data;
     uint32_t mid;
@@ -1320,16 +1345,17 @@
   }
 }
 
-void Curl_doh_cleanup(struct Curl_easy *data)
+void Curl_doh_cleanup(struct Curl_easy *data,
+                      struct Curl_resolv_async *async)
 {
-  struct doh_probes *dohp = data->state.async.doh;
+  struct doh_probes *dohp = async->doh;
   if(dohp) {
     int i;
-    Curl_doh_close(data);
+    doh_close(data, async);
     for(i = 0; i < DOH_SLOT_COUNT; ++i) {
       curlx_dyn_free(&dohp->probe_resp[i].body);
     }
-    Curl_safefree(data->state.async.doh);
+    curlx_safefree(async->doh);
   }
 }
 
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h
index 3eba4df..428b230 100644
--- a/Utilities/cmcurl/lib/doh.h
+++ b/Utilities/cmcurl/lib/doh.h
@@ -25,7 +25,10 @@
  ***************************************************************************/
 #include "urldata.h"
 
-#ifndef CURL_DISABLE_DOH
+/* enums outside of the #ifdef to make the types work in unitprotos.h even on
+   builds without DoH support */
+
+struct Curl_resolv_async;
 
 typedef enum {
   DOH_OK,
@@ -53,6 +56,10 @@
   CURL_DNS_TYPE_HTTPS = 65
 } DNStype;
 
+struct dohentry; /* forward-declare for non-DoH builds */
+
+#ifndef CURL_DISABLE_DOH
+
 enum doh_slot_num {
   /* Explicit values for first two symbols so as to match hard-coded
    * constants in existing code
@@ -86,6 +93,7 @@
   struct curl_slist *req_hds;
   struct dynbuf resp_body;
   size_t req_body_len;
+  uint32_t resolv_id; /* id of the resolve operation */
   DNStype dnstype;
 };
 
@@ -101,7 +109,7 @@
 struct doh_probes {
   struct doh_response probe_resp[DOH_SLOT_COUNT];
   unsigned int pending; /* still outstanding probes */
-  int port;
+  uint16_t port;
   const char *host;
 };
 
@@ -109,12 +117,12 @@
  * Curl_doh() starts a name resolve using DoH (DNS-over-HTTPS). It resolves a
  * name and returns a 'Curl_addrinfo *' with the address information.
  */
+CURLcode Curl_doh(struct Curl_easy *data,
+                  struct Curl_resolv_async *async);
 
-CURLcode Curl_doh(struct Curl_easy *data, const char *hostname,
-                  int port, int ip_version);
-
-CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
-                              struct Curl_dns_entry **dnsp);
+CURLcode Curl_doh_take_result(struct Curl_easy *data,
+                              struct Curl_resolv_async *async,
+                              struct Curl_dns_entry **pdns);
 
 #define DOH_MAX_ADDR  24
 #define DOH_MAX_CNAME 4
@@ -155,27 +163,15 @@
 #endif
 };
 
-void Curl_doh_close(struct Curl_easy *data);
-void Curl_doh_cleanup(struct Curl_easy *data);
+void Curl_doh_cleanup(struct Curl_easy *data,
+                      struct Curl_resolv_async *async);
+#define Curl_doh_wanted(d)  (!!(d)->set.doh)
 
-#ifdef UNITTESTS
-UNITTEST DOHcode doh_req_encode(const char *host,
-                                DNStype dnstype,
-                                unsigned char *dnsp,  /* buffer */
-                                size_t len,  /* buffer size */
-                                size_t *olen);  /* output length */
-UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
-                                 size_t dohlen,
-                                 DNStype dnstype,
-                                 struct dohentry *d);
-
-UNITTEST void de_init(struct dohentry *d);
-UNITTEST void de_cleanup(struct dohentry *d);
-#endif
 
 #else /* CURL_DISABLE_DOH */
-#define Curl_doh(a, b, c, d, e)    NULL
-#define Curl_doh_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_doh(a, b)             NULL
+#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_doh_wanted(d)         FALSE
 #endif /* !CURL_DISABLE_DOH */
 
 #endif /* HEADER_CURL_DOH_H */
diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c
index 7424a57..097306c 100644
--- a/Utilities/cmcurl/lib/dynhds.c
+++ b/Utilities/cmcurl/lib/dynhds.c
@@ -76,7 +76,7 @@
       entry_free(dynhds->hds[i]);
     }
   }
-  Curl_safefree(dynhds->hds);
+  curlx_safefree(dynhds->hds);
   dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0;
 }
 
@@ -158,7 +158,7 @@
     if(dynhds->hds) {
       memcpy(nhds, dynhds->hds,
              dynhds->hds_len * sizeof(struct dynhds_entry *));
-      Curl_safefree(dynhds->hds);
+      curlx_safefree(dynhds->hds);
     }
     dynhds->hds = nhds;
     dynhds->hds_allc = nallc;
@@ -220,21 +220,36 @@
 }
 
 #ifdef UNITTESTS
-/* used by unit2602.c */
+/* @unittest 2602 */
 
-bool Curl_dynhds_contains(struct dynhds *dynhds,
-                          const char *name, size_t namelen)
+/**
+ * Return TRUE iff one or more headers with the given name exist.
+ */
+UNITTEST bool dynhds_contains(struct dynhds *dynhds,
+                              const char *name, size_t namelen);
+UNITTEST bool dynhds_contains(struct dynhds *dynhds,
+                              const char *name, size_t namelen)
 {
   return !!Curl_dynhds_get(dynhds, name, namelen);
 }
 
-bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name)
+/* @unittest 2602 */
+UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name);
+UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name)
 {
-  return Curl_dynhds_contains(dynhds, name, strlen(name));
+  return dynhds_contains(dynhds, name, strlen(name));
 }
 
-size_t Curl_dynhds_count_name(struct dynhds *dynhds,
-                              const char *name, size_t namelen)
+/**
+ * Return how often the given name appears in `dynhds`.
+ * Names are case-insensitive.
+ *
+ * @unittest 2602
+ */
+UNITTEST size_t dynhds_count_name(struct dynhds *dynhds,
+                                  const char *name, size_t namelen);
+UNITTEST size_t dynhds_count_name(struct dynhds *dynhds,
+                                  const char *name, size_t namelen)
 {
   size_t n = 0;
   if(dynhds->hds_len) {
@@ -248,21 +263,30 @@
   return n;
 }
 
-size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name)
+/**
+ * Return how often the given null-terminated name appears in `dynhds`.
+ * Names are case-insensitive.
+ *
+ * @unittest 2602
+ */
+UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds,
+                                        const char *name);
+UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds,
+                                        const char *name)
 {
-  return Curl_dynhds_count_name(dynhds, name, strlen(name));
+  return dynhds_count_name(dynhds, name, strlen(name));
 }
 
-CURLcode Curl_dynhds_set(struct dynhds *dynhds,
-                         const char *name, size_t namelen,
-                         const char *value, size_t valuelen)
-{
-  Curl_dynhds_remove(dynhds, name, namelen);
-  return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
-}
-
-size_t Curl_dynhds_remove(struct dynhds *dynhds,
-                          const char *name, size_t namelen)
+/**
+ * Remove all entries with the given name.
+ * Returns number of entries removed.
+ *
+ * @unittest 2602
+ */
+UNITTEST size_t dynhds_remove(struct dynhds *dynhds,
+                              const char *name, size_t namelen);
+UNITTEST size_t dynhds_remove(struct dynhds *dynhds,
+                              const char *name, size_t namelen)
 {
   size_t n = 0;
   if(dynhds->hds_len) {
@@ -287,12 +311,32 @@
   return n;
 }
 
-size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name)
+/**
+ * Set the give header name and value, replacing any entries with
+ * the same name. The header is added at the end of all (remaining)
+ * entries.
+ *
+ * @unittest 2602
+ */
+UNITTEST CURLcode dynhds_set(struct dynhds *dynhds,
+                             const char *name, size_t namelen,
+                             const char *value, size_t valuelen);
+UNITTEST CURLcode dynhds_set(struct dynhds *dynhds,
+                             const char *name, size_t namelen,
+                             const char *value, size_t valuelen)
 {
-  return Curl_dynhds_remove(dynhds, name, strlen(name));
+  dynhds_remove(dynhds, name, namelen);
+  return Curl_dynhds_add(dynhds, name, namelen, value, valuelen);
 }
 
-#endif
+/* @unittest 2602 */
+UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name);
+UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name)
+{
+  return dynhds_remove(dynhds, name, strlen(name));
+}
+
+#endif /* UNITTESTS */
 
 CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
 {
diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h
index e30eb45..d5337eb 100644
--- a/Utilities/cmcurl/lib/dynhds.h
+++ b/Utilities/cmcurl/lib/dynhds.h
@@ -94,47 +94,6 @@
                                      const char *name, size_t namelen);
 struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name);
 
-#ifdef UNITTESTS
-/* used by unit2602.c */
-
-/**
- * Return TRUE iff one or more headers with the given name exist.
- */
-bool Curl_dynhds_contains(struct dynhds *dynhds,
-                          const char *name, size_t namelen);
-bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name);
-
-/**
- * Return how often the given name appears in `dynhds`.
- * Names are case-insensitive.
- */
-size_t Curl_dynhds_count_name(struct dynhds *dynhds,
-                              const char *name, size_t namelen);
-
-/**
- * Return how often the given null-terminated name appears in `dynhds`.
- * Names are case-insensitive.
- */
-size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name);
-
-/**
- * Remove all entries with the given name.
- * Returns number of entries removed.
- */
-size_t Curl_dynhds_remove(struct dynhds *dynhds,
-                          const char *name, size_t namelen);
-size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name);
-
-/**
- * Set the give header name and value, replacing any entries with
- * the same name. The header is added at the end of all (remaining)
- * entries.
- */
-CURLcode Curl_dynhds_set(struct dynhds *dynhds,
-                         const char *name, size_t namelen,
-                         const char *value, size_t valuelen);
-#endif
-
 CURLcode Curl_dynhds_cset(struct dynhds *dynhds,
                           const char *name, const char *value);
 
@@ -167,7 +126,7 @@
 
 /**
  * Add the headers to the given `dynbuf` in HTTP/1.1 format with
- * cr+lf line endings. Will NOT output a last empty line.
+ * CR+LF line endings. Does NOT output a last empty line.
  */
 CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf);
 
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index d05674e..a472d6d 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -779,8 +779,9 @@
   if(multi->in_callback)
     return CURLE_RECURSIVE_API_CALL;
 
-  /* Copy the MAXCONNECTS option to the multi handle */
+  /* Copy relevant easy options to the multi handle */
   curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
+  curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit);
 
   data->multi_easy = NULL; /* pretend it does not exist */
   mresult = curl_multi_add_handle(multi, data);
@@ -814,9 +815,9 @@
  * curl_easy_perform() is the external interface that performs a blocking
  * transfer as previously setup.
  */
-CURLcode curl_easy_perform(CURL *data)
+CURLcode curl_easy_perform(CURL *curl)
 {
-  return easy_perform(data, FALSE);
+  return easy_perform(curl, FALSE);
 }
 
 #ifdef DEBUGBUILD
@@ -824,9 +825,9 @@
  * curl_easy_perform_ev() is the external interface that performs a blocking
  * transfer using the event-based API internally.
  */
-CURLcode curl_easy_perform_ev(struct Curl_easy *data)
+CURLcode curl_easy_perform_ev(struct Curl_easy *easy)
 {
-  return easy_perform(data, TRUE);
+  return easy_perform(easy, TRUE);
 }
 #endif
 
@@ -834,9 +835,9 @@
  * curl_easy_cleanup() is the external interface to cleaning/freeing the given
  * easy handle.
  */
-void curl_easy_cleanup(CURL *ptr)
+void curl_easy_cleanup(CURL *curl)
 {
-  struct Curl_easy *data = ptr;
+  struct Curl_easy *data = curl;
   if(GOOD_EASY_HANDLE(data)) {
     struct Curl_sigpipe_ctx sigpipe_ctx;
     sigpipe_ignore(data, &sigpipe_ctx);
@@ -850,9 +851,9 @@
  * information from a performed transfer and similar.
  */
 #undef curl_easy_getinfo
-CURLcode curl_easy_getinfo(CURL *easy, CURLINFO info, ...)
+CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
 {
-  struct Curl_easy *data = easy;
+  struct Curl_easy *data = curl;
   va_list arg;
   void *paramp;
   CURLcode result;
@@ -938,7 +939,7 @@
 static void dupeasy_meta_freeentry(void *p)
 {
   (void)p;
-  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+  /* Always FALSE. Cannot use a 0 assert here since compilers
    * are not in agreement if they then want a NORETURN attribute or
    * not. *sigh* */
   DEBUGASSERT(p == NULL);
@@ -949,9 +950,9 @@
  * given input easy handle. The returned handle will be a new working handle
  * with all options set exactly as the input source handle.
  */
-CURL *curl_easy_duphandle(CURL *d)
+CURL *curl_easy_duphandle(CURL *curl)
 {
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   struct Curl_easy *outcurl = NULL;
 
   if(!GOOD_EASY_HANDLE(data))
@@ -1080,9 +1081,9 @@
  * curl_easy_reset() is an external interface that allows an app to re-
  * initialize a session handle to the default values.
  */
-void curl_easy_reset(CURL *d)
+void curl_easy_reset(CURL *curl)
 {
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   if(!GOOD_EASY_HANDLE(data))
     return;
 
@@ -1091,10 +1092,6 @@
 
   /* clear all meta data */
   Curl_meta_reset(data);
-  /* clear any resolve data */
-  Curl_async_shutdown(data);
-  Curl_resolv_unlink(data, &data->state.dns[0]);
-  Curl_resolv_unlink(data, &data->state.dns[1]);
   /* zero out UserDefined data: */
   Curl_freeset(data);
   memset(&data->set, 0, sizeof(struct UserDefined));
@@ -1133,12 +1130,12 @@
  * NOTE: This is one of few API functions that are allowed to be called from
  * within a callback.
  */
-CURLcode curl_easy_pause(CURL *d, int action)
+CURLcode curl_easy_pause(CURL *curl, int action)
 {
   CURLcode result = CURLE_OK;
   bool recursive = FALSE;
   bool changed = FALSE;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   bool recv_paused, recv_paused_new;
   bool send_paused, send_paused_new;
 
@@ -1157,12 +1154,14 @@
   if((send_paused != send_paused_new) ||
      (send_paused_new != Curl_creader_is_paused(data))) {
     changed = TRUE;
-    result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new));
+    result = Curl_1st_fatal(
+      result, Curl_xfer_pause_send(data, send_paused_new));
   }
 
   if(recv_paused != recv_paused_new) {
     changed = TRUE;
-    result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new));
+    result = Curl_1st_fatal(
+      result, Curl_xfer_pause_recv(data, recv_paused_new));
   }
 
   /* If not completely pausing both directions now, run again in any case. */
@@ -1221,11 +1220,11 @@
  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
  * Returns CURLE_OK on success, error code on error.
  */
-CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
+CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
 {
   CURLcode result;
   struct connectdata *c;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
 
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1299,11 +1298,12 @@
  * Sends data over the connected socket. Use after successful
  * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
  */
-CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n)
+CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
+                        size_t *n)
 {
   size_t written = 0;
   CURLcode result;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
   if(Curl_is_in_callback(data))
@@ -1317,9 +1317,9 @@
 /*
  * Performs connection upkeep for the given session handle.
  */
-CURLcode curl_easy_upkeep(CURL *d)
+CURLcode curl_easy_upkeep(CURL *curl)
 {
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   /* Verify that we got an easy handle we can work with. */
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1331,18 +1331,20 @@
   return Curl_cpool_upkeep(data);
 }
 
-CURLcode curl_easy_ssls_import(CURL *d, const char *session_key,
+CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key,
                                const unsigned char *shmac, size_t shmac_len,
                                const unsigned char *sdata, size_t sdata_len)
 {
 #if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
+  if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
+    return CURLE_RECURSIVE_API_CALL;
   return Curl_ssl_session_import(data, session_key,
                                  shmac, shmac_len, sdata, sdata_len);
 #else
-  (void)d;
+  (void)curl;
   (void)session_key;
   (void)shmac;
   (void)shmac_len;
@@ -1352,17 +1354,19 @@
 #endif
 }
 
-CURLcode curl_easy_ssls_export(CURL *d,
+CURLcode curl_easy_ssls_export(CURL *curl,
                                curl_ssls_export_cb *export_fn,
                                void *userptr)
 {
 #if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
+  if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
+    return CURLE_RECURSIVE_API_CALL;
   return Curl_ssl_session_export(data, export_fn, userptr);
 #else
-  (void)d;
+  (void)curl;
   (void)export_fn;
   (void)userptr;
   return CURLE_NOT_BUILT_IN;
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
index cefb01d..b8f916f 100644
--- a/Utilities/cmcurl/lib/easy_lock.h
+++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -49,8 +49,8 @@
    __has_builtin() function, so override it. */
 
 /* if GCC on i386/x86_64 or if the built-in is present */
-#if (defined(__GNUC__) && !defined(__clang__)) &&     \
-    (defined(__i386__) || defined(__x86_64__))
+#if (defined(__GNUC__) && !defined(__clang__)) && \
+  (defined(__i386__) || defined(__x86_64__))
 #define HAVE_BUILTIN_IA32_PAUSE
 #elif defined(__has_builtin)  /* Keep this PP check separate from others */
 #if __has_builtin(__builtin_ia32_pause)
@@ -84,9 +84,7 @@
   atomic_store_explicit(lock, false, memory_order_release);
 }
 
-#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-
-#include <pthread.h>
+#elif defined(HAVE_THREADS_POSIX)
 
 #define curl_simple_lock           pthread_mutex_t
 #define CURL_SIMPLE_LOCK_INIT      PTHREAD_MUTEX_INITIALIZER
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c
index 2685ada..533d7e3 100644
--- a/Utilities/cmcurl/lib/easygetopt.c
+++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -62,8 +62,8 @@
 }
 
 /* Iterates over available options */
-const struct curl_easyoption *
-curl_easy_option_next(const struct curl_easyoption *prev)
+const struct curl_easyoption *curl_easy_option_next(
+  const struct curl_easyoption *prev)
 {
   if(prev && prev->name) {
     prev++;
@@ -88,8 +88,8 @@
   return NULL;
 }
 
-const struct curl_easyoption *
-curl_easy_option_next(const struct curl_easyoption *prev)
+const struct curl_easyoption *curl_easy_option_next(
+  const struct curl_easyoption *prev)
 {
   (void)prev;
   return NULL;
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c
index be44e97..4aff583 100644
--- a/Utilities/cmcurl/lib/escape.c
+++ b/Utilities/cmcurl/lib/escape.c
@@ -33,9 +33,9 @@
 #include "curl_printf.h"
 
 /* for ABI-compatibility with previous versions */
-char *curl_escape(const char *string, int inlength)
+char *curl_escape(const char *string, int length)
 {
-  return curl_easy_escape(NULL, string, inlength);
+  return curl_easy_escape(NULL, string, length);
 }
 
 /* for ABI-compatibility with previous versions */
@@ -47,25 +47,25 @@
 /* Escapes for URL the given unescaped string of given length.
  * 'data' is ignored since 7.82.0.
  */
-char *curl_easy_escape(CURL *data, const char *string, int inlength)
+char *curl_easy_escape(CURL *curl, const char *string, int length)
 {
-  size_t length;
+  size_t len;
   struct dynbuf d;
-  (void)data;
+  (void)curl;
 
-  if(!string || (inlength < 0))
+  if(!string || (length < 0))
     return NULL;
 
-  length = (inlength ? (size_t)inlength : strlen(string));
-  if(!length)
+  len = (length ? (size_t)length : strlen(string));
+  if(!len)
     return curlx_strdup("");
 
-  if(length > SIZE_MAX / 16)
+  if(len > SIZE_MAX / 16)
     return NULL;
 
-  curlx_dyn_init(&d, (length * 3) + 1);
+  curlx_dyn_init(&d, (len * 3) + 1);
 
-  while(length--) {
+  while(len--) {
     /* treat the characters unsigned */
     unsigned char in = (unsigned char)*string++;
 
@@ -138,7 +138,7 @@
 
     if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
        ((ctrl == REJECT_ZERO) && (in == 0))) {
-      Curl_safefree(*ostring);
+      curlx_safefree(*ostring);
       return CURLE_URL_MALFORMAT;
     }
 
@@ -160,24 +160,25 @@
  * If olen == NULL, no output length is stored.
  * 'data' is ignored since 7.82.0.
  */
-char *curl_easy_unescape(CURL *data, const char *string, int length, int *olen)
+char *curl_easy_unescape(CURL *curl, const char *string, int inlength,
+                         int *outlength)
 {
   char *str = NULL;
-  (void)data;
-  if(string && (length >= 0)) {
-    size_t inputlen = (size_t)length;
+  (void)curl;
+  if(string && (inlength >= 0)) {
+    size_t inputlen = (size_t)inlength;
     size_t outputlen;
     CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen,
                                   REJECT_NADA);
     if(res)
       return NULL;
 
-    if(olen) {
+    if(outlength) {
       if(outputlen <= (size_t)INT_MAX)
-        *olen = curlx_uztosi(outputlen);
+        *outlength = curlx_uztosi(outputlen);
       else
         /* too large to return in an int, fail! */
-        Curl_safefree(str);
+        curlx_safefree(str);
     }
   }
   return str;
diff --git a/Utilities/cmcurl/lib/fake_addrinfo.c b/Utilities/cmcurl/lib/fake_addrinfo.c
index d8b1398..5a89202 100644
--- a/Utilities/cmcurl/lib/fake_addrinfo.c
+++ b/Utilities/cmcurl/lib/fake_addrinfo.c
@@ -41,16 +41,16 @@
 }
 
 struct context {
-  struct ares_addrinfo *result;
+  struct ares_addrinfo *addr;
 };
 
 static void async_addrinfo_cb(void *userp, int status, int timeouts,
-                              struct ares_addrinfo *result)
+                              struct ares_addrinfo *addr)
 {
   struct context *ctx = (struct context *)userp;
   (void)timeouts;
   if(ARES_SUCCESS == status) {
-    ctx->result = result;
+    ctx->addr = addr;
   }
 }
 
@@ -184,11 +184,11 @@
   /* Wait until no more requests are left to be processed */
   ares_queue_wait_empty(channel, -1);
 
-  if(ctx.result) {
+  if(ctx.addr) {
     /* convert the c-ares version */
-    *res = mk_getaddrinfo(ctx.result);
+    *res = mk_getaddrinfo(ctx.addr);
     /* free the old */
-    ares_freeaddrinfo(ctx.result);
+    ares_freeaddrinfo(ctx.addr);
   }
   else
     rc = EAI_NONAME; /* got nothing */
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index 1c3f663..fff8fee 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -84,7 +84,7 @@
 
 static void file_cleanup(struct FILEPROTO *file)
 {
-  Curl_safefree(file->freepath);
+  curlx_safefree(file->freepath);
   file->path = NULL;
   if(file->fd != -1) {
     curlx_close(file->fd);
@@ -108,6 +108,8 @@
   (void)conn;
   /* allocate the FILE specific struct */
   filep = curlx_calloc(1, sizeof(*filep));
+  if(filep)
+    filep->fd = -1;
   if(!filep ||
      Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
@@ -191,7 +193,7 @@
     if(actual_path[i] == '/')
       actual_path[i] = '\\';
     else if(!actual_path[i]) { /* binary zero */
-      Curl_safefree(real_path);
+      curlx_safefree(real_path);
       return CURLE_URL_MALFORMAT;
     }
 
@@ -200,7 +202,7 @@
 #else
   if(memchr(real_path, 0, real_path_len)) {
     /* binary zeroes indicate foul play */
-    Curl_safefree(real_path);
+    curlx_safefree(real_path);
     return CURLE_URL_MALFORMAT;
   }
 
@@ -598,7 +600,7 @@
   return result;
 }
 
-static const struct Curl_protocol Curl_protocol_file = {
+const struct Curl_protocol Curl_protocol_file = {
   file_setup_connection,                /* setup_connection */
   file_do,                              /* do_it */
   file_done,                            /* done */
@@ -613,25 +615,9 @@
   file_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
 #endif
-
-/*
- * FILE scheme handler.
- */
-const struct Curl_scheme Curl_scheme_file = {
-  "file",                               /* scheme */
-#ifdef CURL_DISABLE_FILE
-  ZERO_NULL,
-#else
-  &Curl_protocol_file,
-#endif
-  CURLPROTO_FILE,                       /* protocol */
-  CURLPROTO_FILE,                       /* family */
-  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */
-  0                                     /* defport */
-};
diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h
index a306544..0aa411b 100644
--- a/Utilities/cmcurl/lib/file.h
+++ b/Utilities/cmcurl/lib/file.h
@@ -23,6 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_file;
+#ifndef CURL_DISABLE_FILE
+extern const struct Curl_protocol Curl_protocol_file;
+#endif
 
 #endif /* HEADER_CURL_FILE_H */
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index 3770d28..3619c15 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -609,11 +609,11 @@
                           struct curl_httppost **last_post, ...)
 {
   va_list arg;
-  CURLFORMcode result;
+  CURLFORMcode form;
   va_start(arg, last_post);
-  result = FormAdd(httppost, last_post, arg);
+  form = FormAdd(httppost, last_post, arg);
   va_end(arg);
-  return result;
+  return form;
 }
 
 /*
@@ -691,16 +691,16 @@
 static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
 {
   char *zname;
-  CURLcode res;
+  CURLcode result;
 
   if(!name || !len)
     return curl_mime_name(part, name);
   zname = curlx_memdup0(name, len);
   if(!zname)
     return CURLE_OUT_OF_MEMORY;
-  res = curl_mime_name(part, zname);
+  result = curl_mime_name(part, zname);
   curlx_free(zname);
-  return res;
+  return result;
 }
 
 /*
diff --git a/Utilities/cmcurl/lib/ftp-int.h b/Utilities/cmcurl/lib/ftp-int.h
new file mode 100644
index 0000000..68d26f3
--- /dev/null
+++ b/Utilities/cmcurl/lib/ftp-int.h
@@ -0,0 +1,160 @@
+#ifndef HEADER_CURL_FTP_INT_H
+#define HEADER_CURL_FTP_INT_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 "pingpong.h"
+
+#ifndef CURL_DISABLE_FTP
+
+/****************************************************************************
+ * FTP unique setup
+ ***************************************************************************/
+enum {
+  FTP_STOP,    /* do nothing state, stops the state machine */
+  FTP_WAIT220, /* waiting for the initial 220 response immediately after
+                  a connect */
+  FTP_AUTH,
+  FTP_USER,
+  FTP_PASS,
+  FTP_ACCT,
+  FTP_PBSZ,
+  FTP_PROT,
+  FTP_CCC,
+  FTP_PWD,
+  FTP_SYST,
+  FTP_NAMEFMT,
+  FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
+  FTP_RETR_PREQUOTE,
+  FTP_STOR_PREQUOTE,
+  FTP_LIST_PREQUOTE,
+  FTP_POSTQUOTE,
+  FTP_CWD,  /* change directory */
+  FTP_MKD,  /* if the directory did not exist */
+  FTP_MDTM, /* to figure out the datestamp */
+  FTP_TYPE, /* to set type when doing a head-like request */
+  FTP_LIST_TYPE, /* set type when about to do a directory list */
+  FTP_RETR_LIST_TYPE,
+  FTP_RETR_TYPE, /* set type when about to RETR a file */
+  FTP_STOR_TYPE, /* set type when about to STOR a file */
+  FTP_SIZE, /* get the remote file's size for head-like request */
+  FTP_RETR_SIZE, /* get the remote file's size for RETR */
+  FTP_STOR_SIZE, /* get the size for STOR */
+  FTP_REST, /* when used to check if the server supports it in head-like */
+  FTP_RETR_REST, /* when asking for "resume" in for RETR */
+  FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
+  FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */
+  FTP_PASV, /* generic state for PASV and EPSV, check count1 */
+  FTP_LIST, /* generic state for LIST, NLST or a custom list command */
+  FTP_RETR,
+  FTP_STOR, /* generic state for STOR and APPE */
+  FTP_QUIT,
+  FTP_LAST  /* never used */
+};
+typedef unsigned char ftpstate; /* use the enum values */
+
+struct ftp_parselist_data; /* defined later in ftplistparser.c */
+
+struct ftp_wc {
+  struct ftp_parselist_data *parser;
+
+  struct {
+    curl_write_callback write_function;
+    FILE *file_descriptor;
+  } backup;
+};
+
+/* This FTP struct is used in the Curl_easy. All FTP data that is
+   connection-oriented must be in FTP_conn to properly deal with the fact that
+   perhaps the Curl_easy is changed between the times the connection is
+   used. */
+struct FTP {
+  char *path;    /* points to the urlpieces struct field */
+  char *pathalloc; /* if non-NULL a pointer to an allocated path */
+
+  /* transfer a file/body or not, done as a typedefed enum to make debuggers
+     display the full symbol and not the numerical value */
+  curl_pp_transfer transfer;
+  curl_off_t downloadsize;
+};
+
+/* one struct entry for each path component (of 'rawpath') */
+struct pathcomp {
+  int start; /* start column */
+  int len;   /* length in bytes */
+};
+
+/* ftp_conn is used for struct connection-oriented data in the connectdata
+   struct */
+struct ftp_conn {
+  struct pingpong pp;
+  char *account;
+  char *alternative_to_user;
+  char *entrypath; /* the PWD reply when we logged on */
+  const char *file; /* url-decoded filename (or path), points into rawpath */
+  char *rawpath; /* URL decoded, allocated, version of the path */
+  struct pathcomp *dirs; /* allocated array for path components */
+  char *prevpath;   /* url-decoded conn->path from the previous transfer */
+  char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
+                        and others (A/I or zero) */
+  char *server_os;     /* The target server operating system. */
+  curl_off_t known_filesize; /* file size is different from -1, if wildcard
+                                LIST parsing was done and wc_statemach set
+                                it */
+  int count1; /* general purpose counter for the state machine */
+  int count2; /* general purpose counter for the state machine */
+  int count3; /* general purpose counter for the state machine */
+  unsigned short dirdepth;  /* number of entries used in the 'dirs' array,
+                               < FTP_MAX_DIR_DEPTH */
+  unsigned short cwdcount;  /* number of CWD commands issued,
+                               < FTP_MAX_DIR_DEPTH */
+  unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change
+                          state! */
+  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 ccc;       /* ccc level for this connection */
+  BIT(ftp_trying_alternative);
+  BIT(dont_check);  /* Set to TRUE to prevent the final (post-transfer)
+                       file size and 226/250 status check. It should still
+                       read the line, ignore the result. */
+  BIT(ctl_valid);   /* Tells Curl_ftp_quit() whether or not to do anything. If
+                       the connection has timed out or been closed, this
+                       should be FALSE when it gets to Curl_ftp_quit() */
+  BIT(cwddone);     /* if it has been determined that the proper CWD combo
+                       already has been done */
+  BIT(cwdfail);     /* set TRUE if a CWD command fails, as then we must prevent
+                       caching the current directory */
+  BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
+  BIT(shutdown);    /* connection is being shutdown, e.g. QUIT */
+};
+
+/* meta key for storing `struct FTP` as easy meta data */
+#define CURL_META_FTP_EASY   "meta:proto:ftp:easy"
+/* meta key for storing `struct ftp_conn` as connection meta data */
+#define CURL_META_FTP_CONN   "meta:proto:ftp:conn"
+
+#endif /* CURL_DISABLE_FTP */
+
+#endif /* HEADER_CURL_FTP_INT_H */
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 5b1151e..61a78c2 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -49,6 +49,7 @@
 #include "transfer.h"
 #include "escape.h"
 #include "ftp.h"
+#include "ftp-int.h"
 #include "ftplistparser.h"
 #include "curl_range.h"
 #include "strcase.h"
@@ -67,6 +68,7 @@
 #include "curlx/strdup.h"
 #include "curlx/strerr.h"
 #include "curlx/strparse.h"
+#include "curl_ctype.h"
 
 #ifndef NI_MAXHOST
 #define NI_MAXHOST 1025
@@ -173,9 +175,9 @@
 
 static void freedirs(struct ftp_conn *ftpc)
 {
-  Curl_safefree(ftpc->dirs);
+  curlx_safefree(ftpc->dirs);
   ftpc->dirdepth = 0;
-  Curl_safefree(ftpc->rawpath);
+  curlx_safefree(ftpc->rawpath);
   ftpc->file = NULL;
 }
 
@@ -209,7 +211,7 @@
   const char *slashPos = NULL;
   const char *fileName = NULL;
   CURLcode result = CURLE_OK;
-  const char *rawPath = NULL; /* url-decoded "raw" path */
+  const char *rawPath = NULL; /* URL-decoded "raw" path */
   size_t pathLen = 0;
 
   ftpc->ctl_valid = FALSE;
@@ -217,7 +219,7 @@
 
   if(ftpc->rawpath)
     freedirs(ftpc);
-  /* url-decode ftp path before further evaluation */
+  /* URL-decode ftp path before further evaluation */
   result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL);
   if(result) {
     failf(data, "path contains control characters");
@@ -232,8 +234,8 @@
       fileName = rawPath;  /* this is a full file path */
     /*
       else: ftpc->file is not used anywhere other than for operations on
-            a file. In other words, never for directory operations.
-            So we can safely leave filename as NULL here and use it as a
+            a file. In other words, never for directory operations,
+            so we can safely leave filename as NULL here and use it as a
             argument in dir/file decisions.
     */
     break;
@@ -305,7 +307,7 @@
     ftpc->file = fileName;
   else
     ftpc->file = NULL; /* instead of point to a zero byte,
-                            we make it a NULL pointer */
+                          we make it a NULL pointer */
 
   if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
     /* We need a filename when uploading. Return error! */
@@ -657,7 +659,7 @@
 
   while(!*ftpcodep && !result) {
     /* check and reset timeout value every lap */
-    timediff_t timeout = Curl_pp_state_timeout(data, pp);
+    timediff_t timeout = Curl_pp_state_timeleft_ms(data, pp);
     timediff_t interval_ms;
 
     if(timeout <= 0) {
@@ -677,7 +679,7 @@
      * A caution here is that the ftp_readresp() function has a cache that may
      * contain pieces of a response from the previous invoke and we need to
      * make sure we do not wait for input while there is unhandled data in
-     * that cache. But also, if the cache is there, we call ftp_readresp() and
+     * that cache. Also, if the cache is there, we call ftp_readresp() and
      * the cache was not good enough to continue we must not busy-loop around
      * this function.
      *
@@ -730,7 +732,7 @@
   } /* while there is buffer left and loop is requested */
 
   pp->pending_resp = FALSE;
-  CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d",
+  CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d",
                result, *nreadp, *ftpcodep);
 
   return result;
@@ -1060,8 +1062,9 @@
   CURLcode result;
 
   *resp = NULL;
-  result = Curl_resolv_blocking(data, host, 0, conn->ip_version,
-                                dns_entryp);
+  result = Curl_resolv_blocking(
+    data, Curl_resolv_dns_queries(data, conn->ip_version),
+    host, 0, Curl_conn_get_transport(data, conn), dns_entryp);
   if(result)
     failf(data, "failed to resolve the address provided to PORT: %s", host);
   else {
@@ -1385,7 +1388,7 @@
   /* cleanup */
 
   if(dns_entry)
-    Curl_resolv_unlink(data, &dns_entry);
+    Curl_dns_entry_unlink(data, &dns_entry);
   if(result) {
     ftp_state(data, ftpc, FTP_STOP);
   }
@@ -1559,7 +1562,7 @@
   char *cmd;
 
   if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
-    /* url-decode before evaluation: e.g. paths starting/ending with %2f */
+    /* URL-decode before evaluation: e.g. paths starting/ending with %2f */
     const char *rawPath = ftpc->rawpath;
     const char *slashPos = strrchr(rawPath, '/');
     if(slashPos) {
@@ -1683,17 +1686,17 @@
 
   if((data->state.resume_from && !sizechecked) ||
      ((data->state.resume_from > 0) && sizechecked)) {
-    /* we are about to continue the uploading of a file */
-    /* 1. get already existing file's size. We use the SIZE command for this
-       which may not exist in the server!  The SIZE command is not in
-       RFC959. */
+    /* we are about to continue the uploading of a file
+       1. get already existing file's size. We use the SIZE command for this
+          which may not exist in the server!  The SIZE command is not in
+          RFC959.
 
-    /* 2. This used to set REST. But since we can do append, we issue no
-       another ftp command. Skip the source file offset and APPEND the rest on
-       the file instead */
+       2. This used to set REST, but since we can do append, we issue no
+          another ftp command. Skip the source file offset and APPEND the rest
+          on the file instead
 
-    /* 3. pass file-size number of bytes in the source file */
-    /* 4. lower the infilesize counter */
+       3. pass file-size number of bytes in the source file
+       4. lower the infilesize counter */
     /* => transfer as usual */
     int seekerr = CURL_SEEKFUNC_OK;
 
@@ -1942,8 +1945,8 @@
                behavior.
 
                In addition: asking for the size for 'TYPE A' transfers is not
-               constructive since servers do not report the converted size. So
-               skip it.
+               constructive since servers do not report the converted size.
+               Thus, skip it.
             */
             result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
             if(!result)
@@ -2159,9 +2162,10 @@
     if(result)
       goto error;
 
-    (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port,
-                               is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4,
-                               &dns);
+    (void)Curl_resolv_blocking(
+      data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A,
+      host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn),
+      &dns);
     /* we connect to the proxy's port */
     connectport = (unsigned short)ipquad.remote_port;
 
@@ -2185,7 +2189,9 @@
         goto error;
     }
 
-    (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns);
+    (void)Curl_resolv_blocking(
+      data, Curl_resolv_dns_queries(data, conn->ip_version),
+      newhost, newport, Curl_conn_get_transport(data, conn), &dns);
     connectport = newport; /* we connect to the remote port */
 
     if(!dns) {
@@ -2195,6 +2201,12 @@
     }
   }
 
+  DEBUGASSERT(newhost);
+  curlx_free(conn->secondaryhostname);
+  conn->secondary_port = newport;
+  conn->secondaryhostname = newhost;
+  newhost = NULL;
+
   result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns,
                            conn->bits.ftp_use_data_ssl ?
                            CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
@@ -2202,10 +2214,8 @@
   if(result) {
     if((result != CURLE_OUT_OF_MEMORY) &&
        (ftpc->count1 == 0) && (ftpcode == 229)) {
-      curlx_free(newhost);
-      return ftp_epsv_disable(data, ftpc, conn);
+      result = ftp_epsv_disable(data, ftpc, conn);
     }
-
     goto error;
   }
 
@@ -2222,22 +2232,16 @@
      */
     char buf[256];
     Curl_printable_address(dns->addr, buf, sizeof(buf));
-    infof(data, "Connecting to %s (%s) port %d", newhost, buf, connectport);
+    infof(data, "Connecting to %s (%s) port %d",
+          conn->secondaryhostname, buf, connectport);
   }
 #endif
 
-  curlx_free(conn->secondaryhostname);
-  conn->secondary_port = newport;
-  conn->secondaryhostname = curlx_strdup(newhost);
-  if(!conn->secondaryhostname) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto error;
-  }
-
   conn->bits.do_more = TRUE;
   ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
 
 error:
+  Curl_dns_entry_unlink(data, &dns);
   curlx_free(newhost);
   return result;
 }
@@ -2382,10 +2386,10 @@
       else if((data->state.list_only || !ftpc->file) &&
               !(data->set.prequote)) {
         /* The specified path ends with a slash, and therefore we think this
-           is a directory that is requested, use LIST. But before that we
+           is a directory that is requested, use LIST. Before that, we also
            need to set ASCII transfer mode. */
 
-        /* But only if a body transfer was requested. */
+        /* Only if a body transfer was requested. */
         if(ftp->transfer == PPTRANSFER_BODY) {
           result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE);
           if(result)
@@ -2490,24 +2494,32 @@
   return result;
 }
 
-static int twodigit(const char *p)
+/* return TRUE on error, FALSE on success */
+static bool twodigit(const char *p, int *val)
 {
-  return ((p[0] - '0') * 10) + (p[1] - '0');
+  if(!ISDIGIT(p[0]) || !ISDIGIT(p[1]))
+    return TRUE;
+  /* curlx_hexval() works fine here since we make sure it is decimal above */
+  *val = (curlx_hexval(p[0]) * 10) + curlx_hexval(p[1]);
+  return FALSE;
 }
 
-static bool ftp_213_date(const char *p, int *year, int *month, int *day,
-                         int *hour, int *minute, int *second)
+/*
+ * @unittest 1668
+ */
+UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day,
+                           int *hour, int *minute, int *second);
+UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day,
+                           int *hour, int *minute, int *second)
 {
-  size_t len = strlen(p);
-  if(len < 14)
+  int century;
+  if((strlen(p) < 14) || twodigit(&p[0], &century) || twodigit(&p[2], year) ||
+     twodigit(&p[4], month) || twodigit(&p[6], day) ||
+     twodigit(&p[8], hour) || twodigit(&p[10], minute) ||
+     twodigit(&p[12], second))
     return FALSE;
-  *year = (twodigit(&p[0]) * 100) + twodigit(&p[2]);
-  *month = twodigit(&p[4]);
-  *day = twodigit(&p[6]);
-  *hour = twodigit(&p[8]);
-  *minute = twodigit(&p[10]);
-  *second = twodigit(&p[12]);
 
+  *year += century * 100;
   if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
      (*second > 60))
     return FALSE;
@@ -2568,7 +2580,7 @@
     /* If we asked for a time of the file and we actually got one as well,
        we "emulate" an HTTP-style header in our output. */
 
-#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
+#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__))
 #pragma GCC diagnostic push
 /* 'time_t' is unsigned in MSDOS and AmigaOS. Silence:
    warning: comparison of unsigned expression in '>= 0' is always true */
@@ -2576,7 +2588,7 @@
 #endif
     if(data->req.no_body && ftpc->file &&
        data->set.get_filetime && showtime) {
-#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
+#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__))
 #pragma GCC diagnostic pop
 #endif
       char headerbuf[128];
@@ -3078,10 +3090,18 @@
             break; /* get out of this loop */
           }
         }
-        else
+        else {
+          if(ISCNTRL(*ptr)) {
+            /* control characters have no business in a path */
+            curlx_dyn_free(&out);
+            return CURLE_WEIRD_SERVER_REPLY;
+          }
           result = curlx_dyn_addn(&out, ptr, 1);
-        if(result)
+        }
+        if(result) {
+          curlx_dyn_free(&out);
           return result;
+        }
       }
     }
     if(entry_extracted) {
@@ -3709,7 +3729,7 @@
           if(data->set.ftp_filemethod == FTPFILE_NOCWD)
             pathLen = 0; /* relative path => working directory is FTP home */
           else
-            /* file is url-decoded */
+            /* file is URL-decoded */
             pathLen -= ftpc->file ? strlen(ftpc->file) : 0;
           ftpc->prevpath = curlx_memdup0(rawPath, pathLen);
         }
@@ -3867,12 +3887,12 @@
  * This is the actual DO function for FTP. Get a file/directory according to
  * the options previously setup.
  */
-static
-CURLcode ftp_perform(struct Curl_easy *data,
-                     struct ftp_conn *ftpc,
-                     struct FTP *ftp,
-                     bool *connected,  /* connect status after PASV / PORT */
-                     bool *dophase_done)
+static CURLcode ftp_perform(
+  struct Curl_easy *data,
+  struct ftp_conn *ftpc,
+  struct FTP *ftp,
+  bool *connected,  /* connect status after PASV / PORT */
+  bool *dophase_done)
 {
   /* this is FTP and no proxy */
   CURLcode result = CURLE_OK;
@@ -4006,7 +4026,7 @@
     Curl_ftp_parselist_data_free(&ftpwc->parser);
     curlx_free(ftpwc);
   }
-  Curl_safefree(wildcard->pattern);
+  curlx_safefree(wildcard->pattern);
   wildcard->dtor = ZERO_NULL;
   wildcard->ftpwc = NULL;
   return result;
@@ -4341,7 +4361,7 @@
   struct FTP *ftp = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(ftp->pathalloc);
+  curlx_safefree(ftp->pathalloc);
   curlx_free(ftp);
 }
 
@@ -4351,11 +4371,11 @@
   (void)key;
   (void)klen;
   freedirs(ftpc);
-  Curl_safefree(ftpc->account);
-  Curl_safefree(ftpc->alternative_to_user);
-  Curl_safefree(ftpc->entrypath);
-  Curl_safefree(ftpc->prevpath);
-  Curl_safefree(ftpc->server_os);
+  curlx_safefree(ftpc->account);
+  curlx_safefree(ftpc->alternative_to_user);
+  curlx_safefree(ftpc->entrypath);
+  curlx_safefree(ftpc->prevpath);
+  curlx_safefree(ftpc->server_os);
   Curl_pp_disconnect(&ftpc->pp);
   curlx_free(ftpc);
 }
@@ -4418,7 +4438,7 @@
     ftpc->alternative_to_user =
       curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
     if(!ftpc->alternative_to_user) {
-      Curl_safefree(ftpc->account);
+      curlx_safefree(ftpc->account);
       Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
       return CURLE_OUT_OF_MEMORY;
     }
@@ -4457,7 +4477,7 @@
 /*
  * FTP protocol.
  */
-static const struct Curl_protocol Curl_protocol_ftp = {
+const struct Curl_protocol Curl_protocol_ftp = {
   ftp_setup_connection,            /* setup_connection */
   ftp_do,                          /* do_it */
   ftp_done,                        /* done */
@@ -4472,46 +4492,9 @@
   ftp_disconnect,                  /* disconnect */
   ZERO_NULL,                       /* write_resp */
   ZERO_NULL,                       /* write_resp_hd */
-  ZERO_NULL,                       /* connection_check */
+  ZERO_NULL,                       /* connection_is_dead */
   ZERO_NULL,                       /* attach connection */
   ZERO_NULL,                       /* follow */
 };
 
 #endif /* CURL_DISABLE_FTP */
-
-/*
- * FTP protocol handler.
- */
-const struct Curl_scheme Curl_scheme_ftp = {
-  "ftp",                           /* scheme */
-#ifdef CURL_DISABLE_FTP
-  ZERO_NULL,
-#else
-  &Curl_protocol_ftp,
-#endif
-  CURLPROTO_FTP,                   /* protocol */
-  CURLPROTO_FTP,                   /* family */
-  PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
-  PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
-  PROTOPT_WILDCARD | PROTOPT_SSL_REUSE |
-  PROTOPT_CONN_REUSE, /* flags */
-  PORT_FTP,                        /* defport */
-};
-
-/*
- * FTPS protocol handler.
- */
-const struct Curl_scheme Curl_scheme_ftps = {
-  "ftps",                          /* scheme */
-#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_ftp,
-#endif
-  CURLPROTO_FTPS,                  /* protocol */
-  CURLPROTO_FTP,                   /* family */
-  PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
-  PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD |
-  PROTOPT_CONN_REUSE, /* flags */
-  PORT_FTPS,                       /* defport */
-};
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h
index 20b3600..257f595 100644
--- a/Utilities/cmcurl/lib/ftp.h
+++ b/Utilities/cmcurl/lib/ftp.h
@@ -25,73 +25,11 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#include "pingpong.h"
-
-extern const struct Curl_scheme Curl_scheme_ftp;
-extern const struct Curl_scheme Curl_scheme_ftps;
-
 #ifndef CURL_DISABLE_FTP
+extern const struct Curl_protocol Curl_protocol_ftp;
+
 bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn);
 
-#endif /* CURL_DISABLE_FTP */
-
-/****************************************************************************
- * FTP unique setup
- ***************************************************************************/
-enum {
-  FTP_STOP,    /* do nothing state, stops the state machine */
-  FTP_WAIT220, /* waiting for the initial 220 response immediately after
-                  a connect */
-  FTP_AUTH,
-  FTP_USER,
-  FTP_PASS,
-  FTP_ACCT,
-  FTP_PBSZ,
-  FTP_PROT,
-  FTP_CCC,
-  FTP_PWD,
-  FTP_SYST,
-  FTP_NAMEFMT,
-  FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
-  FTP_RETR_PREQUOTE,
-  FTP_STOR_PREQUOTE,
-  FTP_LIST_PREQUOTE,
-  FTP_POSTQUOTE,
-  FTP_CWD,  /* change directory */
-  FTP_MKD,  /* if the directory did not exist */
-  FTP_MDTM, /* to figure out the datestamp */
-  FTP_TYPE, /* to set type when doing a head-like request */
-  FTP_LIST_TYPE, /* set type when about to do a directory list */
-  FTP_RETR_LIST_TYPE,
-  FTP_RETR_TYPE, /* set type when about to RETR a file */
-  FTP_STOR_TYPE, /* set type when about to STOR a file */
-  FTP_SIZE, /* get the remote file's size for head-like request */
-  FTP_RETR_SIZE, /* get the remote file's size for RETR */
-  FTP_STOR_SIZE, /* get the size for STOR */
-  FTP_REST, /* when used to check if the server supports it in head-like */
-  FTP_RETR_REST, /* when asking for "resume" in for RETR */
-  FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */
-  FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */
-  FTP_PASV, /* generic state for PASV and EPSV, check count1 */
-  FTP_LIST, /* generic state for LIST, NLST or a custom list command */
-  FTP_RETR,
-  FTP_STOR, /* generic state for STOR and APPE */
-  FTP_QUIT,
-  FTP_LAST  /* never used */
-};
-typedef unsigned char ftpstate; /* use the enum values */
-
-struct ftp_parselist_data; /* defined later in ftplistparser.c */
-
-struct ftp_wc {
-  struct ftp_parselist_data *parser;
-
-  struct {
-    curl_write_callback write_function;
-    FILE *file_descriptor;
-  } backup;
-};
-
 typedef enum {
   FTPFILE_MULTICWD  = 1, /* as defined by RFC1738 */
   FTPFILE_NOCWD     = 2, /* use SIZE / RETR / STOR on the full path */
@@ -99,74 +37,7 @@
                             file */
 } curl_ftpfile;
 
-/* This FTP struct is used in the Curl_easy. All FTP data that is
-   connection-oriented must be in FTP_conn to properly deal with the fact that
-   perhaps the Curl_easy is changed between the times the connection is
-   used. */
-struct FTP {
-  char *path;    /* points to the urlpieces struct field */
-  char *pathalloc; /* if non-NULL a pointer to an allocated path */
-
-  /* transfer a file/body or not, done as a typedefed enum to make debuggers
-     display the full symbol and not the numerical value */
-  curl_pp_transfer transfer;
-  curl_off_t downloadsize;
-};
-
-/* one struct entry for each path component (of 'rawpath') */
-struct pathcomp {
-  int start; /* start column */
-  int len;   /* length in bytes */
-};
-
-/* ftp_conn is used for struct connection-oriented data in the connectdata
-   struct */
-struct ftp_conn {
-  struct pingpong pp;
-  char *account;
-  char *alternative_to_user;
-  char *entrypath; /* the PWD reply when we logged on */
-  const char *file; /* url-decoded filename (or path), points into rawpath */
-  char *rawpath; /* URL decoded, allocated, version of the path */
-  struct pathcomp *dirs; /* allocated array for path components */
-  char *prevpath;   /* url-decoded conn->path from the previous transfer */
-  char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
-                        and others (A/I or zero) */
-  char *server_os;     /* The target server operating system. */
-  curl_off_t known_filesize; /* file size is different from -1, if wildcard
-                                LIST parsing was done and wc_statemach set
-                                it */
-  int count1; /* general purpose counter for the state machine */
-  int count2; /* general purpose counter for the state machine */
-  int count3; /* general purpose counter for the state machine */
-  unsigned short dirdepth;  /* number of entries used in the 'dirs' array,
-                               < FTP_MAX_DIR_DEPTH */
-  unsigned short cwdcount;  /* number of CWD commands issued,
-                               < FTP_MAX_DIR_DEPTH */
-  unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change
-                          state! */
-  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 ccc;       /* ccc level for this connection */
-  BIT(ftp_trying_alternative);
-  BIT(dont_check);  /* Set to TRUE to prevent the final (post-transfer)
-                       file size and 226/250 status check. It should still
-                       read the line, ignore the result. */
-  BIT(ctl_valid);   /* Tells Curl_ftp_quit() whether or not to do anything. If
-                       the connection has timed out or been closed, this
-                       should be FALSE when it gets to Curl_ftp_quit() */
-  BIT(cwddone);     /* if it has been determined that the proper CWD combo
-                       already has been done */
-  BIT(cwdfail);     /* set TRUE if a CWD command fails, as then we must prevent
-                       caching the current directory */
-  BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
-  BIT(shutdown);    /* connection is being shutdown, e.g. QUIT */
-};
-
-/* meta key for storing `struct FTP` as easy meta data */
-#define CURL_META_FTP_EASY   "meta:proto:ftp:easy"
-/* meta key for storing `struct ftp_conn` as connection meta data */
-#define CURL_META_FTP_CONN   "meta:proto:ftp:conn"
+#endif /* CURL_DISABLE_FTP */
 
 #define DEFAULT_ACCEPT_TIMEOUT   60000 /* milliseconds == one minute */
 
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index f82895d..b5d9338 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -44,6 +44,7 @@
 #include "fileinfo.h"
 #include "llist.h"
 #include "ftp.h"
+#include "ftp-int.h"
 #include "ftplistparser.h"
 #include "curl_fnmatch.h"
 #include "multiif.h"
@@ -324,8 +325,7 @@
 
   /* filter pattern-corresponding filenames */
   Curl_set_in_callback(data, TRUE);
-  if(compare(data->set.fnmatch_data, wc->pattern,
-             finfo->filename) == 0) {
+  if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) {
     /* discard symlink which is containing multiple " -> " */
     if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
        (strstr(finfo->strings.target, " -> "))) {
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index dedafd3..fab63e6 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -78,6 +78,7 @@
 
   info->conn_scheme = 0;
   info->conn_protocol = 0;
+  info->used_proxy = 0;
 
 #ifdef USE_SSL
   Curl_ssl_free_certinfo(data);
@@ -414,6 +415,9 @@
   case CURLINFO_FILETIME_T:
     *param_offt = (curl_off_t)data->info.filetime;
     break;
+  case CURLINFO_SIZE_DELIVERED:
+    *param_offt = data->progress.deliver;
+    break;
   case CURLINFO_SIZE_UPLOAD_T:
     *param_offt = data->progress.ul.cur_size;
     break;
@@ -582,14 +586,16 @@
     break;
   case CURLINFO_TLS_SESSION:
   case CURLINFO_TLS_SSL_PTR: {
+    int query = (info == CURLINFO_TLS_SSL_PTR) ?
+      CF_QUERY_SSL_INFO : CF_QUERY_SSL_CTX_INFO;
     struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
-                                        param_slistp;
+      param_slistp;
     struct curl_tlssessioninfo *tsi = &data->tsi;
 
     /* we are exposing a pointer to internal memory with unknown
      * lifetime here. */
     *tsip = tsi;
-    if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) {
+    if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, query, tsi)) {
       tsi->backend = Curl_ssl_backend();
       tsi->internals = NULL;
     }
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index 71ff392..f087121 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -172,7 +172,7 @@
  * connect-command-download protocols.
  */
 
-static const struct Curl_protocol Curl_protocol_gopher = {
+const struct Curl_protocol Curl_protocol_gopher = {
   ZERO_NULL,                            /* setup_connection */
   gopher_do,                            /* do_it */
   ZERO_NULL,                            /* done */
@@ -187,13 +187,13 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
 #ifdef USE_SSL
-static const struct Curl_protocol Curl_protocol_gophers = {
+const struct Curl_protocol Curl_protocol_gophers = {
   ZERO_NULL,                            /* setup_connection */
   gopher_do,                            /* do_it */
   ZERO_NULL,                            /* done */
@@ -208,36 +208,10 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 #endif
 
 #endif /* CURL_DISABLE_GOPHER */
-
-const struct Curl_scheme Curl_scheme_gopher = {
-  "gopher",                             /* scheme */
-#ifdef CURL_DISABLE_GOPHER
-  ZERO_NULL,
-#else
-  &Curl_protocol_gopher,
-#endif
-  CURLPROTO_GOPHER,                     /* protocol */
-  CURLPROTO_GOPHER,                     /* family */
-  PROTOPT_NONE,                         /* flags */
-  PORT_GOPHER,                          /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_gophers = {
-  "gophers",                            /* scheme */
-#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_gophers,
-#endif
-  CURLPROTO_GOPHERS,                    /* protocol */
-  CURLPROTO_GOPHER,                     /* family */
-  PROTOPT_SSL,                          /* flags */
-  PORT_GOPHER,                          /* defport */
-};
diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h
index bc977bc..1c126be 100644
--- a/Utilities/cmcurl/lib/gopher.h
+++ b/Utilities/cmcurl/lib/gopher.h
@@ -23,7 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_gopher;
-extern const struct Curl_scheme Curl_scheme_gophers;
+#ifndef CURL_DISABLE_GOPHER
+extern const struct Curl_protocol Curl_protocol_gopher;
+extern const struct Curl_protocol Curl_protocol_gophers;
+#endif
 
 #endif /* HEADER_CURL_GOPHER_H */
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c
index 8095ca2..3eae1d2 100644
--- a/Utilities/cmcurl/lib/hash.c
+++ b/Utilities/cmcurl/lib/hash.c
@@ -265,7 +265,7 @@
   DEBUGASSERT(h->init == HASHINIT);
   if(h->table) {
     Curl_hash_clean(h);
-    Curl_safefree(h->table);
+    curlx_safefree(h->table);
   }
   DEBUGASSERT(h->size == 0);
   h->slots = 0;
@@ -359,8 +359,8 @@
 #endif
 }
 
-struct Curl_hash_element *
-Curl_hash_next_element(struct Curl_hash_iterator *iter)
+struct Curl_hash_element *Curl_hash_next_element(
+  struct Curl_hash_iterator *iter)
 {
   struct Curl_hash *h;
   DEBUGASSERT(iter->init == ITERINIT);
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h
index 267f91d..1d7d3de 100644
--- a/Utilities/cmcurl/lib/hash.h
+++ b/Utilities/cmcurl/lib/hash.h
@@ -97,8 +97,8 @@
                              size_t key2_len);
 void Curl_hash_start_iterate(struct Curl_hash *hash,
                              struct Curl_hash_iterator *iter);
-struct Curl_hash_element *
-Curl_hash_next_element(struct Curl_hash_iterator *iter);
+struct Curl_hash_element *Curl_hash_next_element(
+  struct Curl_hash_iterator *iter);
 
 void Curl_hash_print(struct Curl_hash *h, void (*func)(void *));
 
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c
index 5a6b120..195e12b 100644
--- a/Utilities/cmcurl/lib/headers.c
+++ b/Utilities/cmcurl/lib/headers.c
@@ -52,23 +52,23 @@
 }
 
 /* public API */
-CURLHcode curl_easy_header(CURL *easy,
+CURLHcode curl_easy_header(CURL *curl,
                            const char *name,
                            size_t nameindex,
-                           unsigned int type,
+                           unsigned int origin,
                            int request,
                            struct curl_header **hout)
 {
   struct Curl_llist_node *e;
   struct Curl_llist_node *e_pick = NULL;
-  struct Curl_easy *data = easy;
+  struct Curl_easy *data = curl;
   size_t match = 0;
   size_t amount = 0;
   struct Curl_header_store *hs = NULL;
   struct Curl_header_store *pick = NULL;
   if(!name || !hout || !data ||
-     (type > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
-              CURLH_PSEUDO)) || !type || (request < -1))
+     (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX |
+                CURLH_PSEUDO)) || !origin || (request < -1))
     return CURLHE_BAD_ARGUMENT;
   if(!Curl_llist_count(&data->state.httphdrs))
     return CURLHE_NOHEADERS; /* no headers available */
@@ -81,7 +81,7 @@
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     hs = Curl_node_elem(e);
     if(curl_strequal(hs->name, name) &&
-       (hs->type & type) &&
+       (hs->type & origin) &&
        (hs->request == request)) {
       amount++;
       pick = hs;
@@ -100,7 +100,7 @@
     for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
       hs = Curl_node_elem(e);
       if(curl_strequal(hs->name, name) &&
-         (hs->type & type) &&
+         (hs->type & origin) &&
          (hs->request == request) &&
          (match++ == nameindex)) {
         e_pick = e;
@@ -118,12 +118,12 @@
 }
 
 /* public API */
-struct curl_header *curl_easy_nextheader(CURL *easy,
-                                         unsigned int type,
+struct curl_header *curl_easy_nextheader(CURL *curl,
+                                         unsigned int origin,
                                          int request,
                                          struct curl_header *prev)
 {
-  struct Curl_easy *data = easy;
+  struct Curl_easy *data = curl;
   struct Curl_llist_node *pick;
   struct Curl_llist_node *e;
   struct Curl_header_store *hs;
@@ -149,7 +149,7 @@
     /* make sure it is the next header of the desired type */
     do {
       hs = Curl_node_elem(pick);
-      if((hs->type & type) && (hs->request == request))
+      if((hs->type & origin) && (hs->request == request))
         break;
       pick = Curl_node_next(pick);
     } while(pick);
@@ -167,7 +167,7 @@
     struct Curl_header_store *check = Curl_node_elem(e);
     if(curl_strequal(hs->name, check->name) &&
        (check->request == request) &&
-       (check->type & type))
+       (check->type & origin))
       amount++;
     if(e == pick)
       index = amount - 1;
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h
index 85905e5..283179a 100644
--- a/Utilities/cmcurl/lib/headers.h
+++ b/Utilities/cmcurl/lib/headers.h
@@ -38,7 +38,7 @@
 
 /*
  * Initialize header collecting for a transfer.
- * Will add a client writer that catches CLIENTWRITE_HEADER writes.
+ * Add a client writer that catches CLIENTWRITE_HEADER writes.
  */
 CURLcode Curl_headers_init(struct Curl_easy *data);
 
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c
index 028b476..a4d6ebd 100644
--- a/Utilities/cmcurl/lib/hmac.c
+++ b/Utilities/cmcurl/lib/hmac.c
@@ -25,8 +25,8 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) ||      \
-  !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) ||   \
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) ||    \
+  !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
   defined(USE_SSL)
 
 #include "curl_hmac.h"
@@ -98,13 +98,12 @@
   return NULL;
 }
 
-int Curl_HMAC_update(struct HMAC_context *ctxt,
-                     const unsigned char *data,
-                     unsigned int len)
+void Curl_HMAC_update(struct HMAC_context *ctxt,
+                      const unsigned char *data,
+                      unsigned int len)
 {
   /* Update first hash calculation. */
   ctxt->hash->hupdate(ctxt->hashctxt1, data, len);
-  return 0;
 }
 
 int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output)
@@ -143,17 +142,24 @@
  */
 CURLcode Curl_hmacit(const struct HMAC_params *hashparams,
                      const unsigned char *key, const size_t keylen,
-                     const unsigned char *data, const size_t datalen,
+                     const unsigned char *data, size_t datalen,
                      unsigned char *output)
 {
-  struct HMAC_context *ctxt =
-    Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen));
+  struct HMAC_context *ctxt;
+  if(keylen > UINT_MAX) /* unlikely to ever happen */
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  ctxt = Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen));
 
   if(!ctxt)
     return CURLE_OUT_OF_MEMORY;
 
   /* Update the digest with the given challenge */
-  Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen));
+  do {
+    unsigned int ilen = (unsigned int)CURLMIN(datalen, UINT_MAX);
+    Curl_HMAC_update(ctxt, data, ilen);
+    datalen -= ilen;
+    data += ilen;
+  } while(datalen);
 
   /* Finalise the digest */
   Curl_HMAC_final(ctxt, output);
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index 542b655..85f53c4 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -46,21 +46,18 @@
 #include "urldata.h"
 #include "curl_addrinfo.h"
 #include "curl_trc.h"
-#include "connect.h"
+#include "dnscache.h"
 #include "hostip.h"
-#include "hash.h"
 #include "httpsrr.h"
-#include "rand.h"
-#include "curl_share.h"
 #include "url.h"
-#include "curlx/inet_ntop.h"
-#include "curlx/inet_pton.h"
 #include "multiif.h"
-#include "doh.h"
 #include "progress.h"
+#include "doh.h"
 #include "select.h"
 #include "strcase.h"
 #include "easy_lock.h"
+#include "curlx/inet_ntop.h"
+#include "curlx/inet_pton.h"
 #include "curlx/strcopy.h"
 #include "curlx/strparse.h"
 
@@ -77,6 +74,21 @@
 
 #define MAX_DNS_CACHE_SIZE 29999
 
+#define RESOLV_FAIL(for_proxy) \
+  ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST)
+
+#define IS_RESOLV_FAIL(result) \
+  (((result) == CURLE_COULDNT_RESOLVE_HOST) || \
+   ((result) == CURLE_COULDNT_RESOLVE_PROXY))
+/*
+ * ipv6works() returns TRUE if IPv6 seems to work.
+ */
+#ifdef USE_IPV6
+static bool ipv6works(struct Curl_easy *data);
+#else
+#define ipv6works(x) FALSE
+#endif
+
 /*
  * hostip.c explained
  * ==================
@@ -89,10 +101,10 @@
  * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4
  * defined.
  *
- * CURLRES_ARES - is defined if libcurl is built to use c-ares for
+ * USE_RESOLV_ARES - is defined if libcurl is built to use c-ares for
  * asynchronous name resolves. This can be Windows or *nix.
  *
- * CURLRES_THREADED - is defined if libcurl is built to run under (native)
+ * USE_RESOLV_THREADED - is defined if libcurl is built to run under (native)
  * Windows, and then the name resolve will be done in a new thread, and the
  * supported API will be the same as for ares-builds.
  *
@@ -114,85 +126,49 @@
  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
  */
 
+uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version)
+{
+  (void)data;
+  switch(ip_version) {
+  case CURL_IPRESOLVE_V6:
+    return CURL_DNSQ_AAAA;
+  case CURL_IPRESOLVE_V4:
+    return CURL_DNSQ_A;
+  default:
+    if(ipv6works(data))
+      return (CURL_DNSQ_A | CURL_DNSQ_AAAA);
+    else
+      return CURL_DNSQ_A;
+  }
+}
+
 #ifdef CURLVERBOSE
-static void show_resolve_info(struct Curl_easy *data,
-                              struct Curl_dns_entry *dns)
+const char *Curl_resolv_query_str(uint8_t dns_queries)
 {
-  const 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);
-
-  curlx_dyn_init(&out[0], 1024);
-#ifdef CURLRES_IPV6
-  curlx_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(curlx_dyn_len(d))
-        result = curlx_dyn_addn(d, ", ", 2);
-      if(!result)
-        result = curlx_dyn_add(d, buf);
-      if(result) {
-        infof(data, "too many IP, cannot show");
-        goto fail;
-      }
-    }
-    a = a->ai_next;
+  switch(dns_queries) {
+  case (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS):
+    return "A+AAAA+HTTPS";
+  case (CURL_DNSQ_A | CURL_DNSQ_AAAA):
+    return "A+AAAA";
+  case (CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS):
+    return "AAAA+HTTPS";
+  case (CURL_DNSQ_AAAA):
+    return "AAAA";
+  case (CURL_DNSQ_A | CURL_DNSQ_HTTPS):
+    return "A+HTTPS";
+  case (CURL_DNSQ_A):
+    return "A";
+  case (CURL_DNSQ_HTTPS):
+    return "HTTPS";
+  case 0:
+    return "-";
+  default:
+    DEBUGASSERT(0);
+    return "???";
   }
-
-#ifdef CURLRES_IPV6
-  infof(data, "IPv6: %s",
-        (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
-#endif
-  infof(data, "IPv4: %s",
-        (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
-
-fail:
-  curlx_dyn_free(&out[0]);
-#ifdef CURLRES_IPV6
-  curlx_dyn_free(&out[1]);
-#endif
 }
-#else
-#define show_resolve_info(x, y) Curl_nop_stmt
 #endif
 
-static void dnscache_entry_free(struct Curl_dns_entry *dns)
-{
-  Curl_freeaddrinfo(dns->addr);
-#ifdef USE_HTTPSRR
-  if(dns->hinfo) {
-    Curl_httpsrr_cleanup(dns->hinfo);
-    curlx_free(dns->hinfo);
-  }
-#endif
-  curlx_free(dns);
-}
-
 /*
  * 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
@@ -226,449 +202,17 @@
   }
 }
 
-/*
- * Create a hostcache id string for the provided host + port, to be used by
- * the DNS caching. Without alloc. Return length of the id string.
- */
-static size_t create_dnscache_id(const char *name,
-                                 size_t nlen, /* 0 or actual name length */
-                                 int port, char *ptr, size_t buflen)
-{
-  size_t len = nlen ? nlen : strlen(name);
-  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
-  if(len > (buflen - 7))
-    len = buflen - 7;
-  /* store and lower case the name */
-  Curl_strntolower(ptr, name, len);
-  return curl_msnprintf(&ptr[len], 7, ":%u", port) + len;
-}
-
-struct dnscache_prune_data {
-  struct curltime now;
-  timediff_t oldest_ms; /* oldest time in cache not pruned. */
-  timediff_t max_age_ms;
-};
-
-/*
- * This function is set as a callback to be called for every entry in the DNS
- * cache when we want to prune old unused entries.
- *
- * Returning non-zero means remove the entry, return 0 to keep it in the
- * cache.
- */
-static int dnscache_entry_is_stale(void *datap, void *hc)
-{
-  struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap;
-  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc;
-
-  if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) {
-    /* get age in milliseconds */
-    timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp);
-    if(!dns->addr)
-      age *= 2; /* negative entries age twice as fast */
-    if(age >= prune->max_age_ms)
-      return TRUE;
-    if(age > prune->oldest_ms)
-      prune->oldest_ms = age;
-  }
-  return FALSE;
-}
-
-/*
- * Prune the DNS cache. This assumes that a lock has already been taken.
- * Returns the 'age' of the oldest still kept entry - in milliseconds.
- */
-static timediff_t dnscache_prune(struct Curl_hash *hostcache,
-                                 timediff_t cache_timeout_ms,
-                                 struct curltime now)
-{
-  struct dnscache_prune_data user;
-
-  user.max_age_ms = cache_timeout_ms;
-  user.now = now;
-  user.oldest_ms = 0;
-
-  Curl_hash_clean_with_criterium(hostcache,
-                                 (void *)&user,
-                                 dnscache_entry_is_stale);
-
-  return user.oldest_ms;
-}
-
-static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
-{
-  if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
-    return &data->share->dnscache;
-  if(data->multi)
-    return &data->multi->dnscache;
-  return NULL;
-}
-
-static void dnscache_lock(struct Curl_easy *data,
-                          struct Curl_dnscache *dnscache)
-{
-  if(data->share && dnscache == &data->share->dnscache)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-}
-
-static void dnscache_unlock(struct Curl_easy *data,
-                            struct Curl_dnscache *dnscache)
-{
-  if(data->share && dnscache == &data->share->dnscache)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-}
-
-/*
- * Library-wide function for pruning the DNS cache. This function takes and
- * returns the appropriate locks.
- */
-void Curl_dnscache_prune(struct Curl_easy *data)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  /* the timeout may be set -1 (forever) */
-  timediff_t timeout_ms = data->set.dns_cache_timeout_ms;
-
-  if(!dnscache || (timeout_ms == -1))
-    /* NULL hostcache means we cannot do it */
-    return;
-
-  dnscache_lock(data, dnscache);
-
-  do {
-    /* Remove outdated and unused entries from the hostcache */
-    timediff_t oldest_ms =
-      dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data));
-
-    if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE)
-      /* prune the ones over half this age */
-      timeout_ms = oldest_ms / 2;
-    else
-      break;
-
-    /* if the cache size is still too big, use the oldest age as new prune
-       limit */
-  } while(timeout_ms);
-
-  dnscache_unlock(data, dnscache);
-}
-
-void Curl_dnscache_clear(struct Curl_easy *data)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  if(dnscache) {
-    dnscache_lock(data, dnscache);
-    Curl_hash_clean(&dnscache->entries);
-    dnscache_unlock(data, dnscache);
-  }
-}
-
 #ifdef USE_ALARM_TIMEOUT
 /* Beware this is a global and unique instance. This is used to store the
    return address that we can jump back to from inside a signal handler. This
    is not thread-safe stuff. */
 static sigjmp_buf curl_jmpenv;
-static curl_simple_lock curl_jmpenv_lock;
+static curl_simple_lock curl_jmpenv_lock = CURL_SIMPLE_LOCK_INIT;
 #endif
 
-/* lookup address, returns entry if found and not stale */
-static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
-                                         struct Curl_dnscache *dnscache,
-                                         const char *hostname,
-                                         int port,
-                                         int ip_version)
-{
-  struct Curl_dns_entry *dns = NULL;
-  char entry_id[MAX_HOSTCACHE_LEN];
-  size_t entry_len;
-
-  if(!dnscache)
-    return NULL;
-
-  /* Create an entry id, based upon the hostname and port */
-  entry_len = create_dnscache_id(hostname, 0, port,
-                                 entry_id, sizeof(entry_id));
-
-  /* See if it is already in our dns cache */
-  dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
-
-  /* No entry found in cache, check if we might have a wildcard entry */
-  if(!dns && data->state.wildcard_resolve) {
-    entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
-
-    /* See if it is already in our dns cache */
-    dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
-  }
-
-  if(dns && (data->set.dns_cache_timeout_ms != -1)) {
-    /* See whether the returned entry is stale. Done before we release lock */
-    struct dnscache_prune_data user;
-
-    user.now = *Curl_pgrs_now(data);
-    user.max_age_ms = data->set.dns_cache_timeout_ms;
-    user.oldest_ms = 0;
-
-    if(dnscache_entry_is_stale(&user, dns)) {
-      infof(data, "Hostname in DNS cache was stale, zapped");
-      dns = NULL; /* the memory deallocation is being handled by the hash */
-      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
-    }
-  }
-
-  /* See if the returned entry matches the required resolve mode */
-  if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) {
-    int pf = PF_INET;
-    bool found = FALSE;
-    struct Curl_addrinfo *addr = dns->addr;
-
-#ifdef PF_INET6
-    if(ip_version == CURL_IPRESOLVE_V6)
-      pf = PF_INET6;
-#endif
-
-    while(addr) {
-      if(addr->ai_family == pf) {
-        found = TRUE;
-        break;
-      }
-      addr = addr->ai_next;
-    }
-
-    if(!found) {
-      infof(data, "Hostname in DNS cache does not have needed family, zapped");
-      dns = NULL; /* the memory deallocation is being handled by the hash */
-      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
-    }
-  }
-  return dns;
-}
-
-/*
- * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
- *
- * Curl_resolv() checks initially and multi_runsingle() checks each time
- * it discovers the handle in the state WAITRESOLVE whether the hostname
- * has already been resolved and the address has already been stored in
- * the DNS cache. This short circuits waiting for a lot of pending
- * lookups for the same hostname requested by different handles.
- *
- * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
- *
- * The returned data *MUST* be "released" with Curl_resolv_unlink() after
- * use, or we will leak memory!
- */
-struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data,
-                                         const char *hostname,
-                                         int port,
-                                         int ip_version)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  struct Curl_dns_entry *dns = NULL;
-
-  dnscache_lock(data, dnscache);
-
-  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
-  if(dns)
-    dns->refcount++; /* we use it! */
-
-  dnscache_unlock(data, dnscache);
-
-  return dns;
-}
-
-#ifndef CURL_DISABLE_SHUFFLE_DNS
-/*
- * Return # of addresses in a Curl_addrinfo struct
- */
-static int num_addresses(const struct Curl_addrinfo *addr)
-{
-  int i = 0;
-  while(addr) {
-    addr = addr->ai_next;
-    i++;
-  }
-  return i;
-}
-
-UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
-                                    struct Curl_addrinfo **addr);
-/*
- * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
- * struct by re-linking its linked list.
- *
- * The addr argument should be the address of a pointer to the head node of a
- * `Curl_addrinfo` list and it will be modified to point to the new head after
- * shuffling.
- *
- * Not declared static only to make it easy to use in a unit test!
- *
- * @unittest: 1608
- */
-UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
-                                    struct Curl_addrinfo **addr)
-{
-  CURLcode result = CURLE_OK;
-  const int num_addrs = num_addresses(*addr);
-
-  if(num_addrs > 1) {
-    struct Curl_addrinfo **nodes;
-    infof(data, "Shuffling %i addresses", num_addrs);
-
-    nodes = curlx_malloc(num_addrs * sizeof(*nodes));
-    if(nodes) {
-      int i;
-      unsigned int *rnd;
-      const size_t rnd_size = num_addrs * sizeof(*rnd);
-
-      /* build a plain array of Curl_addrinfo pointers */
-      nodes[0] = *addr;
-      for(i = 1; i < num_addrs; i++) {
-        nodes[i] = nodes[i - 1]->ai_next;
-      }
-
-      rnd = curlx_malloc(rnd_size);
-      if(rnd) {
-        /* Fisher-Yates shuffle */
-        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
-          struct Curl_addrinfo *swap_tmp;
-          for(i = num_addrs - 1; i > 0; i--) {
-            swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)];
-            nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i];
-            nodes[i] = swap_tmp;
-          }
-
-          /* relink list in the new order */
-          for(i = 1; i < num_addrs; i++) {
-            nodes[i - 1]->ai_next = nodes[i];
-          }
-
-          nodes[num_addrs - 1]->ai_next = NULL;
-          *addr = nodes[0];
-        }
-        curlx_free(rnd);
-      }
-      else
-        result = CURLE_OUT_OF_MEMORY;
-      curlx_free(nodes);
-    }
-    else
-      result = CURLE_OUT_OF_MEMORY;
-  }
-  return result;
-}
-#endif
-
-struct Curl_dns_entry *
-Curl_dnscache_mk_entry(struct Curl_easy *data,
-                       struct Curl_addrinfo **paddr,
-                       const char *hostname,
-                       size_t hostlen, /* length or zero */
-                       int port,
-                       bool permanent)
-{
-  struct Curl_dns_entry *dns = NULL;
-
-#ifndef CURL_DISABLE_SHUFFLE_DNS
-  /* shuffle addresses if requested */
-  if(data->set.dns_shuffle_addresses && paddr) {
-    CURLcode result = Curl_shuffle_addr(data, paddr);
-    if(result)
-      goto out;
-  }
-#else
-  (void)data;
-#endif
-  if(!hostlen)
-    hostlen = strlen(hostname);
-
-  /* Create a new cache entry */
-  dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
-  if(!dns)
-    goto out;
-
-  dns->refcount = 1; /* the cache has the first reference */
-  dns->addr = paddr ? *paddr : NULL; /* this is the address(es) */
-  if(permanent) {
-    dns->timestamp.tv_sec = 0; /* an entry that never goes stale */
-    dns->timestamp.tv_usec = 0; /* an entry that never goes stale */
-  }
-  else {
-    dns->timestamp = *Curl_pgrs_now(data);
-  }
-  dns->hostport = port;
-  if(hostlen)
-    memcpy(dns->hostname, hostname, hostlen);
-
-out:
-  if(paddr) {
-    if(!dns)
-      Curl_freeaddrinfo(*paddr);
-    *paddr = NULL;
-  }
-  return dns;
-}
-
-static struct Curl_dns_entry *
-dnscache_add_addr(struct Curl_easy *data,
-                  struct Curl_dnscache *dnscache,
-                  struct Curl_addrinfo **paddr,
-                  const char *hostname,
-                  size_t hlen, /* length or zero */
-                  int port,
-                  bool permanent)
-{
-  char entry_id[MAX_HOSTCACHE_LEN];
-  size_t entry_len;
-  struct Curl_dns_entry *dns;
-  struct Curl_dns_entry *dns2;
-
-  dns = Curl_dnscache_mk_entry(data, paddr, hostname, hlen, port, permanent);
-  if(!dns)
-    return NULL;
-
-  /* Create an entry id, based upon the hostname and port */
-  entry_len = create_dnscache_id(hostname, hlen, port,
-                                 entry_id, sizeof(entry_id));
-
-  /* Store the resolved data in our DNS cache. */
-  dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
-                       (void *)dns);
-  if(!dns2) {
-    dnscache_entry_free(dns);
-    return NULL;
-  }
-
-  dns = dns2;
-  dns->refcount++;         /* mark entry as in-use */
-  return dns;
-}
-
-CURLcode Curl_dnscache_add(struct Curl_easy *data,
-                           struct Curl_dns_entry *entry)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  char id[MAX_HOSTCACHE_LEN];
-  size_t idlen;
-
-  if(!dnscache)
-    return CURLE_FAILED_INIT;
-  /* Create an entry id, based upon the hostname and port */
-  idlen = create_dnscache_id(entry->hostname, 0, entry->hostport,
-                             id, sizeof(id));
-
-  /* Store the resolved data in our DNS cache and up ref count */
-  dnscache_lock(data, dnscache);
-  if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
-    dnscache_unlock(data, dnscache);
-    return CURLE_OUT_OF_MEMORY;
-  }
-  entry->refcount++;
-  dnscache_unlock(data, dnscache);
-  return CURLE_OK;
-}
-
 #ifdef USE_IPV6
 /* return a static IPv6 ::1 for the name */
-static struct Curl_addrinfo *get_localhost6(int port, const char *name)
+static struct Curl_addrinfo *get_localhost6(uint16_t port, const char *name)
 {
   struct Curl_addrinfo *ca;
   const size_t ss_size = sizeof(struct sockaddr_in6);
@@ -680,12 +224,9 @@
   if(!ca)
     return NULL;
 
+  memset(&sa6, 0, sizeof(sa6));
   sa6.sin6_family = AF_INET6;
   sa6.sin6_port = htons(port16);
-  sa6.sin6_flowinfo = 0;
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
-  sa6.sin6_scope_id = 0;
-#endif
 
   (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
   memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
@@ -707,7 +248,7 @@
 #endif
 
 /* return a static IPv4 127.0.0.1 for the given name */
-static struct Curl_addrinfo *get_localhost(int port, const char *name)
+static struct Curl_addrinfo *get_localhost(uint16_t port, const char *name)
 {
   struct Curl_addrinfo *ca;
   struct Curl_addrinfo *ca6;
@@ -766,9 +307,9 @@
 }
 
 /*
- * Curl_ipv6works() returns TRUE if IPv6 seems to work.
+ * ipv6works() returns TRUE if IPv6 seems to work.
  */
-bool Curl_ipv6works(struct Curl_easy *data)
+static bool ipv6works(struct Curl_easy *data)
 {
   DEBUGASSERT(data);
   DEBUGASSERT(data->multi);
@@ -804,86 +345,354 @@
   return curl_strnequal(part, &full[flen - plen], plen);
 }
 
-static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
+static CURLcode hostip_resolv_failed(struct Curl_easy *data,
+                                     const char *hostname,
+                                     bool for_proxy)
 {
-#ifdef CURLRES_IPV6
-  if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
-    return FALSE;
-#elif defined(CURLRES_IPV4)
+  failf(data, "Could not resolve %s: %s",
+        for_proxy ? "proxy" : "host", hostname);
+  return RESOLV_FAIL(for_proxy);
+}
+
+static bool can_resolve_dns_queries(struct Curl_easy *data,
+                                    uint8_t dns_queries)
+{
   (void)data;
-  if(ip_version == CURL_IPRESOLVE_V6)
+  if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data))
     return FALSE;
-#else
-#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
-#endif
   return TRUE;
 }
 
-static CURLcode store_negative_resolve(struct Curl_easy *data,
-                                       const char *host,
-                                       int port)
+CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
+                                    void *resolver)
 {
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  struct Curl_dns_entry *dns;
-  DEBUGASSERT(dnscache);
-  if(!dnscache)
-    return CURLE_FAILED_INIT;
+  if(data->set.resolver_start) {
+    int rc;
 
-  /* put this new host in the cache */
-  dns = dnscache_add_addr(data, dnscache, NULL, host, 0, port, FALSE);
-  if(dns) {
-    /* release the returned reference; the cache itself will keep the
-     * entry alive: */
-    dns->refcount--;
-    infof(data, "Store negative name resolve for %s:%d", host, port);
-    return CURLE_OK;
+    CURL_TRC_DNS(data, "announcing resolve to application");
+    Curl_set_in_callback(data, TRUE);
+    rc = data->set.resolver_start(resolver, NULL,
+                                  data->set.resolver_start_client);
+    Curl_set_in_callback(data, FALSE);
+    if(rc) {
+      CURL_TRC_DNS(data, "application aborted resolve");
+      return CURLE_ABORTED_BY_CALLBACK;
+    }
   }
-  return CURLE_OUT_OF_MEMORY;
+  return CURLE_OK;
 }
 
-/*
- * Curl_resolv() is the main name resolve function within libcurl. It resolves
- * a name and returns a pointer to the entry in the 'entry' argument. This
- * function might return immediately if we are using asynch resolves. See the
- * return codes.
- *
- * The cache entry we return will get its 'inuse' counter increased when this
- * function is used. You MUST call Curl_resolv_unlink() later (when you are
- * done using this struct) to decrease the reference counter again.
- *
- * Return codes:
- * CURLE_OK = success, *entry set to non-NULL
- * CURLE_AGAIN = resolving in progress, *entry == NULL
- * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
- * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
- */
-CURLcode Curl_resolv(struct Curl_easy *data,
-                     const char *hostname,
-                     int port,
-                     int ip_version,
-                     bool allowDOH,
-                     struct Curl_dns_entry **entry)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  struct Curl_dns_entry *dns = NULL;
-  struct Curl_addrinfo *addr = NULL;
-  bool respwait = FALSE;
-  size_t hostname_len;
-  CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
+#ifdef USE_CURL_ASYNC
 
-  *entry = NULL;
+static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data,
+                                                  uint8_t dns_queries,
+                                                  const char *hostname,
+                                                  uint16_t port,
+                                                  uint8_t transport,
+                                                  bool for_proxy,
+                                                  timediff_t timeout_ms)
+{
+  struct Curl_resolv_async *async;
+  size_t hostlen = strlen(hostname);
+
+  if(!data->multi) {
+    DEBUGASSERT(0);
+    return NULL;
+  }
+
+  /* struct size already includes the NUL for hostname */
+  async = curlx_calloc(1, sizeof(*async) + hostlen);
+  if(!async)
+    return NULL;
+
+  /* Give every async resolve operation a "unique" id. This may
+   * wrap around after a long time, making collisions highly unlikely.
+   * As we keep the async structs at the easy handle, chances of
+   * easy `mid plus resolv->id` colliding should be astronomical.
+   * `resolv_id == 0` is never used. */
+  if(data->multi->last_resolv_id == UINT32_MAX)
+    data->multi->last_resolv_id = 1; /* wrap around */
+  else
+    data->multi->last_resolv_id++;
+  async->id = data->multi->last_resolv_id;
+  async->dns_queries = dns_queries;
+  async->port = port;
+  async->transport = transport;
+  async->for_proxy = for_proxy;
+  async->start = *Curl_pgrs_now(data);
+  async->timeout_ms = timeout_ms;
+  if(hostlen) {
+    memcpy(async->hostname, hostname, hostlen);
+    async->is_ipaddr = Curl_is_ipaddr(async->hostname);
+    if(async->is_ipaddr)
+      async->is_ipv4addr = Curl_is_ipv4addr(async->hostname);
+  }
+
+  return async;
+}
+
+static CURLcode hostip_resolv_take_result(struct Curl_easy *data,
+                                          struct Curl_resolv_async *async,
+                                          struct Curl_dns_entry **pdns)
+{
+  CURLcode result;
+
+  /* If async resolving is ongoing, this must be set */
+  if(!async)
+    return CURLE_FAILED_INIT;
 
 #ifndef CURL_DISABLE_DOH
-  data->conn->bits.doh = FALSE; /* default is not */
+  if(async->doh)
+    result = Curl_doh_take_result(data, async, pdns);
+  else
+#endif
+  result = Curl_async_take_result(data, async, pdns);
+
+  if(result == CURLE_AGAIN) {
+    CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, "
+                 "ongoing=%d for %s:%d",
+                 Curl_resolv_query_str(async->dns_queries),
+                 Curl_resolv_query_str(async->dns_responses),
+                 async->queries_ongoing, async->hostname, async->port);
+    result = CURLE_OK;
+  }
+  else if(result) {
+    result = Curl_async_failed(data, async, NULL);
+  }
+  else {
+    CURL_TRC_DNS(data, "resolve complete for %s:%u",
+                 async->hostname, async->port);
+    DEBUGASSERT(*pdns);
+  }
+
+  return result;
+}
+
+timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data,
+                                  uint32_t resolv_id)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  if(!async)
+    return CURL_TIMEOUT_RESOLVE_MS;
+  return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start);
+}
+
+bool Curl_resolv_has_answers(struct Curl_easy *data,
+                             uint32_t resolv_id, uint8_t dns_queries)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  uint8_t check_queries;
+  /* a no longer existing/running resolve has all answers. */
+  if(!async || async->done)
+    return TRUE;
+  /* Relevant are only queries undertaken. Others are considered answered. */
+  check_queries = (dns_queries & async->dns_queries);
+  if((check_queries & async->dns_responses) != check_queries) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data,
+                                               uint32_t resolv_id,
+                                               int ai_family,
+                                               unsigned int index)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  (void)index;
+  if(!async)
+    return NULL;
+  if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A))
+    return NULL;
+#ifdef USE_IPV6
+  if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA))
+    return NULL;
+#endif
+  return Curl_async_get_ai(data, async, ai_family, index);
+}
+
+
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *
+Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  if(!async)
+    return NULL;
+  return Curl_async_get_https(data, async);
+}
+
+bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
+  if(!async)
+    return TRUE;
+  return Curl_async_knows_https(data, async);
+}
+
+#endif /* USE_HTTPSRR */
+
+#endif /* USE_CURL_ASYNC */
+
+static CURLcode hostip_resolv_start(struct Curl_easy *data,
+                                    uint8_t dns_queries,
+                                    const char *hostname,
+                                    uint16_t port,
+                                    uint8_t transport,
+                                    bool for_proxy,
+                                    timediff_t timeout_ms,
+                                    bool allowDOH,
+                                    uint32_t *presolv_id,
+                                    struct Curl_dns_entry **pdns)
+{
+#ifdef USE_CURL_ASYNC
+  struct Curl_resolv_async *async = NULL;
+#endif
+  struct Curl_addrinfo *addr = NULL;
+  size_t hostname_len;
+  CURLcode result = CURLE_OK;
+
+  (void)timeout_ms; /* not in all ifdefs */
+  *presolv_id = 0;
+  *pdns = NULL;
+
+  /* Check for "known" things to resolve ourselves. */
+#ifndef USE_RESOLVE_ON_IPS
+  if(Curl_is_ipaddr(hostname)) {
+    /* test655 verifies that the announce is done, even though there
+     * is no real resolving. So, keep doing this. */
+    result = Curl_resolv_announce_start(data, NULL);
+    if(result)
+      goto out;
+    /* shortcut literal IP addresses, if we are not told to resolve them. */
+    result = Curl_str2addr(hostname, port, &addr);
+    goto out;
+  }
+#endif
+
+  hostname_len = strlen(hostname);
+  if(curl_strequal(hostname, "localhost") ||
+     curl_strequal(hostname, "localhost.") ||
+     tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
+     tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
+    result = Curl_resolv_announce_start(data, NULL);
+    if(result)
+      goto out;
+    addr = get_localhost(port, hostname);
+    if(!addr)
+      result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+#ifndef CURL_DISABLE_DOH
+  if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
+    result = Curl_resolv_announce_start(data, NULL);
+    if(result)
+      goto out;
+    if(!async) {
+      async = hostip_async_new(data, dns_queries, hostname, port,
+                               transport, for_proxy, timeout_ms);
+      if(!async) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+    }
+    result = Curl_doh(data, async);
+    goto out;
+  }
 #else
   (void)allowDOH;
 #endif
-  DEBUGASSERT(dnscache);
-  if(!dnscache) {
-    result = CURLE_BAD_FUNCTION_ARGUMENT;
-    goto error;
+
+  /* Can we provide the requested IP specifics in resolving? */
+  if(!can_resolve_dns_queries(data, dns_queries)) {
+    result = RESOLV_FAIL(for_proxy);
+    goto out;
   }
 
+#ifdef CURLRES_ASYNCH
+  (void)addr;
+  if(!async) {
+    async = hostip_async_new(data, dns_queries, hostname, port,
+                             transport, for_proxy, timeout_ms);
+    if(!async) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
+  }
+  result = Curl_async_getaddrinfo(data, async);
+  if(result == CURLE_AGAIN) {
+    /* the answer might be there already. Check. */
+    CURLcode r2 = hostip_resolv_take_result(data, async, pdns);
+    if(r2)
+      result = r2;
+    else if(*pdns)
+      result = CURLE_OK;
+  }
+#else
+  result = Curl_resolv_announce_start(data, NULL);
+  if(result)
+    goto out;
+  addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport);
+  if(!addr)
+    result = RESOLV_FAIL(for_proxy);
+#endif
+
+out:
+  if(!result) {
+    if(addr) {
+      /* we got a response, create a dns entry, add to cache, return */
+      DEBUGASSERT(!*pdns);
+      *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port);
+      if(!*pdns)
+        result = CURLE_OUT_OF_MEMORY;
+    }
+    else if(!*pdns)
+      result = CURLE_AGAIN;
+  }
+  else if(*pdns)
+    Curl_dns_entry_unlink(data, pdns);
+  else if(addr)
+    Curl_freeaddrinfo(addr);
+
+#ifdef USE_CURL_ASYNC
+  if(async) {
+    if(result == CURLE_AGAIN) { /* still need it, link, return id. */
+      *presolv_id = async->id;
+      async->next = data->state.async;
+      data->state.async = async;
+    }
+    else {
+      Curl_async_destroy(data, async);
+    }
+  }
+#endif
+  return result;
+}
+
+static CURLcode hostip_resolv(struct Curl_easy *data,
+                              uint8_t dns_queries,
+                              const char *hostname,
+                              uint16_t port,
+                              uint8_t transport,
+                              bool for_proxy,
+                              timediff_t timeout_ms,
+                              bool allowDOH,
+                              uint32_t *presolv_id,
+                              struct Curl_dns_entry **pdns)
+{
+  size_t hostname_len;
+  CURLcode result = RESOLV_FAIL(for_proxy);
+  bool cache_dns = FALSE;
+
+  (void)timeout_ms; /* not used in all ifdefs */
+  *presolv_id = 0;
+  *pdns = NULL;
+
+#ifdef CURL_DISABLE_DOH
+  (void)allowDOH;
+#endif
+
   /* We should intentionally error and not resolve .onion TLDs */
   hostname_len = strlen(hostname);
   DEBUGASSERT(hostname_len);
@@ -891,152 +700,88 @@
      (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
       curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
     failf(data, "Not resolving .onion address (RFC 7686)");
-    goto error;
+    goto out;
   }
 
+#ifdef DEBUGBUILD
+  CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)",
+               hostname, port, Curl_resolv_query_str(dns_queries));
+  if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) &&
+     getenv("CURL_DBG_RESOLV_FAIL_IPV6")) {
+    infof(data, "DEBUG fail ipv6 resolve");
+    result = hostip_resolv_failed(data, hostname, for_proxy);
+    goto out;
+  }
+#endif
   /* Let's check our DNS cache first */
-  dnscache_lock(data, dnscache);
-  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
-  if(dns)
-    dns->refcount++; /* we pass out the reference. */
-  dnscache_unlock(data, dnscache);
-  if(dns) {
+  result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns);
+  if(*pdns) {
     infof(data, "Hostname %s was found in DNS cache", hostname);
     result = CURLE_OK;
-    goto out;
   }
-
-  /* No luck, we need to resolve hostname. Notify user callback. */
-  if(data->set.resolver_start) {
-    void *resolver = NULL;
-    int st;
-#ifdef CURLRES_ASYNCH
-    result = Curl_async_get_impl(data, &resolver);
-    if(result)
-      goto error;
-#endif
-    Curl_set_in_callback(data, TRUE);
-    st = data->set.resolver_start(resolver, NULL,
-                                  data->set.resolver_start_client);
-    Curl_set_in_callback(data, FALSE);
-    if(st) {
-      result = CURLE_ABORTED_BY_CALLBACK;
-      goto error;
-    }
+  else if(result) {
+    infof(data, "Negative DNS entry");
+    result = hostip_resolv_failed(data, hostname, for_proxy);
   }
-
-  if(Curl_is_ipaddr(hostname)) {
-#ifndef USE_RESOLVE_ON_IPS
-    /* shortcut literal IP addresses, if we are not told to resolve them. */
-    result = Curl_str2addr(hostname, port, &addr);
-    if(result)
-      goto error;
-    goto out;
-#endif
-  }
-
-  if(curl_strequal(hostname, "localhost") ||
-     curl_strequal(hostname, "localhost.") ||
-     tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
-     tailmatch(hostname, hostname_len, STRCONST(".localhost."))) {
-    addr = get_localhost(port, hostname);
-    result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY;
-  }
-#ifndef CURL_DISABLE_DOH
-  else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) {
-    result = Curl_doh(data, hostname, port, ip_version);
-    respwait = TRUE;
-  }
-#endif
   else {
-    /* Can we provide the requested IP specifics in resolving? */
-    if(!can_resolve_ip_version(data, ip_version)) {
-      result = CURLE_COULDNT_RESOLVE_HOST;
-      goto error;
-    }
-
-#ifdef CURLRES_ASYNCH
-    result = Curl_async_getaddrinfo(data, hostname, port, ip_version);
-    respwait = TRUE;
-#else
-    respwait = FALSE; /* no async waiting here */
-    addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
-    if(addr)
-      result = CURLE_OK;
-#endif
+    /* No luck, we need to start resolving. */
+    cache_dns = TRUE;
+    result = hostip_resolv_start(data, dns_queries, hostname, port,
+                                 transport, for_proxy, timeout_ms, allowDOH,
+                                 presolv_id, pdns);
   }
 
 out:
-  /* We either have found a `dns` or looked up the `addr` or `respwait` is set
-   * for an async operation. Everything else is a failure to resolve. */
-  if(result)
-    ;
-  else if(dns) {
-    if(!dns->addr) {
-      infof(data, "Negative DNS entry");
-      dns->refcount--;
-      return CURLE_COULDNT_RESOLVE_HOST;
+  if(result && (result != CURLE_AGAIN)) {
+    Curl_dns_entry_unlink(data, pdns);
+    if(IS_RESOLV_FAIL(result)) {
+      if(cache_dns)
+        Curl_dnscache_add_negative(data, dns_queries, hostname, port);
+      failf(data, "Could not resolve: %s:%u", hostname, port);
     }
-    *entry = dns;
-    return CURLE_OK;
-  }
-  else if(addr) {
-    /* we got a response, create a dns entry, add to cache, return */
-    dns = Curl_dnscache_mk_entry(data, &addr, hostname, 0, port, FALSE);
-    if(!dns || Curl_dnscache_add(data, dns)) {
-      /* this is OOM or similar, do not store such negative resolves */
-      result = CURLE_OUT_OF_MEMORY;
-      goto error;
+    else {
+      failf(data, "Error %d resolving %s:%u", result, hostname, port);
     }
-    show_resolve_info(data, dns);
-    *entry = dns;
-    return CURLE_OK;
   }
-  else if(respwait) {
-#ifdef USE_CURL_ASYNC
-    if(!Curl_resolv_check(data, &dns)) {
-      *entry = dns;
-      return dns ? CURLE_OK : CURLE_AGAIN;
-    }
-#endif
-    result = CURLE_COULDNT_RESOLVE_HOST;
+  else if(cache_dns && *pdns) {
+    result = Curl_dnscache_add(data, *pdns);
+    if(result)
+      Curl_dns_entry_unlink(data, pdns);
   }
-error:
-  if(dns)
-    Curl_resolv_unlink(data, &dns);
-  Curl_async_shutdown(data);
-  if(result == CURLE_COULDNT_RESOLVE_HOST)
-    store_negative_resolve(data, hostname, port);
-  DEBUGASSERT(result);
+
   return result;
 }
 
 CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+                              uint8_t dns_queries,
                               const char *hostname,
-                              int port,
-                              int ip_version,
-                              struct Curl_dns_entry **entry)
+                              uint16_t port,
+                              uint8_t transport,
+                              struct Curl_dns_entry **pdns)
 {
   CURLcode result;
+  uint32_t resolv_id;
   DEBUGASSERT(hostname && *hostname);
-  *entry = NULL;
-  result = Curl_resolv(data, hostname, port, ip_version, FALSE, entry);
+  *pdns = NULL;
+  /* We cannot do a blocking resolve using DoH currently */
+  result = hostip_resolv(data, dns_queries,
+                         hostname, port, transport, FALSE, 0, FALSE,
+                         &resolv_id, pdns);
   switch(result) {
   case CURLE_OK:
-    DEBUGASSERT(*entry);
-    return CURLE_OK;
+    DEBUGASSERT(*pdns);
+    break;
+#ifdef USE_CURL_ASYNC
   case CURLE_AGAIN:
-    DEBUGASSERT(!*entry);
-    result = Curl_async_await(data, entry);
-    if(result || !*entry) {
-      /* close the connection, since we cannot return failure here without
-         cleaning up this connection properly. */
-      connclose(data->conn, "async resolve failed");
-    }
-    return result;
+    DEBUGASSERT(!*pdns);
+    result = Curl_async_await(data, resolv_id, pdns);
+    Curl_resolv_destroy(data, resolv_id);
+    break;
+#endif
   default:
-    return result;
+    break;
   }
+  return result;
 }
 
 #ifdef USE_ALARM_TIMEOUT
@@ -1052,35 +797,18 @@
 }
 #endif /* USE_ALARM_TIMEOUT */
 
-/*
- * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
- * timeout. This function might return immediately if we are using asynch
- * resolves. See the return codes.
- *
- * The cache entry we return will get its 'inuse' counter increased when this
- * function is used. You MUST call Curl_resolv_unlink() later (when you are
- * done using this struct) to decrease the reference counter again.
- *
- * If built with a synchronous resolver and use of signals is not
- * disabled by the application, then a nonzero timeout will cause a
- * timeout after the specified number of milliseconds. Otherwise, timeout
- * is ignored.
- *
- * Return codes:
- * CURLE_OK = success, *entry set to non-NULL
- * CURLE_AGAIN = resolving in progress, *entry == NULL
- * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
- * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
- */
-
-CURLcode Curl_resolv_timeout(struct Curl_easy *data,
-                             const char *hostname,
-                             int port,
-                             int ip_version,
-                             struct Curl_dns_entry **entry,
-                             timediff_t timeoutms)
-{
 #ifdef USE_ALARM_TIMEOUT
+
+static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
+                                     uint8_t dns_queries,
+                                     const char *hostname,
+                                     uint16_t port,
+                                     uint8_t transport,
+                                     bool for_proxy,
+                                     timediff_t timeout_ms,
+                                     uint32_t *presolv_id,
+                                     struct Curl_dns_entry **entry)
+{
 #ifdef HAVE_SIGACTION
   struct sigaction keep_sigact; /* store the old struct here */
   volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
@@ -1092,32 +820,17 @@
 #endif /* HAVE_SIGACTION */
   volatile long timeout;
   volatile unsigned int prev_alarm = 0;
-#endif /* USE_ALARM_TIMEOUT */
   CURLcode result;
 
   DEBUGASSERT(hostname && *hostname);
-  *entry = NULL;
-
-  if(timeoutms < 0)
-    /* got an already expired timeout */
-    return CURLE_OPERATION_TIMEDOUT;
-
-#ifdef USE_ALARM_TIMEOUT
-  if(data->set.no_signal)
-    /* Ignore the timeout when signals are disabled */
-    timeout = 0;
-  else
-    timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
-
-  if(!timeout
+  DEBUGASSERT(timeout_ms > 0);
+  DEBUGASSERT(!data->set.no_signal);
 #ifndef CURL_DISABLE_DOH
-     || data->set.doh
+  DEBUGASSERT(!data->set.doh);
 #endif
-    )
-    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve
-       done using DoH */
-    return Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
 
+  *entry = NULL;
+  timeout = (timeout_ms > LONG_MAX) ? LONG_MAX : (long)timeout_ms;
   if(timeout < 1000) {
     /* The alarm() function only provides integer second resolution, so if
        we want to wait less than one second we must bail out already now. */
@@ -1168,23 +881,12 @@
     prev_alarm = alarm(curlx_sltoui(timeout / 1000L));
   }
 
-#else /* !USE_ALARM_TIMEOUT */
-#ifndef CURLRES_ASYNCH
-  if(timeoutms)
-    infof(data, "timeout on name lookup is not supported");
-#else
-  (void)timeoutms;
-#endif
-#endif /* USE_ALARM_TIMEOUT */
-
   /* Perform the actual name resolution. This might be interrupted by an
-   * alarm if it takes too long.
-   */
-  result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
+   * alarm if it takes too long. */
+  result = hostip_resolv(data, dns_queries, hostname, port, transport,
+                         for_proxy, timeout_ms, FALSE, presolv_id, entry);
 
-#ifdef USE_ALARM_TIMEOUT
 clean_up:
-
   if(!prev_alarm)
     /* deactivate a possibly active alarm before uninstalling the handler */
     alarm(0);
@@ -1227,364 +929,202 @@
     else
       alarm((unsigned int)alarm_set);
   }
-#endif /* USE_ALARM_TIMEOUT */
 
   return result;
 }
 
+#endif /* USE_ALARM_TIMEOUT */
+
 /*
- * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
- * When the reference count reaches 0, the entry is destroyed. It is important
- * that only one unlink is made for each Curl_resolv() call.
+ * Curl_resolv() is the main name resolve function within libcurl. It resolves
+ * a name and returns a pointer to the entry in the 'entry' argument. This
+ * function might return immediately if we are using asynch resolves. See the
+ * return codes.
  *
- * May be called with 'data' == NULL for global cache.
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_dns_entry_unlink() later (when you are
+ * done using this struct) to decrease the reference counter again.
+ *
+ * If built with a synchronous resolver and use of signals is not
+ * disabled by the application, then a nonzero timeout will cause a
+ * timeout after the specified number of milliseconds. Otherwise, timeout
+ * is ignored.
+ *
+ * Return codes:
+ * CURLE_OK = success, *pdns set to non-NULL
+ * CURLE_AGAIN = resolving in progress, *pdns == NULL
+ * any other CURLcode error, *pdns == NULL
  */
-void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
+CURLcode Curl_resolv(struct Curl_easy *data,
+                     uint8_t dns_queries,
+                     const char *hostname,
+                     uint16_t port,
+                     uint8_t transport,
+                     bool for_proxy,
+                     timediff_t timeout_ms,
+                     uint32_t *presolv_id,
+                     struct Curl_dns_entry **pdns)
 {
-  if(*pdns) {
-    struct Curl_dnscache *dnscache = dnscache_get(data);
-    struct Curl_dns_entry *dns = *pdns;
-    *pdns = NULL;
-    dnscache_lock(data, dnscache);
-    dns->refcount--;
-    if(dns->refcount == 0)
-      dnscache_entry_free(dns);
-    dnscache_unlock(data, dnscache);
+  DEBUGASSERT(hostname && *hostname);
+  *presolv_id = 0;
+  *pdns = NULL;
+
+  if(timeout_ms < 0)
+    /* got an already expired timeout */
+    return CURLE_OPERATION_TIMEDOUT;
+  else if(!timeout_ms)
+    timeout_ms = CURL_TIMEOUT_RESOLVE_MS;
+
+#ifdef USE_ALARM_TIMEOUT
+  if(timeout_ms && data->set.no_signal) {
+    /* Cannot use ALARM when signals are disabled */
+    timeout_ms = 0;
   }
-}
+  if(timeout_ms && !Curl_doh_wanted(data)) {
+    return resolv_alarm_timeout(data, dns_queries, hostname, port, transport,
+                                for_proxy, timeout_ms, presolv_id, pdns);
+  }
+#endif /* !USE_ALARM_TIMEOUT */
 
-static void dnscache_entry_dtor(void *entry)
-{
-  struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry;
-  DEBUGASSERT(dns && (dns->refcount > 0));
-  dns->refcount--;
-  if(dns->refcount == 0)
-    dnscache_entry_free(dns);
-}
-
-/*
- * Curl_dnscache_init() inits a new DNS cache.
- */
-void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
-{
-  Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
-                 dnscache_entry_dtor);
-}
-
-void Curl_dnscache_destroy(struct Curl_dnscache *dns)
-{
-  Curl_hash_destroy(&dns->entries);
-}
-
-CURLcode Curl_loadhostpairs(struct Curl_easy *data)
-{
-  struct Curl_dnscache *dnscache = dnscache_get(data);
-  struct curl_slist *hostp;
-
-  if(!dnscache)
-    return CURLE_FAILED_INIT;
-
-  /* Default is no wildcard found */
-  data->state.wildcard_resolve = FALSE;
-
-  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
-    char entry_id[MAX_HOSTCACHE_LEN];
-    const char *host = hostp->data;
-    struct Curl_str source;
-    if(!host)
-      continue;
-    if(*host == '-') {
-      curl_off_t num = 0;
-      size_t entry_len;
-      host++;
-      if(!curlx_str_single(&host, '[')) {
-        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
-           curlx_str_single(&host, ']') ||
-           curlx_str_single(&host, ':'))
-          continue;
-      }
-      else {
-        if(curlx_str_until(&host, &source, 4096, ':') ||
-           curlx_str_single(&host, ':')) {
-          continue;
-        }
-      }
-
-      if(!curlx_str_number(&host, &num, 0xffff)) {
-        /* Create an entry id, based upon the hostname and port */
-        entry_len = create_dnscache_id(curlx_str(&source),
-                                       curlx_strlen(&source), (int)num,
-                                       entry_id, sizeof(entry_id));
-        dnscache_lock(data, dnscache);
-        /* delete entry, ignore if it did not exist */
-        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
-        dnscache_unlock(data, dnscache);
-      }
-    }
-    else {
-      struct Curl_dns_entry *dns;
-      struct Curl_addrinfo *head = NULL, *tail = NULL;
-      size_t entry_len;
-      char address[64];
-      curl_off_t port = 0;
-      bool permanent = TRUE;
-      bool error = TRUE;
-      VERBOSE(const char *addresses = NULL);
-
-      if(*host == '+') {
-        host++;
-        permanent = FALSE;
-      }
-      if(!curlx_str_single(&host, '[')) {
-        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
-           curlx_str_single(&host, ']'))
-          continue;
-      }
-      else {
-        if(curlx_str_until(&host, &source, 4096, ':'))
-          continue;
-      }
-      if(curlx_str_single(&host, ':') ||
-         curlx_str_number(&host, &port, 0xffff) ||
-         curlx_str_single(&host, ':'))
-        goto err;
-
-      VERBOSE(addresses = host);
-
-      /* start the address section */
-      while(*host) {
-        struct Curl_str target;
-        struct Curl_addrinfo *ai;
-        CURLcode result;
-
-        if(!curlx_str_single(&host, '[')) {
-          if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
-             curlx_str_single(&host, ']'))
-            goto err;
-        }
-        else {
-          if(curlx_str_until(&host, &target, 4096, ',')) {
-            if(curlx_str_single(&host, ','))
-              goto err;
-            /* survive nothing but a comma */
-            continue;
-          }
-        }
-#ifndef USE_IPV6
-        if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) {
-          infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.",
-                (int)curlx_strlen(&target), curlx_str(&target));
-          if(curlx_str_single(&host, ','))
-            goto err;
-          continue;
-        }
+#ifndef CURLRES_ASYNCH
+  if(timeout_ms)
+    infof(data, "timeout on name lookup is not supported");
 #endif
 
-        if(curlx_strlen(&target) >= sizeof(address))
-          goto err;
-
-        memcpy(address, curlx_str(&target), curlx_strlen(&target));
-        address[curlx_strlen(&target)] = '\0';
-
-        result = Curl_str2addr(address, (int)port, &ai);
-        if(result) {
-          infof(data, "Resolve address '%s' found illegal", address);
-          goto err;
-        }
-
-        if(tail) {
-          tail->ai_next = ai;
-          tail = tail->ai_next;
-        }
-        else {
-          head = tail = ai;
-        }
-        if(curlx_str_single(&host, ','))
-          break;
-      }
-
-      if(!head)
-        goto err;
-
-      error = FALSE;
-err:
-      if(error) {
-        failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data);
-        Curl_freeaddrinfo(head);
-        return CURLE_SETOPT_OPTION_SYNTAX;
-      }
-
-      /* Create an entry id, based upon the hostname and port */
-      entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
-                                     (int)port,
-                                     entry_id, sizeof(entry_id));
-
-      dnscache_lock(data, dnscache);
-
-      /* See if it is already in our dns cache */
-      dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
-
-      if(dns) {
-        infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
-              " - old addresses discarded", (int)curlx_strlen(&source),
-              curlx_str(&source), port);
-        /* delete old entry, there are two reasons for this
-         1. old entry may have different addresses.
-         2. even if entry with correct addresses is already in the cache,
-            but if it is close to expire, then by the time next http
-            request is made, it can get expired and pruned because old
-            entry is not necessarily marked as permanent.
-         3. when adding a non-permanent entry, we want it to remove and
-            replace an existing permanent entry.
-         4. when adding a non-permanent entry, we want it to get a "fresh"
-            timeout that starts _now_. */
-
-        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
-      }
-
-      /* put this new host in the cache */
-      dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source),
-                              curlx_strlen(&source), (int)port, permanent);
-      if(dns)
-        /* release the returned reference; the cache itself will keep the
-         * entry alive: */
-        dns->refcount--;
-
-      dnscache_unlock(data, dnscache);
-
-      if(!dns)
-        return CURLE_OUT_OF_MEMORY;
-
-      infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
-            (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
-            permanent ? "" : " (non-permanent)");
-
-      /* Wildcard hostname */
-      if(curlx_str_casecompare(&source, "*")) {
-        infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
-              port);
-        data->state.wildcard_resolve = TRUE;
-      }
-    }
-  }
-  data->state.resolve = NULL; /* dealt with now */
-
-  return CURLE_OK;
+  return hostip_resolv(data, dns_queries, hostname, port, transport,
+                       for_proxy, timeout_ms, TRUE, presolv_id, pdns);
 }
 
 #ifdef USE_CURL_ASYNC
-CURLcode Curl_resolv_check(struct Curl_easy *data,
-                           struct Curl_dns_entry **dns)
+
+struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
+                                         uint32_t resolv_id)
 {
+  struct Curl_resolv_async *async = data->state.async;
+  for(; async; async = async->next) {
+    if(async->id == resolv_id)
+      return async;
+  }
+  return NULL;
+}
+
+CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
+                                 struct Curl_dns_entry **pdns)
+{
+  struct Curl_resolv_async *async = Curl_async_get(data, resolv_id);
   CURLcode result;
 
   /* If async resolving is ongoing, this must be set */
-  if(!data->state.async.hostname)
+  if(!async)
     return CURLE_FAILED_INIT;
 
   /* check if we have the name resolved by now (from someone else) */
-  *dns = Curl_dnscache_get(data, data->state.async.hostname,
-                           data->state.async.port,
-                           data->state.async.ip_version);
-  if(*dns) {
+  result = Curl_dnscache_get(data, async->dns_queries,
+                             async->hostname, async->port, pdns);
+  if(*pdns) {
     /* Tell a possibly async resolver we no longer need the results. */
-    infof(data, "Hostname '%s' was found in DNS cache",
-          data->state.async.hostname);
-    Curl_async_shutdown(data);
-    data->state.async.dns = *dns;
-    data->state.async.done = TRUE;
+    infof(data, "Hostname '%s' was found in DNS cache", async->hostname);
+    Curl_async_shutdown(data, async);
     return CURLE_OK;
   }
-
-#ifndef CURL_DISABLE_DOH
-  if(data->conn->bits.doh) {
-    result = Curl_doh_is_resolved(data, dns);
-    if(result)
-      Curl_resolver_error(data, NULL);
+  else if(result) {
+    Curl_async_shutdown(data, async);
+    return Curl_async_failed(data, async, NULL);
   }
-  else
-#endif
-  result = Curl_async_is_resolved(data, dns);
-  if(*dns)
-    show_resolve_info(data, *dns);
-  if((result == CURLE_COULDNT_RESOLVE_HOST) ||
-     (result == CURLE_COULDNT_RESOLVE_PROXY))
-    store_negative_resolve(data, data->state.async.hostname,
-                           data->state.async.port);
+
+  result = hostip_resolv_take_result(data, async, pdns);
+
+  if(*pdns) {
+    /* Add to cache */
+    result = Curl_dnscache_add(data, *pdns);
+    if(result)
+      Curl_dns_entry_unlink(data, pdns);
+  }
+  else if(IS_RESOLV_FAIL(result)) {
+    Curl_dnscache_add_negative(data, async->dns_queries,
+                               async->hostname, async->port);
+    failf(data, "Could not resolve: %s:%u", async->hostname, async->port);
+  }
+  else if(result) {
+    failf(data, "Error %d resolving %s:%u",
+          result, async->hostname, async->port);
+  }
   return result;
 }
-#endif
 
 CURLcode Curl_resolv_pollset(struct Curl_easy *data,
                              struct easy_pollset *ps)
 {
-#ifdef CURLRES_ASYNCH
-#ifndef CURL_DISABLE_DOH
-  if(data->conn->bits.doh)
-    /* nothing to wait for during DoH resolve, those handles have their own
-       sockets */
-    return CURLE_OK;
-#endif
-  return Curl_async_pollset(data, ps);
-#else
-  (void)data;
+  struct Curl_resolv_async *async = data->state.async;
+  CURLcode result = CURLE_OK;
+
   (void)ps;
-  return CURLE_OK;
+  for(; async && !result; async = async->next) {
+#ifndef CURL_DISABLE_DOH
+    if(async->doh) /* DoH has nothing for the pollset */
+      continue;
 #endif
-}
-
-/* Call this function after Curl_connect() has returned async=TRUE and
-   then a successful name resolve has been received.
-
-   Note: this function disconnects and frees the conn data in case of
-   resolve failure */
-CURLcode Curl_once_resolved(struct Curl_easy *data,
-                            struct Curl_dns_entry *dns,
-                            bool *protocol_done)
-{
-  CURLcode result;
-  struct connectdata *conn = data->conn;
-
-#ifdef USE_CURL_ASYNC
-  if(data->state.async.dns) {
-    DEBUGASSERT(data->state.async.dns == dns);
-    data->state.async.dns = NULL;
-  }
-#endif
-
-  result = Curl_setup_conn(data, dns, protocol_done);
-
-  if(result) {
-    Curl_detach_connection(data);
-    Curl_conn_terminate(data, conn, TRUE);
+    result = Curl_async_pollset(data, async, ps);
   }
   return result;
 }
 
-/*
- * Curl_resolver_error() calls failf() with the appropriate message after a
- * resolve error
- */
-
-#ifdef USE_CURL_ASYNC
-CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail)
+void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id)
 {
-  struct connectdata *conn = data->conn;
-  const char *host_or_proxy = "host";
-  const char *name = conn->host.dispname;
-  CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
+  struct Curl_resolv_async **panchor = &data->state.async;
 
-#ifndef CURL_DISABLE_PROXY
-  if(conn->bits.proxy) {
-    host_or_proxy = "proxy";
-    result = CURLE_COULDNT_RESOLVE_PROXY;
-    name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
-      conn->http_proxy.host.dispname;
+  for(; *panchor; panchor = &(*panchor)->next) {
+    struct Curl_resolv_async *async = *panchor;
+    if(async->id == resolv_id) {
+      *panchor = async->next;
+      Curl_async_destroy(data, async);
+      break;
+    }
   }
-#endif
-
-  failf(data, "Could not resolve %s: %s%s%s%s", host_or_proxy, name,
-        detail ? " (" : "", detail ? detail : "", detail ? ")" : "");
-  return result;
 }
+
+void Curl_resolv_shutdown_all(struct Curl_easy *data)
+{
+  struct Curl_resolv_async *async = data->state.async;
+  for(; async; async = async->next) {
+    Curl_async_shutdown(data, async);
+  }
+}
+
+void Curl_resolv_destroy_all(struct Curl_easy *data)
+{
+  while(data->state.async) {
+    struct Curl_resolv_async *async = data->state.async;
+    data->state.async = async->next;
+    Curl_async_destroy(data, async);
+  }
+}
+
 #endif /* USE_CURL_ASYNC */
+
+#ifdef USE_UNIX_SOCKETS
+CURLcode Curl_resolv_unix(struct Curl_easy *data,
+                          const char *unix_path,
+                          bool abstract_path,
+                          struct Curl_dns_entry **pdns)
+{
+  struct Curl_addrinfo *addr;
+  CURLcode result;
+
+  DEBUGASSERT(unix_path);
+  *pdns = NULL;
+
+  result = Curl_unix2addr(unix_path, abstract_path, &addr);
+  if(result) {
+    if(result == CURLE_TOO_LARGE) {
+      /* Long paths are not supported for now */
+      failf(data, "Unix socket path too long: '%s'", unix_path);
+      result = CURLE_COULDNT_RESOLVE_HOST;
+    }
+    return result;
+  }
+
+  *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0);
+  return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+#endif /* USE_UNIX_SOCKETS */
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index 6c7f129..780fb4d 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -35,8 +35,7 @@
  */
 #define CURL_HOSTENT_SIZE 9000
 
-#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this
-                                    many seconds for a name resolve */
+#define CURL_TIMEOUT_RESOLVE_MS (300 * 1000)
 
 struct addrinfo;
 struct hostent;
@@ -45,6 +44,22 @@
 struct easy_pollset;
 struct Curl_https_rrinfo;
 struct Curl_multi;
+struct Curl_dns_entry;
+
+/* DNS query types */
+#define CURL_DNSQ_A           (1U << 0)
+#define CURL_DNSQ_AAAA        (1U << 1)
+#define CURL_DNSQ_HTTPS       (1U << 2)
+
+#define CURL_DNSQ_ALL         (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS)
+#define CURL_DNSQ_IP(x)       (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA))
+
+#ifdef CURLVERBOSE
+const char *Curl_resolv_query_str(uint8_t dns_queries);
+#endif
+
+/* Return CURL_DNSQ_* bits for the transfer and ip_version. */
+uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version);
 
 enum alpnid {
   ALPN_none = 0,
@@ -53,151 +68,108 @@
   ALPN_h3 = CURLALTSVC_H3
 };
 
-struct Curl_dns_entry {
-  struct Curl_addrinfo *addr;
-#ifdef USE_HTTPSRR
-  struct Curl_https_rrinfo *hinfo;
-#endif
-  /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
-  struct curltime timestamp;
-  /* reference counter, entry is freed on reaching 0 */
-  size_t refcount;
-  /* hostname port number that resolved to addr. */
-  int hostport;
-  /* hostname that resolved to addr. may be NULL (Unix domain sockets). */
-  char hostname[1];
-};
-
-struct Curl_dnscache {
-  struct Curl_hash entries;
-};
-
 bool Curl_host_is_ipnum(const char *hostname);
 
-/*
- * Curl_resolv() returns an entry with the info for the specified host
- * and port.
- *
- * The returned data *MUST* be "released" with Curl_resolv_unlink() after
- * use, or we will leak memory!
- */
-CURLcode Curl_resolv(struct Curl_easy *data,
-                     const char *hostname,
-                     int port,
-                     int ip_version,
-                     bool allowDOH,
-                     struct Curl_dns_entry **entry);
-
-CURLcode Curl_resolv_blocking(struct Curl_easy *data,
-                              const char *hostname,
-                              int port,
-                              int ip_version,
-                              struct Curl_dns_entry **entry);
-
-CURLcode Curl_resolv_timeout(struct Curl_easy *data,
-                             const char *hostname, int port,
-                             int ip_version,
-                             struct Curl_dns_entry **entry,
-                             timediff_t timeoutms);
-
 #ifdef USE_IPV6
-
 /* probe if it seems to work */
 CURLcode Curl_probeipv6(struct Curl_multi *multi);
-/*
- * Curl_ipv6works() returns TRUE if IPv6 seems to work.
- */
-bool Curl_ipv6works(struct Curl_easy *data);
 #else
 #define Curl_probeipv6(x) CURLE_OK
-#define Curl_ipv6works(x) FALSE
 #endif
 
-/* unlink a dns entry, potentially shared with a cache */
-void Curl_resolv_unlink(struct Curl_easy *data,
-                        struct Curl_dns_entry **pdns);
-
-/* init a new dns cache */
-void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size);
-
-void Curl_dnscache_destroy(struct Curl_dnscache *dns);
-
-/* prune old entries from the DNS cache */
-void Curl_dnscache_prune(struct Curl_easy *data);
-
-/* clear the DNS cache */
-void Curl_dnscache_clear(struct Curl_easy *data);
-
 /* IPv4 thread-safe resolve function used for synch and asynch builds */
-struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
-
-CURLcode Curl_once_resolved(struct Curl_easy *data,
-                            struct Curl_dns_entry *dns,
-                            bool *protocol_done);
+struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port);
 
 /*
  * Curl_printable_address() returns a printable version of the 1st address
- * given in the 'ip' argument. The result will be stored in the buf that is
+ * given in the 'ai' argument. The result will be stored in the buf that is
  * bufsize bytes big.
  */
 void Curl_printable_address(const struct Curl_addrinfo *ai,
                             char *buf, size_t bufsize);
 
-/*
- * Make a `Curl_dns_entry`.
- * Creates a dnscache entry *without* adding it to a dnscache. This allows
- * further modifications of the entry *before* then adding it to a cache.
- *
- * The entry is created with a reference count of 1.
- * Use `Curl_resolv_unlink()` to release your hold on it.
- *
- * The call takes ownership of `addr`, even in case of failure, and always
- * clears `*paddr`. It makes a copy of `hostname`.
- *
- * Returns entry or NULL on OOM.
+/* Start DNS resolving for the given parameters. Returns
+ * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked).
+    *          `*presolv_id` is 0.
+ * - CURLE_AGAIN: resolve is asynchronous and not finished yet.
+ *             `presolv_id` is the identifier for querying results later.
+ * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0.
  */
-struct Curl_dns_entry *
-Curl_dnscache_mk_entry(struct Curl_easy *data,
-                       struct Curl_addrinfo **paddr,
-                       const char *hostname,
-                       size_t hostlen, /* length or zero */
-                       int port,
-                       bool permanent);
+CURLcode Curl_resolv(struct Curl_easy *data,
+                     uint8_t dns_queries,
+                     const char *hostname,
+                     uint16_t port,
+                     uint8_t transport,
+                     bool for_proxy,
+                     timediff_t timeout_ms,
+                     uint32_t *presolv_id,
+                     struct Curl_dns_entry **pdns);
 
-/*
- * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
- *
- * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
- *
- * The returned data *MUST* be "released" with Curl_resolv_unlink() after
- * use, or we will leak memory!
- */
-struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data,
-                                         const char *hostname, int port,
-                                         int ip_version);
+CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+                              uint8_t dns_queries,
+                              const char *hostname,
+                              uint16_t port,
+                              uint8_t transport,
+                              struct Curl_dns_entry **pdns);
 
-/*
- * Curl_dnscache_addr() adds `entry` to the cache, increasing its
- * reference count on success.
- */
-CURLcode Curl_dnscache_add(struct Curl_easy *data,
-                           struct Curl_dns_entry *entry);
-
-/*
- * Populate the cache with specified entries from CURLOPT_RESOLVE.
- */
-CURLcode Curl_loadhostpairs(struct Curl_easy *data);
+/* Announce start of a resolve operation to application callback,
+ * passing the resolver implementation (maybe NULL). */
+CURLcode Curl_resolv_announce_start(struct Curl_easy *data,
+                                    void *resolver);
 
 #ifdef USE_CURL_ASYNC
-CURLcode Curl_resolv_check(struct Curl_easy *data,
-                           struct Curl_dns_entry **dns);
-#else
-#define Curl_resolv_check(x, y) CURLE_NOT_BUILT_IN
-#endif
+
 CURLcode Curl_resolv_pollset(struct Curl_easy *data,
                              struct easy_pollset *ps);
 
-CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail);
+/* Get the `async` struct for the given `resolv_id`, if it exists. */
+struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data,
+                                         uint32_t resolv_id);
+
+/* Shut down all resolves of the given easy handle. */
+void Curl_resolv_shutdown_all(struct Curl_easy *data);
+
+/* Destroy all resolve resources of the given easy handle. */
+void Curl_resolv_destroy_all(struct Curl_easy *data);
+
+CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
+                                 struct Curl_dns_entry **pdns);
+
+void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id);
+
+/* How much time has gone by since start of resolve.
+ * Returns CURL_TIMEOUT_RESOLVE_MS if `resolv_id` is no longer valid. */
+timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data,
+                                  uint32_t resolv_id);
+
+/* Return TRUE if `resolv_id` has answers (positive or negative) to
+ * all queries in `dns_queries`.
+ * Queries not requested are considered answered. */
+bool Curl_resolv_has_answers(struct Curl_easy *data,
+                             uint32_t resolv_id, uint8_t dns_queries);
+
+const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data,
+                                               uint32_t resolv_id,
+                                               int ai_family,
+                                               unsigned int index);
+#ifdef USE_HTTPSRR
+const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data,
+                                                      uint32_t resolv_id);
+bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id);
+#endif /* USE_HTTPSRR */
+
+#else /* !USE_CURL_ASYNC */
+#define Curl_resolv_shutdown_all(x)      Curl_nop_stmt
+#define Curl_resolv_destroy_all(x)       Curl_nop_stmt
+#define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN
+#define Curl_resolv_elapsed_ms(x, y)     CURL_TIMEOUT_RESOLVE_MS
+#define Curl_resolv_has_answers(x, y, z) TRUE
+#define Curl_resolv_get_ai(x, y, z, a)   NULL
+#define Curl_resolv_get_https(x, y)      NULL
+#define Curl_resolv_knows_https(x, y)    TRUE
+#define Curl_resolv_pollset(x, y)        CURLE_OK
+#define Curl_resolv_destroy(x, y)        Curl_nop_stmt
+#endif /* USE_CURL_ASYNC */
 
 #ifdef CURLRES_SYNCH
 /*
@@ -206,10 +178,17 @@
  * support and platform.
  */
 struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            uint8_t dns_queries,
                                             const char *hostname,
-                                            int port,
-                                            int ip_version);
+                                            uint16_t port,
+                                            uint8_t transport);
+#endif
 
+#ifdef USE_UNIX_SOCKETS
+CURLcode Curl_resolv_unix(struct Curl_easy *data,
+                          const char *unix_path,
+                          bool abstract_path,
+                          struct Curl_dns_entry **pdns);
 #endif
 
 #endif /* HEADER_CURL_HOSTIP_H */
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c
index f8cfac1..fb35e39 100644
--- a/Utilities/cmcurl/lib/hostip4.c
+++ b/Utilities/cmcurl/lib/hostip4.c
@@ -68,13 +68,15 @@
  *
  */
 struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            uint8_t dns_queries,
                                             const char *hostname,
-                                            int port,
-                                            int ip_version)
+                                            uint16_t port,
+                                            uint8_t transport)
 {
   struct Curl_addrinfo *ai = NULL;
 
-  (void)ip_version;
+  (void)dns_queries;
+  (void)transport;
 
   ai = Curl_ipv4_resolve_r(hostname, port);
   if(!ai)
@@ -85,7 +87,8 @@
 #endif /* CURLRES_SYNCH */
 #endif /* CURLRES_IPV4 */
 
-#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA)
+#if defined(CURLRES_IPV4) && !defined(USE_RESOLV_ARES) && \
+  !defined(CURLRES_AMIGA)
 
 /*
  * Curl_ipv4_resolve_r() - ipv4 thread-safe resolver function.
@@ -95,7 +98,7 @@
  *
  */
 struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
-                                          int port)
+                                          uint16_t port)
 {
 #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \
   defined(HAVE_GETHOSTBYNAME_R_3)
@@ -277,4 +280,4 @@
 
   return ai;
 }
-#endif /* CURLRES_IPV4 && !CURLRES_ARES && !CURLRES_AMIGA */
+#endif /* CURLRES_IPV4 && !USE_RESOLV_ARES && !CURLRES_AMIGA */
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index 8aa1974..7412f42 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -63,9 +63,10 @@
  * Curl_freeaddrinfo(), nothing else.
  */
 struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            uint8_t dns_queries,
                                             const char *hostname,
-                                            int port,
-                                            int ip_version)
+                                            uint16_t port,
+                                            uint8_t transport)
 {
   struct addrinfo hints;
   struct Curl_addrinfo *res;
@@ -77,14 +78,12 @@
 #endif
   int pf = PF_INET;
 
-  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
-    /* The stack seems to be IPv6-enabled */
+  if(dns_queries & CURL_DNSQ_AAAA)
     pf = PF_UNSPEC;
 
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = pf;
-  hints.ai_socktype =
-    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
+  hints.ai_socktype = (transport == TRNSPRT_TCP) ?
     SOCK_STREAM : SOCK_DGRAM;
 
 #ifndef USE_RESOLVE_ON_IPS
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index 03f51f8..400b442 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -40,7 +40,7 @@
 
 #define MAX_HSTS_LINE    4095
 #define MAX_HSTS_HOSTLEN 2048
-#define MAX_HSTS_DATELEN 256
+#define MAX_HSTS_DATELEN 17
 #define UNLIMITED        "unlimited"
 
 #if defined(DEBUGBUILD) || defined(UNITTESTS)
@@ -91,6 +91,20 @@
   }
 }
 
+/* append the new entry to the list after possibly removing an old entry
+   first */
+static void hsts_append(struct hsts *h, struct stsentry *sts)
+{
+  if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) {
+    /* It's full. Remove the first entry in the list */
+    struct Curl_llist_node *e = Curl_llist_head(&h->list);
+    struct stsentry *oldsts = Curl_node_elem(e);
+    Curl_node_remove(e);
+    hsts_free(oldsts);
+  }
+  Curl_llist_append(&h->list, sts, &sts->node);
+}
+
 static CURLcode hsts_create(struct hsts *h,
                             const char *hostname,
                             size_t hlen,
@@ -111,7 +125,7 @@
     memcpy(sts->host, hostname, hlen);
     sts->expires = expires;
     sts->includeSubDomains = subdomains;
-    Curl_llist_append(&h->list, sts, &sts->node);
+    hsts_append(h, sts);
   }
   return CURLE_OK;
 }
@@ -386,6 +400,61 @@
 }
 
 /* only returns SERIOUS errors */
+static CURLcode hsts_add_host_expire(struct hsts *h,
+                                     const char *host, size_t hostlen,
+                                     const char *expire, size_t explen,
+                                     bool subdomain) /* default */
+{
+  CURLcode result = CURLE_OK;
+  struct stsentry *e;
+  char dbuf[MAX_HSTS_DATELEN + 1];
+  time_t expires = 0;
+  time_t now = time(NULL);
+
+  /* The date parser works on a null-terminated string. */
+  if(explen > MAX_HSTS_DATELEN)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  memcpy(dbuf, expire, explen);
+  dbuf[explen] = 0;
+
+  if(!strcmp(dbuf, UNLIMITED))
+    expires = TIME_T_MAX;
+  else
+    Curl_getdate_capped(dbuf, &expires);
+
+  if(expires <= now)
+    /* this entry already expired */
+    return CURLE_OK;
+
+  if(host[0] == '.') {
+    host++;
+    hostlen--;
+    subdomain = TRUE;
+  }
+  if(hostlen && (host[hostlen - 1] == '.'))
+    /* strip off any trailing dot */
+    hostlen--;
+
+  if(hostlen) {
+    /* only add it if not already present */
+    e = Curl_hsts(h, host, hostlen, subdomain);
+    if(!e)
+      result = hsts_create(h, host, hostlen, subdomain, expires);
+    /* 'host' is not necessarily null terminated */
+    else if((hostlen == strlen(e->host) &&
+             curl_strnequal(host, e->host, hostlen))) {
+      /* the same hostname, use the largest expire time and keep the strictest
+         subdomain policy */
+      if(expires > e->expires)
+        e->expires = expires;
+      if(subdomain)
+        e->includeSubDomains = TRUE;
+    }
+  }
+  return result;
+}
+
+/* only returns SERIOUS errors */
 static CURLcode hsts_add(struct hsts *h, const char *line)
 {
   /* Example lines:
@@ -401,39 +470,9 @@
      curlx_str_newline(&line))
     ;
   else {
-    CURLcode result = CURLE_OK;
-    bool subdomain = FALSE;
-    struct stsentry *e;
-    char dbuf[MAX_HSTS_DATELEN + 1];
-    time_t expires = 0;
-    const char *hp = curlx_str(&host);
-
-    /* The date parser works on a null-terminated string. The maximum length
-       is upheld by curlx_str_quotedword(). */
-    memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
-    dbuf[curlx_strlen(&date)] = 0;
-
-    if(!strcmp(dbuf, UNLIMITED))
-      expires = TIME_T_MAX;
-    else
-      Curl_getdate_capped(dbuf, &expires);
-
-    if(hp[0] == '.') {
-      curlx_str_nudge(&host, 1);
-      subdomain = TRUE;
-    }
-    /* only add it if not already present */
-    e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain);
-    if(!e)
-      result = hsts_create(h, curlx_str(&host), curlx_strlen(&host),
-                           subdomain, expires);
-    else if(curlx_str_casecompare(&host, e->host)) {
-      /* the same hostname, use the largest expire time */
-      if(expires > e->expires)
-        e->expires = expires;
-    }
-    if(result)
-      return result;
+    return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host),
+                                curlx_str(&date), curlx_strlen(&date),
+                                FALSE);
   }
 
   return CURLE_OK;
@@ -456,23 +495,23 @@
       e.namelen = sizeof(buffer) - 1;
       e.includeSubDomains = FALSE; /* default */
       e.expire[0] = 0;
+      e.expire[MAX_HSTS_DATELEN] = 0;
       e.name[0] = 0; /* to make it clean */
+      e.name[MAX_HSTS_HOSTLEN] = 0;
       sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
       if(sc == CURLSTS_OK) {
-        time_t expires = 0;
         CURLcode result;
-        DEBUGASSERT(e.name[0]);
-        if(!e.name[0])
-          /* bail out if no name was stored */
+        const char *date = e.expire;
+        if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] ||
+           e.name[MAX_HSTS_HOSTLEN])
+          /* bail out if no name was stored or if a null terminator is gone */
           return CURLE_BAD_FUNCTION_ARGUMENT;
-        if(e.expire[0])
-          Curl_getdate_capped(e.expire, &expires);
-        else
-          expires = TIME_T_MAX; /* the end of time */
-        result = hsts_create(h, e.name, strlen(e.name),
-                             /* bitfield to bool conversion: */
-                             e.includeSubDomains ? TRUE : FALSE,
-                             expires);
+        if(!date[0])
+          date = UNLIMITED;
+        result = hsts_add_host_expire(h, e.name, strlen(e.name),
+                                      date, strlen(date),
+                                      /* bitfield to bool conversion: */
+                                      e.includeSubDomains ? TRUE : FALSE);
         if(result)
           return result;
       }
@@ -505,26 +544,28 @@
 
   fp = curlx_fopen(file, FOPEN_READTEXT);
   if(fp) {
-    struct dynbuf buf;
-    bool eof = FALSE;
-    curlx_dyn_init(&buf, MAX_HSTS_LINE);
-    do {
-      result = Curl_get_line(&buf, fp, &eof);
-      if(!result) {
-        const char *lineptr = curlx_dyn_ptr(&buf);
-        curlx_str_passblanks(&lineptr);
+    curlx_struct_stat stat;
+    if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
+      struct dynbuf buf;
+      bool eof = FALSE;
+      curlx_dyn_init(&buf, MAX_HSTS_LINE);
+      do {
+        result = Curl_get_line(&buf, fp, &eof);
+        if(!result) {
+          const char *lineptr = curlx_dyn_ptr(&buf);
+          curlx_str_passblanks(&lineptr);
 
-        /*
-         * Skip empty or commented lines, since we know the line will have a
-         * trailing newline from Curl_get_line we can treat length 1 as empty.
-         */
-        if((*lineptr == '#') || strlen(lineptr) <= 1)
-          continue;
+          /* Skip empty or commented lines, since we know the line will have
+             a trailing newline from Curl_get_line we can treat length 1 as
+             empty. */
+          if((*lineptr == '#') || strlen(lineptr) <= 1)
+            continue;
 
-        hsts_add(h, lineptr);
-      }
-    } while(!result && !eof);
-    curlx_dyn_free(&buf); /* free the line buffer */
+          hsts_add(h, lineptr);
+        }
+      } while(!result && !eof);
+      curlx_dyn_free(&buf); /* free the line buffer */
+    }
     curlx_fclose(fp);
   }
   return result;
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h
index e0f6363..0e6585f 100644
--- a/Utilities/cmcurl/lib/hsts.h
+++ b/Utilities/cmcurl/lib/hsts.h
@@ -28,6 +28,8 @@
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS)
 #include "llist.h"
 
+#define MAX_HSTS_ENTRIES 10000
+
 #if defined(DEBUGBUILD) || defined(UNITTESTS)
 extern time_t deltatime;
 #endif
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 188da5f..9118c7e 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -166,17 +166,29 @@
 }
 #endif
 
+/* If the header has a value, this function returns TRUE and the value is in
+   'outp' with blanks trimmed off.
+*/
+static bool header_has_value(const char **headerp, struct Curl_str *outp)
+{
+  bool value = !curlx_str_cspn(headerp, outp, ";:") &&
+    (!curlx_str_single(headerp, ':') || !curlx_str_single(headerp, ';'));
+
+  if(value) {
+    curlx_str_untilnl(headerp, outp, MAX_HTTP_RESP_HEADER_SIZE);
+    curlx_str_trimblanks(outp);
+  }
+  return value;
+}
+
 static bool http_header_is_empty(const char *header)
 {
   struct Curl_str out;
 
-  if(!curlx_str_cspn(&header, &out, ";:") &&
-     (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) {
-    curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
-    curlx_str_trimblanks(&out);
+  if(header_has_value(&header, &out)) {
     return curlx_strlen(&out) == 0;
   }
-  return TRUE; /* invalid head format, treat as empty */
+  return TRUE; /* invalid header format, treat as empty */
 }
 
 /*
@@ -192,11 +204,7 @@
   struct Curl_str out;
 
   /* find the end of the header name */
-  if(!curlx_str_cspn(&header, &out, ";:") &&
-     (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) {
-    curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
-    curlx_str_trimblanks(&out);
-
+  if(header_has_value(&header, &out)) {
     *valp = curlx_memdup0(curlx_str(&out), curlx_strlen(&out));
     if(*valp)
       return CURLE_OK;
@@ -214,6 +222,8 @@
  *
  * This function MUST be used after the header has already been confirmed to
  * lead with "word:".
+ *
+ * @unittest: 1626
  */
 char *Curl_copy_header_value(const char *header)
 {
@@ -254,7 +264,7 @@
      connection */
   if(proxy) {
 #ifndef CURL_DISABLE_PROXY
-    userp = &data->state.aptr.proxyuserpwd;
+    userp = &data->req.proxyuserpwd;
     user = data->state.aptr.proxyuser;
     pwd = data->state.aptr.proxypasswd;
 #else
@@ -262,7 +272,7 @@
 #endif
   }
   else {
-    userp = &data->state.aptr.userpwd;
+    userp = &data->req.userpwd;
     user = data->state.aptr.user;
     pwd = data->state.aptr.passwd;
   }
@@ -310,7 +320,7 @@
   char **userp;
   CURLcode result = CURLE_OK;
 
-  userp = &data->state.aptr.userpwd;
+  userp = &data->req.userpwd;
   curlx_free(*userp);
   *userp = curl_maprintf("Authorization: Bearer %s\r\n",
                          data->set.str[STRING_BEARER]);
@@ -470,53 +480,53 @@
   DEBUGASSERT(data->conn);
 
   /*
-  ** If we have not been asked to fail on error,
-  ** do not fail.
-  */
+   * If we have not been asked to fail on error,
+   * do not fail.
+   */
   if(!data->set.http_fail_on_error)
     return FALSE;
 
   /*
-  ** Any code < 400 is never terminal.
-  */
+   * Any code < 400 is never terminal.
+   */
   if(httpcode < 400)
     return FALSE;
 
   /*
-  ** A 416 response to a resume request is presumably because the file is
-  ** already completely downloaded and thus not actually a fail.
-  */
+   * A 416 response to a resume request is presumably because the file is
+   * already completely downloaded and thus not actually a fail.
+   */
   if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
      httpcode == 416)
     return FALSE;
 
   /*
-  ** Any code >= 400 that is not 401 or 407 is always
-  ** a terminal error
-  */
+   * Any code >= 400 that is not 401 or 407 is always
+   * a terminal error
+   */
   if((httpcode != 401) && (httpcode != 407))
     return TRUE;
 
   /*
-  ** All we have left to deal with is 401 and 407
-  */
+   * All we have left to deal with is 401 and 407
+   */
   DEBUGASSERT((httpcode == 401) || (httpcode == 407));
 
   /*
-  ** Examine the current authentication state to see if this is an error. The
-  ** idea is for this function to get called after processing all the headers
-  ** in a response message. So, if we have been to asked to authenticate a
-  ** particular stage, and we have done it, we are OK. If we are already
-  ** completely authenticated, it is not OK to get another 401 or 407.
-  **
-  ** It is possible for authentication to go stale such that the client needs
-  ** to reauthenticate. Once that info is available, use it here.
-  */
+   * Examine the current authentication state to see if this is an error. The
+   * idea is for this function to get called after processing all the headers
+   * in a response message. If we have been to asked to authenticate
+   * a particular stage, and we have done it, we are OK. If we are already
+   * completely authenticated, it is not OK to get another 401 or 407.
+   *
+   * It is possible for authentication to go stale such that the client needs
+   * to reauthenticate. Once that info is available, use it here.
+   */
 
   /*
-  ** Either we are not authenticating, or we are supposed to be authenticating
-  ** something else. This is an error.
-  */
+   * Either we are not authenticating, or we are supposed to be authenticating
+   * something else. This is an error.
+   */
   if((httpcode == 401) && !data->state.aptr.user)
     return TRUE;
 #ifndef CURL_DISABLE_PROXY
@@ -721,6 +731,10 @@
 
   if(auth) {
 #ifndef CURL_DISABLE_PROXY
+    if(proxy)
+      data->info.proxyauthpicked = authstatus->picked;
+    else
+      data->info.httpauthpicked = authstatus->picked;
     infof(data, "%s auth using %s with user '%s'",
           proxy ? "Proxy" : "Server", auth,
           proxy ? (data->state.aptr.proxyuser ?
@@ -735,8 +749,13 @@
 #endif
     authstatus->multipass = !authstatus->done;
   }
-  else
+  else {
     authstatus->multipass = FALSE;
+    if(proxy)
+      data->info.proxyauthpicked = 0;
+    else
+      data->info.httpauthpicked = 0;
+  }
 
   return result;
 }
@@ -755,14 +774,14 @@
  *
  * @returns CURLcode
  */
-CURLcode
-Curl_http_output_auth(struct Curl_easy *data,
-                      struct connectdata *conn,
-                      const char *request,
-                      Curl_HttpReq httpreq,
-                      const char *path,
-                      bool proxytunnel) /* TRUE if this is the request setting
-                                           up the proxy tunnel */
+CURLcode Curl_http_output_auth(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               const char *request,
+                               Curl_HttpReq httpreq,
+                               const char *path,
+                               bool proxytunnel) /* TRUE if this is
+                                                    the request setting up
+                                                    the proxy tunnel */
 {
   CURLcode result = CURLE_OK;
   struct auth *authhost;
@@ -943,8 +962,10 @@
                             struct auth *authp,
                             uint32_t *availp)
 {
-  if(authp->avail & CURLAUTH_DIGEST)
+  if(authp->avail & CURLAUTH_DIGEST) {
+    *availp |= CURLAUTH_DIGEST;
     infof(data, "Ignoring duplicate digest auth header.");
+  }
   else if(Curl_auth_is_digest_supported()) {
     CURLcode result;
 
@@ -1113,6 +1134,11 @@
   Curl_creader_set_rewind(data, FALSE);
 }
 
+#define HTTPREQ_IS_POST(data)                           \
+  ((data)->state.httpreq == HTTPREQ_POST ||             \
+   (data)->state.httpreq == HTTPREQ_POST_FORM ||        \
+   (data)->state.httpreq == HTTPREQ_POST_MIME)
+
 CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
                           followtype type)
 {
@@ -1201,60 +1227,43 @@
       return CURLE_OUT_OF_MEMORY;
   }
   else {
-    uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
-    if(uc)
+    bool same_origin;
+    CURLcode result;
+    CURLU *u = curl_url();
+    if(!u)
+      return CURLE_OUT_OF_MEMORY;
+    uc = curl_url_set(u, CURLUPART_URL,
+                      Curl_bufref_ptr(&data->state.url),
+                      CURLU_URLENCODE | CURLU_ALLOW_SPACE);
+    if(!uc)
+      uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
+    if(uc) {
+      curl_url_cleanup(u);
       return Curl_uc_to_curlcode(uc);
+    }
 
-    /* Clear auth if this redirects to a different port number or protocol,
-       unless permitted */
-    if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
-      int port;
-      bool clear = FALSE;
+    same_origin = Curl_url_same_origin(u, data->state.uh);
+    curl_url_cleanup(u);
 
-      if(data->set.use_port && data->state.allow_port)
-        /* a custom port is used */
-        port = (int)data->set.use_port;
-      else {
-        curl_off_t value;
-        char *portnum;
-        const char *p;
-        uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
-                          CURLU_DEFAULT_PORT);
-        if(uc) {
-          curlx_free(follow_url);
-          return Curl_uc_to_curlcode(uc);
-        }
-        p = portnum;
-        curlx_str_number(&p, &value, 0xffff);
-        port = (int)value;
-        curlx_free(portnum);
-      }
-      if(port != data->info.conn_remote_port) {
-        infof(data, "Clear auth, redirects to port from %u to %u",
-              data->info.conn_remote_port, port);
-        clear = TRUE;
-      }
-      else {
-        char *scheme;
-        const struct Curl_scheme *p;
-        uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
-        if(uc) {
-          curlx_free(follow_url);
-          return Curl_uc_to_curlcode(uc);
-        }
+#ifndef CURL_DISABLE_DIGEST_AUTH
+    if(!same_origin)
+      Curl_auth_digest_cleanup(&data->state.digest);
+#endif
 
-        p = Curl_get_scheme(scheme);
-        if(p && (p->protocol != data->info.conn_protocol)) {
-          infof(data, "Clear auth, redirects scheme from %s to %s",
-                data->info.conn_scheme, scheme);
-          clear = TRUE;
-        }
-        curlx_free(scheme);
+    if((!same_origin && !data->set.allow_auth_to_other_hosts) ||
+       !data->set.str[STRING_USERNAME]) {
+      result = Curl_reset_userpwd(data);
+      if(result) {
+        curlx_free(follow_url);
+        return result;
       }
-      if(clear) {
-        Curl_safefree(data->state.aptr.user);
-        Curl_safefree(data->state.aptr.passwd);
-      }
+      curlx_safefree(data->state.aptr.user);
+      curlx_safefree(data->state.aptr.passwd);
+    }
+    result = Curl_reset_proxypwd(data);
+    if(result) {
+      curlx_free(follow_url);
+      return result;
     }
   }
   DEBUGASSERT(follow_url);
@@ -1321,10 +1330,7 @@
      * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
      * can be overridden with CURLOPT_POSTREDIR.
      */
-    if((data->state.httpreq == HTTPREQ_POST ||
-        data->state.httpreq == HTTPREQ_POST_FORM ||
-        data->state.httpreq == HTTPREQ_POST_MIME) &&
-       !data->set.post301) {
+    if(HTTPREQ_IS_POST(data) && !data->set.post301) {
       http_switch_to_get(data, 301);
       switch_to_get = TRUE;
     }
@@ -1346,10 +1352,7 @@
      * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
      * can be overridden with CURLOPT_POSTREDIR.
      */
-    if((data->state.httpreq == HTTPREQ_POST ||
-        data->state.httpreq == HTTPREQ_POST_FORM ||
-        data->state.httpreq == HTTPREQ_POST_MIME) &&
-       !data->set.post302) {
+    if(HTTPREQ_IS_POST(data) && !data->set.post302) {
       http_switch_to_get(data, 302);
       switch_to_get = TRUE;
     }
@@ -1359,13 +1362,8 @@
     /* 'See Other' location is not the resource but a substitute for the
      * resource. In this case we switch the method to GET/HEAD, unless the
      * method is POST and the user specified to keep it as POST.
-     * https://github.com/curl/curl/issues/5237#issuecomment-614641049
      */
-    if(data->state.httpreq != HTTPREQ_GET &&
-       ((data->state.httpreq != HTTPREQ_POST &&
-         data->state.httpreq != HTTPREQ_POST_FORM &&
-         data->state.httpreq != HTTPREQ_POST_MIME) ||
-        !data->set.post303)) {
+    if(!HTTPREQ_IS_POST(data) || !data->set.post303) {
       http_switch_to_get(data, 303);
       switch_to_get = TRUE;
     }
@@ -1400,8 +1398,10 @@
 /*
  * Curl_compareheader()
  *
- * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
- * Pass headers WITH the colon.
+ * Returns TRUE if 'headerline' contains the 'header' with given 'content'
+ * (within a comma-separated list of tokens). Pass 'header' WITH the colon.
+ *
+ * @unittest: 1625
  */
 bool Curl_compareheader(const char *headerline, /* line to check */
                         const char *header, /* header keyword _with_ colon */
@@ -1435,9 +1435,24 @@
   if(curlx_strlen(&val) >= clen) {
     size_t len;
     p = curlx_str(&val);
-    for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
-      if(curl_strnequal(p, content, clen))
+    for(len = curlx_strlen(&val); len >= clen;) {
+      struct Curl_str next;
+      const char *o = p;
+      /* after a match there must be a comma, space, newline or null byte */
+      if(curl_strnequal(p, content, clen) &&
+         ((p[clen] == ',') || ISBLANK(p[clen]) || ISNEWLINE(p[clen]) ||
+          !p[clen]))
         return TRUE; /* match! */
+      /* advance to the next comma */
+      if(curlx_str_until(&p, &next, MAX_HTTP_RESP_HEADER_SIZE, ',') ||
+         curlx_str_single(&p, ','))
+        break; /* no comma, get out */
+
+      /* if there are more dummy commas, move over them as well */
+      do
+        curlx_str_passblanks(&p);
+      while(!curlx_str_single(&p, ','));
+      len -= (p - o);
     }
   }
   return FALSE; /* no match */
@@ -1482,7 +1497,7 @@
     /* We are now waiting for a reply from the server or
      * a timeout on our side IFF the request has been fully sent. */
     DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
-           "timeout %dms", data->set.expect_100_timeout));
+                 "timeout %dms", data->set.expect_100_timeout));
     ctx->state = EXP100_AWAITING_CONTINUE;
     ctx->start = *Curl_pgrs_now(data);
     Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
@@ -1598,7 +1613,7 @@
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
 
-  if(CURL_WANT_RECV(data)) {
+  if(CURL_REQ_WANT_RECV(data)) {
     result = Curl_pollset_add_in(data, ps, conn->sock[FIRSTSOCKET]);
   }
 
@@ -1973,7 +1988,7 @@
 
 static CURLcode http_useragent(struct Curl_easy *data)
 {
-  /* The User-Agent string might have been allocated in url.c already, because
+  /* The User-Agent string might have been allocated already, because
      it might have been used in the proxy connect, but if we have got a header
      with the user-agent string specified, we erase the previously made string
      here. */
@@ -2001,7 +2016,10 @@
     data->state.first_remote_port = conn->remote_port;
     data->state.first_remote_protocol = conn->scheme->protocol;
   }
-  Curl_safefree(aptr->host);
+  curlx_safefree(aptr->host);
+#ifndef CURL_DISABLE_COOKIES
+  curlx_safefree(data->req.cookiehost);
+#endif
 
   ptr = Curl_checkheaders(data, STRCONST("Host"));
   if(ptr && (!data->state.this_is_a_follow ||
@@ -2037,8 +2055,7 @@
         if(colon)
           *colon = 0; /* The host must not include an embedded port number */
       }
-      curlx_free(aptr->cookiehost);
-      aptr->cookiehost = cookiehost;
+      data->req.cookiehost = cookiehost;
     }
 #endif
 
@@ -2049,9 +2066,9 @@
     }
   }
   else {
-    /* When building Host: headers, we must put the hostname within
-       [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */
-    const char *host = conn->host.name;
+    /* Use the hostname as present in the URL if it was IPv6. */
+    char *host = (data->state.up.hostname[0] == '[') ?
+       data->state.up.hostname : conn->host.name;
 
     if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
         (conn->remote_port == PORT_HTTPS)) ||
@@ -2059,14 +2076,9 @@
         (conn->remote_port == PORT_HTTP)))
       /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
          the port number in the host string */
-      aptr->host = curl_maprintf("Host: %s%s%s\r\n",
-                                 conn->bits.ipv6_ip ? "[" : "",
-                                 host, conn->bits.ipv6_ip ? "]" : "");
+      aptr->host = curl_maprintf("Host: %s\r\n", host);
     else
-      aptr->host = curl_maprintf("Host: %s%s%s:%d\r\n",
-                                 conn->bits.ipv6_ip ? "[" : "",
-                                 host, conn->bits.ipv6_ip ? "]" : "",
-                                 conn->remote_port);
+      aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port);
 
     if(!aptr->host)
       /* without Host: we cannot make a nice request */
@@ -2097,7 +2109,7 @@
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
     /* Using a proxy but does not tunnel through it */
 
-    /* The path sent to the proxy is in fact the entire URL. But if the remote
+    /* The path sent to the proxy is in fact the entire URL, but if the remote
        host is a IDN-name, we must make sure that the request we produce only
        uses the encoded hostname! */
 
@@ -2209,7 +2221,7 @@
       result = Curl_getformdata(data, data->state.formp, data->set.httppost,
                                 data->state.fread_func);
       if(result) {
-        Curl_safefree(data->state.formp);
+        curlx_safefree(data->state.formp);
         return result;
       }
       data->state.mimepost = data->state.formp;
@@ -2538,8 +2550,8 @@
 
     if(data->cookies && data->state.cookie_engine) {
       bool okay;
-      const char *host = data->state.aptr.cookiehost ?
-        data->state.aptr.cookiehost : data->conn->host.name;
+      const char *host = data->req.cookiehost ?
+        data->req.cookiehost : data->conn->host.name;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
       result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
       if(!result && okay) {
@@ -2663,7 +2675,7 @@
     if(conn->bits.close) {
       /* Abort after the headers if "follow Location" is set
          and we are set to close anyway. */
-      k->keepon &= ~KEEP_RECV;
+      CURL_REQ_CLEAR_RECV(data);
       k->done = TRUE;
       return CURLE_OK;
     }
@@ -2682,7 +2694,7 @@
       infof(data, "The entire document is already downloaded");
       streamclose(conn, "already downloaded");
       /* Abort download */
-      k->keepon &= ~KEEP_RECV;
+      CURL_REQ_CLEAR_RECV(data);
       k->done = TRUE;
       return CURLE_OK;
     }
@@ -2885,14 +2897,14 @@
 
 #ifndef CURL_DISABLE_PROXY
   case H1_HD_PROXY_AUTH:
-    if(data->state.aptr.proxyuserpwd)
-      result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd);
+    if(data->req.proxyuserpwd)
+      result = curlx_dyn_add(req, data->req.proxyuserpwd);
     break;
 #endif
 
   case H1_HD_USER_AUTH:
-    if(data->state.aptr.userpwd)
-      result = curlx_dyn_add(req, data->state.aptr.userpwd);
+    if(data->req.userpwd)
+      result = curlx_dyn_add(req, data->req.userpwd);
     break;
 
   case H1_HD_RANGE:
@@ -2923,7 +2935,7 @@
     break;
 
   case H1_HD_ACCEPT_ENCODING:
-    Curl_safefree(data->state.aptr.accept_encoding);
+    curlx_safefree(data->state.aptr.accept_encoding);
     if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
        data->set.str[STRING_ENCODING])
       result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n",
@@ -2931,7 +2943,7 @@
     break;
 
   case H1_HD_REFERER:
-    Curl_safefree(data->state.aptr.ref);
+    curlx_safefree(data->state.aptr.ref);
     if(Curl_bufref_ptr(&data->state.referer) &&
        !Curl_checkheaders(data, STRCONST("Referer")))
       result = curlx_dyn_addf(req, "Referer: %s\r\n",
@@ -2955,7 +2967,7 @@
 #ifndef CURL_DISABLE_ALTSVC
   case H1_HD_ALT_USED:
     if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used")))
-      result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n",
+      result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n",
                               conn->conn_to_host.name,
                               conn->conn_to_port);
     break;
@@ -3103,12 +3115,6 @@
   if(result == CURLE_TOO_LARGE)
     failf(data, "HTTP request too large");
 
-  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
-   * from reused connections */
-  Curl_safefree(data->state.aptr.userpwd);
-#ifndef CURL_DISABLE_PROXY
-  Curl_safefree(data->state.aptr.proxyuserpwd);
-#endif
   curlx_dyn_free(&req);
   return result;
 }
@@ -3156,13 +3162,13 @@
 static statusline checkrtspprefix(struct Curl_easy *data,
                                   const char *s, size_t len)
 {
-  statusline result = STATUS_BAD;
+  statusline status = STATUS_BAD;
   statusline onmatch = len >= 5 ? STATUS_DONE : STATUS_UNKNOWN;
   (void)data;
   if(checkprefixmax("RTSP/", s, len))
-    result = onmatch;
+    status = onmatch;
 
-  return result;
+  return status;
 }
 #endif /* CURL_DISABLE_RTSP */
 
@@ -3205,7 +3211,7 @@
   const char *v;
   struct connectdata *conn = data->conn;
   v = (data->asi &&
-       (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ||
+       (Curl_xfer_is_secure(data) ||
 #ifdef DEBUGBUILD
         /* allow debug builds to circumvent the HTTPS restriction */
         getenv("CURL_ALTSVC_HTTP")
@@ -3545,8 +3551,8 @@
   if(v) {
     /* If there is a custom-set Host: name, use it here, or else use
      * real peer hostname. */
-    const char *host = data->state.aptr.cookiehost ?
-      data->state.aptr.cookiehost : conn->host.name;
+    const char *host = data->req.cookiehost ?
+      data->req.cookiehost : conn->host.name;
     const bool secure_context = Curl_secure_context(conn, host);
     CURLcode result;
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@@ -3559,7 +3565,7 @@
 #ifndef CURL_DISABLE_HSTS
   /* If enabled, the header is incoming and this is over HTTPS */
   v = (data->hsts &&
-       (Curl_conn_is_ssl(conn, FIRSTSOCKET) ||
+       (Curl_xfer_is_secure(data) ||
 #ifdef DEBUGBUILD
         /* allow debug builds to circumvent the HTTPS restriction */
         getenv("CURL_HSTS_HTTP")
@@ -3569,11 +3575,11 @@
          )
     ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
   if(v) {
-    CURLcode check =
+    CURLcode result =
       Curl_hsts_parse(data->hsts, conn->host.name, v);
-    if(check) {
-      if(check == CURLE_OUT_OF_MEMORY)
-        return check;
+    if(result) {
+      if(result == CURLE_OUT_OF_MEMORY)
+        return result;
       infof(data, "Illegal STS header skipped");
     }
 #ifdef DEBUGBUILD
@@ -3726,13 +3732,13 @@
     /* no major version switch mid-connection */
     if(k->httpversion_sent &&
        (k->httpversion / 10 != k->httpversion_sent / 10)) {
-      failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)",
+      failf(data, "Version mismatch (from HTTP/%d to HTTP/%d)",
             k->httpversion_sent / 10, k->httpversion / 10);
       return CURLE_WEIRD_SERVER_REPLY;
     }
     break;
   default:
-    failf(data, "Unsupported HTTP version (%u.%d) in response",
+    failf(data, "Unsupported HTTP version (%d.%d) in response",
           k->httpversion / 10, k->httpversion % 10);
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
@@ -3875,136 +3881,112 @@
   return CURLE_OK;
 }
 
-static CURLcode http_on_response(struct Curl_easy *data,
-                                 const char *last_hd, size_t last_hd_len,
-                                 const char *buf, size_t blen,
-                                 size_t *pconsumed)
+/*
+ * Handle a 101 Switching Protocols response. Performs the actual protocol
+ * upgrade to HTTP/2 or WebSocket based on what was requested.
+ */
+static CURLcode http_on_101_upgrade(struct Curl_easy *data,
+                                    const char *buf, size_t blen,
+                                    size_t *pconsumed,
+                                    bool *conn_changed)
 {
   struct connectdata *conn = data->conn;
-  CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
-  bool conn_changed = FALSE;
 
-  (void)buf; /* not used without HTTP2 enabled */
-  *pconsumed = 0;
-
-  if(k->upgr101 == UPGR101_RECEIVED) {
-    /* supposedly upgraded to http2 now */
-    if(data->req.httpversion != 20)
-      infof(data, "Lying server, not serving HTTP/2");
-  }
-
-  if(k->httpcode < 200 && last_hd) {
-    /* Intermediate responses might trigger processing of more
-     * responses, write the last header to the client before
-     * proceeding. */
-    result = http_write_header(data, last_hd, last_hd_len);
-    last_hd = NULL; /* handled it */
-    if(result)
-      goto out;
-  }
-
-  if(k->httpcode < 100) {
-    failf(data, "Unsupported response code in HTTP response");
-    result = CURLE_UNSUPPORTED_PROTOCOL;
-    goto out;
-  }
-  else if(k->httpcode < 200) {
-    /* "A user agent MAY ignore unexpected 1xx status responses."
-     * By default, we expect to get more responses after this one. */
-    k->header = TRUE;
-    k->headerline = 0; /* restart the header line counter */
-
-    switch(k->httpcode) {
-    case 100:
-      /*
-       * We have made an HTTP PUT or POST and this is 1.1-lingo
-       * that tells us that the server is OK with this and ready
-       * to receive the data.
-       */
-      http_exp100_got100(data);
-      break;
-    case 101: {
-      int upgr101_requested = k->upgr101;
-
-      if(k->httpversion_sent != 11) {
-        /* invalid for other HTTP versions */
-        failf(data, "server sent 101 response while not talking HTTP/1.1");
-        result = CURLE_WEIRD_SERVER_REPLY;
-        goto out;
-      }
-
-      /* Whatever the success, upgrade was selected. */
-      k->upgr101 = UPGR101_RECEIVED;
-      data->conn->bits.upgrade_in_progress = FALSE;
-      conn_changed = TRUE;
-
-      /* To be fully conform, we would check the "Upgrade:" response header
-       * to mention the protocol we requested. */
-      switch(upgr101_requested) {
-      case UPGR101_H2:
-        /* Switch to HTTP/2, where we will get more responses.
-         * blen bytes in bug are already h2 protocol bytes */
-        infof(data, "Received 101, Switching to HTTP/2");
-        result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
-        if(result)
-          goto out;
-        *pconsumed += blen;
-        break;
-#ifndef CURL_DISABLE_WEBSOCKETS
-      case UPGR101_WS:
-        /* Switch to WebSocket, where we now stream ws frames.
-         * blen bytes in bug are already ws protocol bytes */
-        infof(data, "Received 101, Switching to WebSocket");
-        result = Curl_ws_accept(data, buf, blen);
-        if(result)
-          goto out;
-        *pconsumed += blen; /* ws accept handled the data */
-        break;
+#if !defined(USE_NGHTTP2) && defined(CURL_DISABLE_WEBSOCKETS)
+  (void)buf;
+  (void)blen;
+  (void)pconsumed;
+#else
+  CURLcode result;
+  int upgr101_requested = k->upgr101;
 #endif
-      default:
-        /* We silently accept this as the final response. What are we
-         * switching to if we did not ask for an Upgrade? Maybe the
-         * application provided an `Upgrade: xxx` header? */
-        k->header = FALSE;
-        break;
-      }
-      /* processed 101 */
-      break;
-    }
-    default:
-      /* The server may send us other 1xx responses, like informative
-       * 103. This have no influence on request processing and we expect
-       * to receive a final response eventually. */
-      break;
-    }
-    goto out;
+
+  if(k->httpversion_sent != 11) {
+    /* invalid for other HTTP versions */
+    failf(data, "server sent 101 response while not talking HTTP/1.1");
+    return CURLE_WEIRD_SERVER_REPLY;
   }
 
-  /* k->httpcode >= 200, final response */
+  /* Whatever the success, upgrade was selected. */
+  k->upgr101 = UPGR101_RECEIVED;
+  conn->bits.upgrade_in_progress = FALSE;
+  *conn_changed = TRUE;
+
+  /* To be fully compliant, we would check the "Upgrade:" response header to
+   * mention the protocol we requested. */
+#ifdef USE_NGHTTP2
+  if(upgr101_requested == UPGR101_H2) {
+    /* Switch to HTTP/2, where we will get more responses. blen bytes in buf
+     * are already h2 protocol bytes */
+    infof(data, "Received 101, Switching to HTTP/2");
+    result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
+    if(!result)
+      *pconsumed += blen;
+    return result;
+  }
+#endif
+#ifndef CURL_DISABLE_WEBSOCKETS
+  if(upgr101_requested == UPGR101_WS) {
+    /* Switch to WebSocket, where we now stream ws frames. blen bytes in buf
+     * are already ws protocol bytes */
+    infof(data, "Received 101, Switching to WebSocket");
+    result = Curl_ws_accept(data, buf, blen);
+    if(!result)
+      *pconsumed += blen; /* ws accept handled the data */
+    return result;
+  }
+#endif
+  /* We silently accept this as the final response. What are we switching to
+   * if we did not ask for an Upgrade? Maybe the application provided an
+   * `Upgrade: xxx` header? */
   k->header = FALSE;
-  if(data->conn->bits.upgrade_in_progress) {
-    /* Asked for protocol upgrade, but it was not selected by the server */
-    data->conn->bits.upgrade_in_progress = FALSE;
-    conn_changed = TRUE;
-  }
+  return CURLE_OK;
+}
 
-  if((k->size == -1) && !k->chunk && !conn->bits.close &&
-     (k->httpversion == 11) &&
-     !(conn->scheme->protocol & CURLPROTO_RTSP) &&
-     data->state.httpreq != HTTPREQ_HEAD) {
-    /* On HTTP 1.1, when connection is not to get closed, but no
-       Content-Length nor Transfer-Encoding chunked have been
-       received, according to RFC2616 section 4.4 point 5, we
-       assume that the server will close the connection to
-       signal the end of the document. */
-    infof(data, "no chunk, no close, no size. Assume close to "
-          "signal end");
-    streamclose(conn, "HTTP: No end-of-message indicator");
-  }
+/*
+ * Handle 1xx intermediate HTTP responses. Sets up state for more
+ * headers and processes 100-continue and 101 upgrade responses.
+ */
+static CURLcode http_on_1xx_response(struct Curl_easy *data,
+                                     const char *buf, size_t blen,
+                                     size_t *pconsumed,
+                                     bool *conn_changed)
+{
+  struct SingleRequest *k = &data->req;
 
-  /* At this point we have some idea about the fate of the connection.
-     If we are closing the connection it may result auth failure. */
+  /* "A user agent MAY ignore unexpected 1xx status responses."
+   * By default, we expect to get more responses after this one. */
+  k->header = TRUE;
+  k->headerline = 0; /* restart the header line counter */
+
+  switch(k->httpcode) {
+  case 100:
+    /* We have made an HTTP PUT or POST and this is 1.1-lingo that tells us
+     * that the server is OK with this and ready to receive the data. */
+    http_exp100_got100(data);
+    break;
+  case 101:
+    return http_on_101_upgrade(data, buf, blen, pconsumed, conn_changed);
+  default:
+    /* The server may send us other 1xx responses, like informative 103. This
+     * has no influence on request processing and we expect to receive a
+     * final response eventually. */
+    break;
+  }
+  return CURLE_OK;
+}
+
+#if defined(USE_NTLM) || defined(USE_SPNEGO)
+/*
+ * Check if NTLM or SPNEGO authentication negotiation failed due to
+ * connection closure (typically on HTTP/1.0 servers).
+ */
+static void http_check_auth_closure(struct Curl_easy *data,
+                                    struct connectdata *conn)
+{
+  /* At this point we have some idea about the fate of the connection. If we
+     are closing the connection it may result auth failure. */
 #ifdef USE_NTLM
   if(conn->bits.close &&
      (((data->req.httpcode == 401) &&
@@ -4033,9 +4015,154 @@
     conn->proxy_negotiate_state = GSS_AUTHSUCC;
   }
 #endif
+}
+#else
+#define http_check_auth_closure(x, y) /* empty */
+#endif
+
+/*
+ * Handle an error response (>= 300) received while still sending the
+ * request body. Deals with 417 Expectation Failed retries, keep-sending
+ * on error, and aborting the send.
+ */
+static CURLcode http_handle_send_error(struct Curl_easy *data)
+{
+  struct connectdata *conn = data->conn;
+  struct SingleRequest *k = &data->req;
+  CURLcode result = CURLE_OK;
+
+  if(!data->req.authneg && !conn->bits.close &&
+     !Curl_creader_will_rewind(data)) {
+    /*
+     * General treatment of errors when about to send data.
+     * Including: "417 Expectation Failed", while waiting for
+     * 100-continue.
+     *
+     * The check for close above is done because if something
+     * else has already deemed the connection to get closed then
+     * something else should have considered the big picture and
+     * we avoid this check.
+     */
+
+    switch(data->state.httpreq) {
+    case HTTPREQ_PUT:
+    case HTTPREQ_POST:
+    case HTTPREQ_POST_FORM:
+    case HTTPREQ_POST_MIME:
+      /* We got an error response. If this happened before the
+       * whole request body has been sent we stop sending and
+       * mark the connection for closure after we have read the
+       * entire response. */
+      if(!Curl_req_done_sending(data)) {
+        if((k->httpcode == 417) && http_exp100_is_selected(data)) {
+          /* 417 Expectation Failed - try again without the
+             Expect header */
+          if(!k->writebytecount && http_exp100_is_waiting(data)) {
+            infof(data, "Got HTTP failure 417 while waiting for a 100");
+          }
+          else {
+            infof(data, "Got HTTP failure 417 while sending data");
+            streamclose(conn, "Stop sending data before everything sent");
+            result = http_perhapsrewind(data, conn);
+            if(result)
+              return result;
+          }
+          data->state.disableexpect = TRUE;
+          Curl_req_abort_sending(data);
+          DEBUGASSERT(!data->req.newurl);
+          data->req.newurl = Curl_bufref_dup(&data->state.url);
+          if(!data->req.newurl)
+            return CURLE_OUT_OF_MEMORY;
+        }
+        else if(data->set.http_keep_sending_on_error) {
+          infof(data, "HTTP error before end of send, keep sending");
+          http_exp100_send_anyway(data);
+        }
+        else {
+          infof(data, "HTTP error before end of send, stop sending");
+          streamclose(conn, "Stop sending data before everything sent");
+          result = Curl_req_abort_sending(data);
+          if(result)
+            return result;
+        }
+      }
+      break;
+
+    default: /* default label present to avoid compiler warnings */
+      break;
+    }
+  }
+
+  if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) {
+    /* We rewind before next send, continue sending now */
+    infof(data, "Keep sending data to get tossed away");
+    CURL_REQ_SET_SEND(data);
+  }
+  return result;
+}
+
+static CURLcode http_on_response(struct Curl_easy *data,
+                                 const char *last_hd, size_t last_hd_len,
+                                 const char *buf, size_t blen,
+                                 size_t *pconsumed)
+{
+  struct connectdata *conn = data->conn;
+  CURLcode result = CURLE_OK;
+  struct SingleRequest *k = &data->req;
+  bool conn_changed = FALSE;
+
+  (void)buf; /* not used without HTTP2 enabled */
+  *pconsumed = 0;
+
+  if(k->upgr101 == UPGR101_RECEIVED) {
+    /* supposedly upgraded to http2 now */
+    if(data->req.httpversion != 20)
+      infof(data, "Lying server, not serving HTTP/2");
+  }
+
+  if(k->httpcode < 200 && last_hd) {
+    /* Intermediate responses might trigger processing of more responses,
+     * write the last header to the client before proceeding. */
+    result = http_write_header(data, last_hd, last_hd_len);
+    last_hd = NULL; /* handled it */
+    if(result)
+      goto out;
+  }
+
+  if(k->httpcode < 100) {
+    failf(data, "Unsupported response code in HTTP response");
+    result = CURLE_UNSUPPORTED_PROTOCOL;
+    goto out;
+  }
+  else if(k->httpcode < 200) {
+    result = http_on_1xx_response(data, buf, blen, pconsumed, &conn_changed);
+    goto out;
+  }
+
+  /* k->httpcode >= 200, final response */
+  k->header = FALSE;
+  if(conn->bits.upgrade_in_progress) {
+    /* Asked for protocol upgrade, but it was not selected */
+    conn->bits.upgrade_in_progress = FALSE;
+    conn_changed = TRUE;
+  }
+
+  if((k->size == -1) && !k->chunk && !conn->bits.close &&
+     (k->httpversion == 11) &&
+     !(conn->scheme->protocol & CURLPROTO_RTSP) &&
+     data->state.httpreq != HTTPREQ_HEAD) {
+    /* On HTTP 1.1, when connection is not to get closed, but no
+       Content-Length nor Transfer-Encoding chunked have been received,
+       according to RFC2616 section 4.4 point 5, we assume that the server
+       will close the connection to signal the end of the document. */
+    infof(data, "no chunk, no close, no size. Assume close to signal end");
+    streamclose(conn, "HTTP: No end-of-message indicator");
+  }
+
+  http_check_auth_closure(data, conn);
 
 #ifndef CURL_DISABLE_WEBSOCKETS
-  /* All >=200 HTTP status codes are errors when wanting WebSocket */
+  /* All >=200 HTTP status codes are errors when wanting ws */
   if(data->req.upgr101 == UPGR101_WS) {
     failf(data, "Refused WebSocket upgrade: %d", k->httpcode);
     result = CURLE_HTTP_RETURNED_ERROR;
@@ -4045,88 +4172,23 @@
 
   /* Check if this response means the transfer errored. */
   if(http_should_fail(data, data->req.httpcode)) {
-    failf(data, "The requested URL returned error: %d", k->httpcode);
+    failf(data, "The requested URL returned error: %d",
+          k->httpcode);
     result = CURLE_HTTP_RETURNED_ERROR;
     goto out;
   }
 
-  /* Curl_http_auth_act() checks what authentication methods
-   * that are available and decides which one (if any) to
-   * use. It will set 'newurl' if an auth method was picked. */
+  /* Curl_http_auth_act() checks what authentication methods that are
+   * available and decides which one (if any) to use. It will set 'newurl' if
+   * an auth method was picked. */
   result = Curl_http_auth_act(data);
   if(result)
     goto out;
 
   if(k->httpcode >= 300) {
-    if(!data->req.authneg && !conn->bits.close &&
-       !Curl_creader_will_rewind(data)) {
-      /*
-       * General treatment of errors when about to send data. Including :
-       * "417 Expectation Failed", while waiting for 100-continue.
-       *
-       * The check for close above is done because of something
-       * else has already deemed the connection to get closed then
-       * something else should have considered the big picture and we
-       * avoid this check.
-       *
-       */
-
-      switch(data->state.httpreq) {
-      case HTTPREQ_PUT:
-      case HTTPREQ_POST:
-      case HTTPREQ_POST_FORM:
-      case HTTPREQ_POST_MIME:
-        /* We got an error response. If this happened before the whole
-         * request body has been sent we stop sending and mark the
-         * connection for closure after we have read the entire response.
-         */
-        if(!Curl_req_done_sending(data)) {
-          if((k->httpcode == 417) && http_exp100_is_selected(data)) {
-            /* 417 Expectation Failed - try again without the Expect
-               header */
-            if(!k->writebytecount && http_exp100_is_waiting(data)) {
-              infof(data, "Got HTTP failure 417 while waiting for a 100");
-            }
-            else {
-              infof(data, "Got HTTP failure 417 while sending data");
-              streamclose(conn, "Stop sending data before everything sent");
-              result = http_perhapsrewind(data, conn);
-              if(result)
-                goto out;
-            }
-            data->state.disableexpect = TRUE;
-            Curl_req_abort_sending(data);
-            DEBUGASSERT(!data->req.newurl);
-            data->req.newurl = Curl_bufref_dup(&data->state.url);
-            if(!data->req.newurl) {
-              result = CURLE_OUT_OF_MEMORY;
-              goto out;
-            }
-          }
-          else if(data->set.http_keep_sending_on_error) {
-            infof(data, "HTTP error before end of send, keep sending");
-            http_exp100_send_anyway(data);
-          }
-          else {
-            infof(data, "HTTP error before end of send, stop sending");
-            streamclose(conn, "Stop sending data before everything sent");
-            result = Curl_req_abort_sending(data);
-            if(result)
-              goto out;
-          }
-        }
-        break;
-
-      default: /* default label present to avoid compiler warnings */
-        break;
-      }
-    }
-
-    if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) {
-      /* We rewind before next send, continue sending now */
-      infof(data, "Keep sending data to get tossed away");
-      k->keepon |= KEEP_SEND;
-    }
+    result = http_handle_send_error(data);
+    if(result)
+      goto out;
   }
 
   /* If we requested a "no body", this is a good time to get
@@ -4135,11 +4197,10 @@
   if(data->req.no_body)
     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 would
-     like to call http2_handle_stream_close to properly close a
-     stream. In order to do this, we keep reading until we
-     close the stream. */
+  /* If max download size is *zero* (nothing) we already have nothing and can
+     safely return ok now! For HTTP/2, we would like to call
+     http2_handle_stream_close to properly close a stream. In order to do
+     this, we keep reading until we close the stream. */
   if((k->maxdownload == 0) && (k->httpversion_sent < 20))
     k->download_done = TRUE;
 
@@ -4147,21 +4208,18 @@
   result = http_firstwrite(data);
 
   if(!result)
-    /* This is the last response that we get for the current request.
-     * Check on the body size and determine if the response is complete.
-     */
+    /* This is the last response that we get for the current request. Check on
+     * the body size and determine if the response is complete. */
     result = http_size(data);
 
 out:
-  if(last_hd) {
+  if(last_hd)
     /* if not written yet, write it now */
-    result = Curl_1st_err(
-      result, http_write_header(data, last_hd, last_hd_len));
-  }
-  if(conn_changed) {
-    /* poke the multi handle to allow any pending pipewait to retry now */
+    result = Curl_1st_fatal(result,
+                            http_write_header(data, last_hd, last_hd_len));
+  if(conn_changed)
+    /* poke the multi handle to allow pending pipewait to retry */
     Curl_multi_connchanged(data->multi);
-  }
   return result;
 }
 
@@ -4173,7 +4231,7 @@
   CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
   int writetype;
-  DEBUGASSERT(!hd[hdlen]); /* null terminated */
+  DEBUGASSERT(!hd[hdlen]); /* null-terminated */
 
   *pconsumed = 0;
   if((0x0a == *hd) || (0x0d == *hd)) {
@@ -4534,18 +4592,18 @@
   CURLcode result;
   size_t consumed;
   char tmp = 0;
-  DEBUGASSERT(!hd[hdlen]); /* null terminated */
+  DEBUGASSERT(!hd[hdlen]); /* null-terminated */
 
   result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed);
   if(!result && is_eos) {
-    result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS),
+    result = Curl_client_write(data, (CLIENTWRITE_BODY | CLIENTWRITE_EOS),
                                &tmp, 0);
   }
   return result;
 }
 
 /*
- * HTTP protocol `write_resp` implementation. Will parse headers
+ * HTTP protocol `write_resp` implementation. Parse headers
  * when not done yet and otherwise return without consuming data.
  */
 CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
@@ -4892,7 +4950,7 @@
       infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
     }
     else {
-      scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? "https" : "http";
+      scheme = Curl_xfer_is_secure(data) ? "https" : "http";
     }
   }
 
@@ -4982,7 +5040,7 @@
 /*
  * HTTP handler interface.
  */
-static const struct Curl_protocol Curl_protocol_http = {
+const struct Curl_protocol Curl_protocol_http = {
   Curl_http_setup_conn,                 /* setup_connection */
   Curl_http,                            /* do_it */
   Curl_http_done,                       /* done */
@@ -4997,43 +5055,9 @@
   ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
   Curl_http_write_resp_hd,              /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   Curl_http_follow,                     /* follow */
 };
 
 #endif /* CURL_DISABLE_HTTP */
-
-/*
- * HTTP handler interface.
- */
-const struct Curl_scheme Curl_scheme_http = {
-  "http",                               /* scheme */
-#ifdef CURL_DISABLE_HTTP
-  ZERO_NULL,
-#else
-  &Curl_protocol_http,
-#endif
-  CURLPROTO_HTTP,                       /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_CREDSPERREQUEST |             /* flags */
-  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
-  PORT_HTTP,                            /* defport */
-};
-
-/*
- * HTTPS handler interface.
- */
-const struct Curl_scheme Curl_scheme_https = {
-  "https",                              /* scheme */
-#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_http,
-#endif
-  CURLPROTO_HTTPS,                      /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
-  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
-  PORT_HTTPS,                           /* defport */
-};
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index bb53595..6e33c00 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -37,27 +37,16 @@
   HTTPREQ_HEAD
 } Curl_HttpReq;
 
-/* When redirecting transfers. */
-typedef enum {
-  FOLLOW_NONE,  /* not used within the function, a placeholder to allow
-                   initing to this */
-  FOLLOW_FAKE,  /* only records stuff, not actually following */
-  FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
-                   redirect following */
-  FOLLOW_REDIR /* a full true redirect */
-} followtype;
-
 #define CURL_HTTP_V1x   (1 << 0)
 #define CURL_HTTP_V2x   (1 << 1)
 #define CURL_HTTP_V3x   (1 << 2)
 /* bitmask of CURL_HTTP_V* values */
 typedef unsigned char http_majors;
 
-extern const struct Curl_scheme Curl_scheme_http;
-extern const struct Curl_scheme Curl_scheme_https;
-
 #ifndef CURL_DISABLE_HTTP
 
+extern const struct Curl_protocol Curl_protocol_http;
+
 struct dynhds;
 
 struct http_negotiation {
@@ -196,14 +185,14 @@
  *
  * @returns CURLcode
  */
-CURLcode
-Curl_http_output_auth(struct Curl_easy *data,
-                      struct connectdata *conn,
-                      const char *request,
-                      Curl_HttpReq httpreq,
-                      const char *path,
-                      bool proxytunnel); /* TRUE if this is the request setting
-                                            up the proxy tunnel */
+CURLcode Curl_http_output_auth(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               const char *request,
+                               Curl_HttpReq httpreq,
+                               const char *path,
+                               bool proxytunnel); /* TRUE if this is
+                                                     the request setting up
+                                                     the proxy tunnel */
 
 /* Decode HTTP status code string. */
 CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index e1c5798..7be5abd 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -154,7 +154,7 @@
   size_t i;
   for(i = 0; i < stream->push_headers_used; i++)
     curlx_free(stream->push_headers[i]);
-  Curl_safefree(stream->push_headers);
+  curlx_safefree(stream->push_headers);
   stream->push_headers_used = 0;
 }
 
@@ -203,6 +203,7 @@
 {
   if(ctx->h2) {
     nghttp2_session_del(ctx->h2);
+    ctx->h2 = NULL;
   }
 }
 
@@ -375,6 +376,10 @@
 
   (void)cf;
   DEBUGASSERT(data);
+
+  if(!data)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
   stream = H2_STREAM_CTX(ctx, data);
   if(stream) {
     *pstream = stream;
@@ -713,8 +718,9 @@
   return second;
 }
 
-static int set_transfer_url(struct Curl_easy *data,
-                            struct curl_pushheaders *hp)
+static int set_transfer_url(struct Curl_easy *newhandle,
+                            struct curl_pushheaders *hp,
+                            struct Curl_easy *data)
 {
   const char *v;
   CURLUcode uc;
@@ -752,6 +758,13 @@
     }
   }
 
+  /* We can only allow PUSH of resource from the same origin, e.g.
+   * scheme + hostname + port */
+  if(!Curl_url_same_origin(data->state.uh, u)) {
+    rc = 1;
+    goto fail;
+  }
+
   uc = curl_url_get(u, CURLUPART_URL, &url, 0);
   if(uc)
     rc = 4;
@@ -760,7 +773,7 @@
   if(rc)
     return rc;
 
-  Curl_bufref_set(&data->state.url, url, 0, curl_free);
+  Curl_bufref_set(&newhandle->state.url, url, 0, curl_free);
   return 0;
 }
 
@@ -806,7 +819,7 @@
     heads.stream = stream;
     heads.frame = frame;
 
-    rv = set_transfer_url(newhandle, &heads);
+    rv = set_transfer_url(newhandle, &heads, data);
     if(rv) {
       CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d",
                   frame->promised_stream_id, rv);
@@ -863,7 +876,7 @@
                                               newstream->id,
                                               newhandle);
     if(rv) {
-      infof(data, "failed to set user_data for stream %u",
+      infof(data, "failed to set user_data for stream %d",
             newstream->id);
       DEBUGASSERT(0);
       discard_newhandle(cf, newhandle);
@@ -1010,7 +1023,7 @@
     Curl_multi_mark_dirty(data);
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
+    if(CURL_REQ_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
       /* need more data, force processing of transfer */
       Curl_multi_mark_dirty(data);
     }
@@ -1099,7 +1112,7 @@
       memcpy(scratch, frame->goaway.opaque_data, len);
     scratch[len] = '\0';
     return curl_msnprintf(buffer, blen,
-                          "FRAME[GOAWAY, error=%d, reason='%s', "
+                          "FRAME[GOAWAY, error=%u, reason='%s', "
                           "last_stream=%d]", frame->goaway.error_code,
                           scratch, frame->goaway.last_stream_id);
   }
@@ -1134,7 +1147,7 @@
   if((frame->hd.type == NGHTTP2_GOAWAY) && !ctx->sent_goaway) {
     /* A GOAWAY not initiated by us, but by nghttp2 itself on detecting
      * a protocol error on the connection */
-    failf(data, "nghttp2 shuts down connection with error %d: %s",
+    failf(data, "nghttp2 shuts down connection with error %u: %s",
           frame->goaway.error_code,
           nghttp2_http2_strerror(frame->goaway.error_code));
   }
@@ -1172,7 +1185,7 @@
           session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
         ctx->enable_push = nghttp2_session_get_remote_settings(
           session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
-        CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d",
+        CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %u",
                     ctx->max_concurrent_streams);
         CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s",
                     ctx->enable_push ? "TRUE" : "false");
@@ -1187,7 +1200,7 @@
          * 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(CURL_WANT_SEND(data))
+        if(CURL_REQ_WANT_SEND(data))
           Curl_multi_mark_dirty(data);
       }
       break;
@@ -1197,7 +1210,7 @@
       ctx->goaway_error = frame->goaway.error_code;
       ctx->remote_max_sid = frame->goaway.last_stream_id;
       if(data) {
-        infof(data, "received GOAWAY, error=%u, last_stream=%u",
+        infof(data, "received GOAWAY, error=%u, last_stream=%d",
               ctx->goaway_error, ctx->remote_max_sid);
         Curl_multi_connchanged(data->multi);
       }
@@ -1327,7 +1340,7 @@
     stream->reset = TRUE;
 
   if(stream->error)
-    CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
+    CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %u)",
               stream_id, nghttp2_http2_strerror(error_code), error_code);
   else
     CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
@@ -1336,7 +1349,7 @@
   /* remove `data_s` from the nghttp2 stream */
   rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
   if(rv) {
-    infof(data_s, "http/2: failed to clear user_data for stream %u",
+    infof(data_s, "http/2: failed to clear user_data for stream %d",
           stream_id);
     DEBUGASSERT(0);
   }
@@ -1474,8 +1487,11 @@
       stream->push_headers = headp;
     }
     h = curl_maprintf("%s:%s", name, value);
-    if(h)
-      stream->push_headers[stream->push_headers_used++] = h;
+    if(!h) {
+      free_push_headers(stream);
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
+    stream->push_headers[stream->push_headers_used++] = h;
     return 0;
   }
 
@@ -1505,7 +1521,7 @@
       cf_h2_header_error(cf, data_s, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     }
-    hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
+    hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
                           stream->status_code);
     result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
     if(result) {
@@ -1692,9 +1708,9 @@
         stream->close_handled = TRUE;
         return CURLE_OK;
     }
-    failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32
-          " %s)", stream->id, stream->reset_by_server ? "server" : "curl",
-           stream->error, nghttp2_http2_strerror(stream->error));
+    failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)",
+          stream->id, stream->reset_by_server ? "server" : "curl",
+          stream->error, nghttp2_http2_strerror(stream->error));
     return stream->error ? CURLE_HTTP2_STREAM :
            (data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2);
   }
@@ -1717,8 +1733,8 @@
         break;
       curlx_dyn_reset(&dbuf);
       result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
-                            (int)e->namelen, e->name,
-                            (int)e->valuelen, e->value);
+                              (int)e->namelen, e->name,
+                              (int)e->valuelen, e->value);
       if(result)
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
@@ -1817,7 +1833,7 @@
   /* Defer flushing during the connect phase so that the SETTINGS and
    * other initial frames are sent together with the first request.
    * Unless we are 'connect_only' where the request will never come. */
-  if(!cf->connected && !cf->conn->connect_only)
+  if(!cf->connected && !cf->conn->bits.connect_only)
     return CURLE_OK;
   return nw_out_flush(cf, data);
 }
@@ -1940,10 +1956,15 @@
                            char *buf, size_t len, size_t *pnread)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
+  struct h2_stream_ctx *stream;
   CURLcode result, r2;
   struct cf_call_data save;
 
+  if(!data)
+    return CURLE_HTTP2;
+
+  stream = H2_STREAM_CTX(ctx, data);
+
   *pnread = 0;
   if(!stream) {
     /* Abnormal call sequence: either this transfer has never opened a stream
@@ -1986,7 +2007,7 @@
     /* pending data to send, need to be called again. Ideally, we
      * monitor the socket for POLLOUT, but when not SENDING
      * any more, we force processing of the transfer. */
-    if(!CURL_WANT_SEND(data))
+    if(!CURL_REQ_WANT_SEND(data))
       Curl_multi_mark_dirty(data);
   }
   else if(r2) {
@@ -2031,7 +2052,7 @@
       return CURLE_OK;
     }
     /* Server closed before we got a response, this is an error */
-    infof(data, "stream %u closed", stream->id);
+    infof(data, "stream %d closed", stream->id);
     return CURLE_SEND_ERROR;
   }
 
@@ -2131,7 +2152,7 @@
   }
 
   if(stream_id < 0) {
-    CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
+    CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%d",
                 nghttp2_strerror(stream_id), stream_id);
     result = CURLE_SEND_ERROR;
     goto out;
@@ -2180,7 +2201,7 @@
 out:
   CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu",
               stream ? stream->id : -1, result, *pnwritten);
-  Curl_safefree(nva);
+  curlx_safefree(nva);
   *pstream = stream;
   Curl_dynhds_free(&h2_headers);
   return result;
@@ -2231,7 +2252,7 @@
   /* if the stream has been closed in egress handling (nghttp2 does that
    * when it does not like the headers, for example */
   if(stream && stream->closed) {
-    infof(data, "stream %u closed", stream->id);
+    infof(data, "stream %d closed", stream->id);
     result = CURLE_SEND_ERROR;
     goto out;
   }
@@ -2440,7 +2461,7 @@
     rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
                                               data);
     if(rc) {
-      infof(data, "http/2: failed to set user_data for stream %u",
+      infof(data, "http/2: failed to set user_data for stream %d",
             stream->id);
       DEBUGASSERT(0);
     }
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h
index a14c848..e38dc57 100644
--- a/Utilities/cmcurl/lib/http2.h
+++ b/Utilities/cmcurl/lib/http2.h
@@ -70,7 +70,6 @@
 
 #define Curl_http2_request_upgrade(x, y)  CURLE_UNSUPPORTED_PROTOCOL
 #define Curl_http2_switch(a)              CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_upgrade(a, b, c, d, e) CURLE_UNSUPPORTED_PROTOCOL
 #define Curl_h2_http_1_1_error(x)         0
 #endif
 
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 492be1a..55efa12 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -679,6 +679,10 @@
   return compare;
 }
 
+/* @unittest 1979 */
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+                             struct dynbuf *new_path,
+                             bool do_uri_encode);
 UNITTEST CURLcode canon_path(const char *q, size_t len,
                              struct dynbuf *new_path,
                              bool do_uri_encode)
@@ -705,6 +709,8 @@
   return result;
 }
 
+/* @unittest 1980 */
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq);
 UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
 {
   CURLcode result = CURLE_OK;
@@ -1107,8 +1113,8 @@
   Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
                    curlx_str(&provider0), curlx_strlen(&provider0));
 
-  curlx_free(data->state.aptr.userpwd);
-  data->state.aptr.userpwd = auth_headers;
+  curlx_free(data->req.userpwd);
+  data->req.userpwd = auth_headers;
   data->state.authhost.done = TRUE;
   result = CURLE_OK;
 
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h
index 25009c6..7dd0024 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.h
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.h
@@ -31,10 +31,4 @@
 /* this is for creating aws_sigv4 header output */
 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data);
 
-#ifdef UNITTESTS
-UNITTEST CURLcode canon_path(const char *q, size_t len,
-                             struct dynbuf *new_path, bool normalize);
-UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq);
-#endif
-
 #endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c
index db650b1..aa45c79 100644
--- a/Utilities/cmcurl/lib/http_chunks.c
+++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -258,12 +258,12 @@
             size_t trlen = curlx_dyn_len(&ch->trailer);
             if(cw_next)
               result = Curl_cwriter_write(data, cw_next,
-                                          CLIENTWRITE_HEADER|
+                                          CLIENTWRITE_HEADER |
                                           CLIENTWRITE_TRAILER,
                                           tr, trlen);
             else
               result = Curl_client_write(data,
-                                         CLIENTWRITE_HEADER|
+                                         CLIENTWRITE_HEADER |
                                          CLIENTWRITE_TRAILER,
                                          tr, trlen);
             if(result) {
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index f5b20c5..b700707 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -91,7 +91,7 @@
     return CURLE_NOT_BUILT_IN;
 #else
     digest = &data->state.proxydigest;
-    allocuserpwd = &data->state.aptr.proxyuserpwd;
+    allocuserpwd = &data->req.proxyuserpwd;
     userp = data->state.aptr.proxyuser;
     passwdp = data->state.aptr.proxypasswd;
     authp = &data->state.authproxy;
@@ -99,13 +99,13 @@
   }
   else {
     digest = &data->state.digest;
-    allocuserpwd = &data->state.aptr.userpwd;
+    allocuserpwd = &data->req.userpwd;
     userp = data->state.aptr.user;
     passwdp = data->state.aptr.passwd;
     authp = &data->state.authhost;
   }
 
-  Curl_safefree(*allocuserpwd);
+  curlx_safefree(*allocuserpwd);
 
   /* not set means empty */
   if(!userp)
@@ -125,7 +125,7 @@
     return CURLE_OK;
   }
 
-  /* So IE browsers < v7 cut off the URI part at the query part when they
+  /* IE browsers < v7 cut off the URI part at the query part when they
      evaluate the MD5 and some (IIS?) servers work with them so we may need to
      do the Digest IE-style. Note that the different ways cause different MD5
      sums to get sent.
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c
index a6e5c19..74d63d6 100644
--- a/Utilities/cmcurl/lib/http_negotiate.c
+++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -217,13 +217,13 @@
 
     if(proxy) {
 #ifndef CURL_DISABLE_PROXY
-      curlx_free(data->state.aptr.proxyuserpwd);
-      data->state.aptr.proxyuserpwd = userp;
+      curlx_free(data->req.proxyuserpwd);
+      data->req.proxyuserpwd = userp;
 #endif
     }
     else {
-      curlx_free(data->state.aptr.userpwd);
-      data->state.aptr.userpwd = userp;
+      curlx_free(data->req.userpwd);
+      data->req.userpwd = userp;
     }
 
     curlx_free(base64);
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c
index c4dbdf9..82b0505 100644
--- a/Utilities/cmcurl/lib/http_ntlm.c
+++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -139,7 +139,7 @@
 
   if(proxy) {
 #ifndef CURL_DISABLE_PROXY
-    allocuserpwd = &data->state.aptr.proxyuserpwd;
+    allocuserpwd = &data->req.proxyuserpwd;
     userp = data->state.aptr.proxyuser;
     passwdp = data->state.aptr.proxypasswd;
     service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
@@ -152,7 +152,7 @@
 #endif
   }
   else {
-    allocuserpwd = &data->state.aptr.userpwd;
+    allocuserpwd = &data->req.userpwd;
     userp = data->state.aptr.user;
     passwdp = data->state.aptr.passwd;
     service = data->set.str[STRING_SERVICE_NAME] ?
@@ -176,9 +176,9 @@
 #ifdef USE_WINDOWS_SSPI
   if(!Curl_pSecFn) {
     /* not thread-safe and leaks - use curl_global_init() to avoid */
-    CURLcode err = Curl_sspi_global_init();
+    result = Curl_sspi_global_init();
     if(!Curl_pSecFn)
-      return err;
+      return result;
   }
 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
   ntlm->sslContext = conn->sslContext;
@@ -242,7 +242,7 @@
       data->info.proxyauthpicked = CURLAUTH_NTLM;
     else
       data->info.httpauthpicked = CURLAUTH_NTLM;
-    Curl_safefree(*allocuserpwd);
+    curlx_safefree(*allocuserpwd);
     authp->done = TRUE;
     break;
   }
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index a9521c9..a4bdd7e 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -164,7 +164,7 @@
 
 void Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
                                      const char **phostname,
-                                     int *pport, bool *pipv6_ip)
+                                     uint16_t *pport, bool *pipv6_ip)
 {
   DEBUGASSERT(cf);
   DEBUGASSERT(cf->conn);
@@ -183,10 +183,7 @@
   else
     *pport = cf->conn->remote_port;
 
-  if(*phostname != cf->conn->host.name)
-    *pipv6_ip = (strchr(*phostname, ':') != NULL);
-  else
-    *pipv6_ip = (bool)cf->conn->bits.ipv6_ip;
+  *pipv6_ip = (strchr(*phostname, ':') != NULL);
 }
 
 struct cf_proxy_ctx {
@@ -202,14 +199,14 @@
   struct cf_proxy_ctx *ctx = cf->ctx;
   const char *hostname = NULL;
   char *authority = NULL;
-  int port;
+  uint16_t port;
   bool ipv6_ip;
   CURLcode result;
   struct httpreq *req = NULL;
 
   Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
 
-  authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname,
+  authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname,
                             ipv6_ip ? "]" : "", port);
   if(!authority) {
     result = CURLE_OUT_OF_MEMORY;
@@ -236,9 +233,9 @@
       goto out;
   }
 
-  if(data->state.aptr.proxyuserpwd) {
+  if(data->req.proxyuserpwd) {
     result = Curl_dynhds_h1_cadd_line(&req->headers,
-                                      data->state.aptr.proxyuserpwd);
+                                      data->req.proxyuserpwd);
     if(result)
       goto out;
   }
@@ -394,7 +391,7 @@
 
 struct Curl_cftype Curl_cft_http_proxy = {
   "HTTP-PROXY",
-  CF_TYPE_IP_CONNECT | CF_TYPE_PROXY,
+  CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP,
   0,
   http_proxy_cf_destroy,
   http_proxy_cf_connect,
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h
index c27787b..155b222 100644
--- a/Utilities/cmcurl/lib/http_proxy.h
+++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -37,7 +37,7 @@
 
 void Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
                                      const char **phostname,
-                                     int *pport, bool *pipv6_ip);
+                                     uint16_t *pport, bool *pipv6_ip);
 
 CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
                                         struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/httpsrr.c b/Utilities/cmcurl/lib/httpsrr.c
index 169a0b8..90fcb52 100644
--- a/Utilities/cmcurl/lib/httpsrr.c
+++ b/Utilities/cmcurl/lib/httpsrr.c
@@ -30,6 +30,7 @@
 #include "connect.h"
 #include "curl_trc.h"
 #include "curlx/strdup.h"
+#include "curlx/inet_ntop.h"
 
 static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len,
                                     unsigned char *alpns)
@@ -68,71 +69,164 @@
   return CURLE_OK;
 }
 
-CURLcode Curl_httpsrr_set(struct Curl_easy *data,
-                          struct Curl_https_rrinfo *hi,
+#ifdef CURLVERBOSE
+
+static CURLcode httpsrr_print_addr(struct dynbuf *dyn,
+                                   int ai_family,
+                                   const uint8_t *addr,
+                                   size_t total_len)
+{
+  char buf[MAX_IPADR_LEN];
+  size_t i, alen = (ai_family == AF_INET6) ? 16 : 4;
+  const char *sep = "";
+  CURLcode result = CURLE_OK;
+
+  for(i = 0; (i < (total_len / alen)) && !result; ++i) {
+    if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf)))
+      result = curlx_dyn_add(dyn, "<error parsing address>");
+    else
+      result = curlx_dyn_addf(dyn, "%s%s", sep, buf);
+    sep = ",";
+  }
+  return result;
+}
+
+void Curl_httpsrr_trace(struct Curl_easy *data,
+                        struct Curl_https_rrinfo *rr)
+{
+  struct dynbuf tmp;
+  CURLcode result;
+
+  if(!rr || !rr->complete) {
+    CURL_TRC_DNS(data, "[HTTPS-RR] not available");
+    return;
+  }
+  curlx_dyn_init(&tmp, 1024);
+  result = Curl_httpsrr_print(&tmp, rr);
+  if(!result)
+    CURL_TRC_DNS(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp));
+  else
+    CURL_TRC_DNS(data, "Error printing HTTPS-RR information");
+  curlx_dyn_free(&tmp);
+}
+
+CURLcode Curl_httpsrr_print(struct dynbuf *tmp,
+                            struct Curl_https_rrinfo *rr)
+{
+  CURLcode result;
+  int i;
+
+  curlx_dyn_reset(tmp);
+  result = curlx_dyn_addf(tmp, "%u %s", rr->priority,
+                          rr->target ? rr->target : ".");
+  if(!result && rr->mandatory)
+    result = curlx_dyn_add(tmp, " mandatory-keys(ignored)");
+  if(!result && rr->alpns[0]) {
+    const char *sep = "", *name;
+    result = curlx_dyn_add(tmp, " alpn=");
+    for(i = 0; !result && (i < 4); ++i) {
+      switch(rr->alpns[i]) {
+      case ALPN_h1:
+        name = "http/1.1";
+        break;
+      case ALPN_h2:
+        name = "h2";
+        break;
+      case ALPN_h3:
+        name = "h3";
+        break;
+      default:
+        name = NULL;
+      }
+      if(name) {
+        result = curlx_dyn_addf(tmp, "%s%s", sep, name);
+        sep = ",";
+      }
+    }
+  }
+  if(!result && rr->port_set) {
+    result = curlx_dyn_addf(tmp, " port=%u", rr->port);
+  }
+  if(!result && rr->no_def_alpn)
+    result = curlx_dyn_add(tmp, " no-default-alpn");
+  if(!result && rr->ipv6hints_len) {
+    result = curlx_dyn_add(tmp, " ipv6hint=");
+    if(!result)
+      result = httpsrr_print_addr(
+        tmp, AF_INET6, rr->ipv6hints, rr->ipv6hints_len);
+  }
+  if(!result && rr->ipv4hints_len) {
+    result = curlx_dyn_add(tmp, " ipv4hint=");
+    if(!result)
+      result = httpsrr_print_addr(
+        tmp, AF_INET, rr->ipv4hints, rr->ipv4hints_len);
+  }
+  if(!result && rr->echconfiglist_len)
+    result = curlx_dyn_addf(tmp, " ech=<%zu bytes>", rr->echconfiglist_len);
+
+  return result;
+}
+
+#endif /* CURLVERBOSE */
+
+CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr,
                           uint16_t rrkey, const uint8_t *val, size_t vlen)
 {
   CURLcode result = CURLE_OK;
   switch(rrkey) {
   case HTTPS_RR_CODE_MANDATORY:
-    CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement");
+    rr->mandatory = TRUE;
     break;
   case HTTPS_RR_CODE_ALPN: /* str_list */
-    result = httpsrr_decode_alpn(val, vlen, hi->alpns);
-    CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u",
-                 hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]);
+    result = httpsrr_decode_alpn(val, vlen, rr->alpns);
     break;
   case HTTPS_RR_CODE_NO_DEF_ALPN:
     if(vlen) /* no data */
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    hi->no_def_alpn = TRUE;
-    CURL_TRC_DNS(data, "HTTPS RR no-def-alpn");
+    rr->no_def_alpn = TRUE;
     break;
   case HTTPS_RR_CODE_IPV4: /* addr4 list */
     if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    curlx_free(hi->ipv4hints);
-    hi->ipv4hints = curlx_memdup(val, vlen);
-    if(!hi->ipv4hints)
+    curlx_free(rr->ipv4hints);
+    rr->ipv4hints = curlx_memdup(val, vlen);
+    if(!rr->ipv4hints)
       return CURLE_OUT_OF_MEMORY;
-    hi->ipv4hints_len = vlen;
-    CURL_TRC_DNS(data, "HTTPS RR IPv4");
+    rr->ipv4hints_len = vlen;
     break;
   case HTTPS_RR_CODE_ECH:
     if(!vlen)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    curlx_free(hi->echconfiglist);
-    hi->echconfiglist = curlx_memdup(val, vlen);
-    if(!hi->echconfiglist)
+    curlx_free(rr->echconfiglist);
+    rr->echconfiglist = curlx_memdup(val, vlen);
+    if(!rr->echconfiglist)
       return CURLE_OUT_OF_MEMORY;
-    hi->echconfiglist_len = vlen;
-    CURL_TRC_DNS(data, "HTTPS RR ECH");
+    rr->echconfiglist_len = vlen;
     break;
   case HTTPS_RR_CODE_IPV6: /* addr6 list */
     if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    curlx_free(hi->ipv6hints);
-    hi->ipv6hints = curlx_memdup(val, vlen);
-    if(!hi->ipv6hints)
+    curlx_free(rr->ipv6hints);
+    rr->ipv6hints = curlx_memdup(val, vlen);
+    if(!rr->ipv6hints)
       return CURLE_OUT_OF_MEMORY;
-    hi->ipv6hints_len = vlen;
-    CURL_TRC_DNS(data, "HTTPS RR IPv6");
+    rr->ipv6hints_len = vlen;
     break;
   case HTTPS_RR_CODE_PORT:
     if(vlen != 2)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    hi->port = (unsigned short)((val[0] << 8) | val[1]);
-    CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port);
+    rr->port = (uint16_t)((val[0] << 8) | val[1]);
+    rr->port_set = TRUE;
     break;
   default:
-    CURL_TRC_DNS(data, "HTTPS RR unknown code");
+    /* unknown code */
     break;
   }
   return result;
 }
 
-struct Curl_https_rrinfo *
-Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo)
+struct Curl_https_rrinfo *Curl_httpsrr_dup_move(
+  struct Curl_https_rrinfo *rrinfo)
 {
   struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo));
   if(dup)
@@ -142,17 +236,27 @@
 
 void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo)
 {
-  Curl_safefree(rrinfo->target);
-  Curl_safefree(rrinfo->echconfiglist);
-  Curl_safefree(rrinfo->ipv4hints);
-  Curl_safefree(rrinfo->ipv6hints);
-  Curl_safefree(rrinfo->rrname);
+  curlx_safefree(rrinfo->target);
+  curlx_safefree(rrinfo->echconfiglist);
+  curlx_safefree(rrinfo->ipv4hints);
+  curlx_safefree(rrinfo->ipv6hints);
+  curlx_safefree(rrinfo->rrname);
+  rrinfo->complete = FALSE;
+}
+
+bool Curl_httpsrr_applicable(struct Curl_easy *data,
+                             const struct Curl_https_rrinfo *rr)
+{
+  if(!data->conn || !rr)
+    return FALSE;
+  return (!rr->target || !rr->target[0] ||
+          (rr->target[0] == '.' && !rr->target[1])) &&
+         (!rr->port_set || rr->port == data->conn->remote_port);
 }
 
 #ifdef USE_ARES
 
-static CURLcode httpsrr_opt(struct Curl_easy *data,
-                            const ares_dns_rr_t *rr,
+static CURLcode httpsrr_opt(const ares_dns_rr_t *rr,
                             ares_dns_rr_key_t key, size_t idx,
                             struct Curl_https_rrinfo *hinfo)
 {
@@ -161,11 +265,10 @@
   size_t len = 0;
 
   code = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
-  return Curl_httpsrr_set(data, hinfo, code, val, len);
+  return Curl_httpsrr_set(hinfo, code, val, len);
 }
 
-CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
-                                const ares_dns_record_t *dnsrec,
+CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec,
                                 struct Curl_https_rrinfo *hinfo)
 {
   CURLcode result = CURLE_OK;
@@ -188,19 +291,18 @@
         result = CURLE_OUT_OF_MEMORY;
         goto out;
       }
-      CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target);
     }
-    CURL_TRC_DNS(data, "HTTPS RR priority: %u",
-                 ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
+    hinfo->priority = ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY);
     for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
         opt++) {
-      result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
+      result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
       if(result)
         break;
     }
   }
 out:
-  Curl_safefree(hinfo->rrname);
+  hinfo->complete = !result;
+  curlx_safefree(hinfo->rrname);
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/httpsrr.h b/Utilities/cmcurl/lib/httpsrr.h
index 8fe7c0c..28a790d 100644
--- a/Utilities/cmcurl/lib/httpsrr.h
+++ b/Utilities/cmcurl/lib/httpsrr.h
@@ -35,6 +35,7 @@
 #define MAX_HTTPSRR_ALPNS     4
 
 struct Curl_easy;
+struct dynbuf;
 
 struct Curl_https_rrinfo {
   char *rrname; /* if NULL, the same as the URL hostname */
@@ -51,20 +52,26 @@
   size_t ipv6hints_len;
   unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
   /* store parsed alpnid entries in the array, end with ALPN_none */
-  int port; /* -1 means not set */
+  uint16_t port;
   uint16_t priority;
   BIT(no_def_alpn); /* keytag = 2 */
+  BIT(mandatory); /* keytag = 0 */
+  BIT(port_set); /* port value has been assigned */
+  BIT(complete); /* values have been successfully assigned */
 };
 
-CURLcode Curl_httpsrr_set(struct Curl_easy *data,
-                          struct Curl_https_rrinfo *hi,
+CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr,
                           uint16_t rrkey, const uint8_t *val, size_t vlen);
 
-struct Curl_https_rrinfo *
-Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo);
+struct Curl_https_rrinfo *Curl_httpsrr_dup_move(
+  struct Curl_https_rrinfo *rrinfo);
 
 void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo);
 
+/* TRUE if the record is applicable to the transfer and its connection. */
+bool Curl_httpsrr_applicable(struct Curl_easy *data,
+                             const struct Curl_https_rrinfo *rr);
+
 /*
  * Code points for DNS wire format SvcParams as per RFC 9460
  */
@@ -77,10 +84,19 @@
 #define HTTPS_RR_CODE_IPV6            0x06
 
 #ifdef USE_ARES
-CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
-                                const ares_dns_record_t *dnsrec,
+CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec,
                                 struct Curl_https_rrinfo *hinfo);
 #endif /* USE_ARES */
+
+#ifdef CURLVERBOSE
+CURLcode Curl_httpsrr_print(struct dynbuf *tmp,
+                            struct Curl_https_rrinfo *rr);
+void Curl_httpsrr_trace(struct Curl_easy *data,
+                        struct Curl_https_rrinfo *rr);
+#else
+#define Curl_httpsrr_trace(a, b) Curl_nop_stmt
+#endif
+
 #endif /* USE_HTTPSRR */
 
 #endif /* HEADER_CURL_HTTPSRR_H */
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c
index 2db4f6d..f2b954e 100644
--- a/Utilities/cmcurl/lib/idn.c
+++ b/Utilities/cmcurl/lib/idn.c
@@ -33,10 +33,10 @@
 #include <idn2.h>
 
 #if defined(_WIN32) && defined(UNICODE)
-#define IDN2_LOOKUP(name, host, flags)                                  \
+#define IDN2_LOOKUP(name, host, flags)                           \
   idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
 #else
-#define IDN2_LOOKUP(name, host, flags)                          \
+#define IDN2_LOOKUP(name, host, flags)                         \
   idn2_lookup_ul((const char *)(name), (char **)(host), flags)
 #endif
 #endif /* USE_LIBIDN2 */
@@ -348,7 +348,7 @@
  */
 void Curl_free_idnconverted_hostname(struct hostname *host)
 {
-  Curl_safefree(host->encalloc);
+  curlx_safefree(host->encalloc);
 }
 
 #endif /* USE_IDN */
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c
index 05e3f84..b71254a 100644
--- a/Utilities/cmcurl/lib/if2ip.c
+++ b/Utilities/cmcurl/lib/if2ip.c
@@ -210,13 +210,13 @@
   memcpy(req.ifr_name, interf, len + 1);
   req.ifr_addr.sa_family = AF_INET;
 
-#if defined(__GNUC__) && defined(_AIX)
+#if defined(CURL_HAVE_DIAG) && defined(_AIX)
 /* Suppress warning inside system headers */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wshift-sign-overflow"
 #endif
   if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
-#if defined(__GNUC__) && defined(_AIX)
+#if defined(CURL_HAVE_DIAG) && defined(_AIX)
 #pragma GCC diagnostic pop
 #endif
     sclose(dummy);
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index 0546595..5ef2a2c 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -775,7 +775,8 @@
   char *mailbox;
 
   /* Invalidate old information as we are switching mailboxes */
-  Curl_safefree(imapc->mailbox);
+  curlx_safefree(imapc->mailbox);
+  imapc->mb_uidvalidity_set = FALSE;
 
   /* Check we have a mailbox */
   if(!imap->mailbox) {
@@ -1024,7 +1025,6 @@
                                            imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
 
   (void)instate;
@@ -1076,7 +1076,7 @@
       line += wordlen;
     }
   }
-  else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+  else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) {
     /* PREAUTH is not compatible with STARTTLS. */
     if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
       /* Switch to TLS connection now */
@@ -1452,8 +1452,8 @@
       if(result)
         return result;
 
-      infof(data, "Written %zu bytes, %" FMT_OFF_TU
-            " bytes are left for transfer", chunk, size - chunk);
+      infof(data, "Written %zu bytes, %" FMT_OFF_T
+            " bytes are left for transfer", chunk, (curl_off_t)(size - chunk));
 
       /* Have we used the entire overflow or part of it?*/
       if(pp->overflow > chunk) {
@@ -1696,14 +1696,15 @@
 
 static void imap_easy_reset(struct IMAP *imap)
 {
-  Curl_safefree(imap->mailbox);
-  Curl_safefree(imap->uid);
-  Curl_safefree(imap->mindex);
-  Curl_safefree(imap->section);
-  Curl_safefree(imap->partial);
-  Curl_safefree(imap->query);
-  Curl_safefree(imap->custom);
-  Curl_safefree(imap->custom_params);
+  curlx_safefree(imap->mailbox);
+  curlx_safefree(imap->uid);
+  curlx_safefree(imap->mindex);
+  curlx_safefree(imap->section);
+  curlx_safefree(imap->partial);
+  curlx_safefree(imap->query);
+  curlx_safefree(imap->custom);
+  curlx_safefree(imap->custom_params);
+  imap->uidvalidity_set = FALSE;
   /* Clear the transfer mode for the next request */
   imap->transfer = PPTRANSFER_BODY;
 }
@@ -2249,7 +2250,7 @@
   (void)klen;
   Curl_pp_disconnect(&imapc->pp);
   curlx_dyn_free(&imapc->dyn);
-  Curl_safefree(imapc->mailbox);
+  curlx_safefree(imapc->mailbox);
   curlx_free(imapc);
 }
 
@@ -2302,7 +2303,7 @@
 /*
  * IMAP protocol.
  */
-static const struct Curl_protocol Curl_protocol_imap = {
+const struct Curl_protocol Curl_protocol_imap = {
   imap_setup_connection,            /* setup_connection */
   imap_do,                          /* do_it */
   imap_done,                        /* done */
@@ -2317,44 +2318,9 @@
   imap_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp_hd */
-  ZERO_NULL,                        /* connection_check */
+  ZERO_NULL,                        /* connection_is_dead */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* follow */
 };
 
 #endif /* CURL_DISABLE_IMAP */
-
-/*
- * IMAP protocol handler.
- */
-const struct Curl_scheme Curl_scheme_imap = {
-  "imap",                           /* scheme */
-#ifdef CURL_DISABLE_IMAP
-  ZERO_NULL,
-#else
-  &Curl_protocol_imap,
-#endif
-  CURLPROTO_IMAP,                   /* protocol */
-  CURLPROTO_IMAP,                   /* family */
-  PROTOPT_CLOSEACTION |             /* flags */
-  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE |
-  PROTOPT_CONN_REUSE,
-  PORT_IMAP,                        /* defport */
-};
-
-/*
- * IMAPS protocol handler.
- */
-const struct Curl_scheme Curl_scheme_imaps = {
-  "imaps",                          /* scheme */
-#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_imap,
-#endif
-  CURLPROTO_IMAPS,                  /* protocol */
-  CURLPROTO_IMAP,                   /* family */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
-  PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
-  PORT_IMAPS,                       /* defport */
-};
diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h
index f179991..ecfb1ec 100644
--- a/Utilities/cmcurl/lib/imap.h
+++ b/Utilities/cmcurl/lib/imap.h
@@ -23,8 +23,6 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_imap;
-extern const struct Curl_scheme Curl_scheme_imaps;
 
 /* Authentication type flags */
 #define IMAP_TYPE_CLEARTEXT (1 << 0)
@@ -34,4 +32,8 @@
 #define IMAP_TYPE_NONE      0
 #define IMAP_TYPE_ANY       (IMAP_TYPE_CLEARTEXT | IMAP_TYPE_SASL)
 
+#ifndef CURL_DISABLE_IMAP
+extern const struct Curl_protocol Curl_protocol_imap;
+#endif
+
 #endif /* HEADER_CURL_IMAP_H */
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index e223078..16c93ee 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -27,7 +27,7 @@
 
 #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -115,7 +115,7 @@
 
 static curl_ldap_num_t ldap_url_parse_low(struct Curl_easy *data,
                                           const struct connectdata *conn,
-                                          LDAPURLDesc **ludp);
+                                          LDAPURLDesc **ludpp);
 static void ldap_free_urldesc_low(LDAPURLDesc *ludp);
 
 #undef ldap_free_urldesc
@@ -185,8 +185,8 @@
   }
 
   if(method && user && passwd) {
-    CURLcode res = Curl_create_sspi_identity(user, passwd, &cred);
-    if(!res) {
+    CURLcode result = Curl_create_sspi_identity(user, passwd, &cred);
+    if(!result) {
       rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method);
       Curl_sspi_free_identity(&cred);
     }
@@ -303,9 +303,8 @@
   if(ldap_ssl)
     server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1);
   else
-#else
-    server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port);
 #endif
+    server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port);
   if(!server) {
     failf(data, "LDAP: cannot setup connect to %s:%u",
           conn->host.dispname, ipquad.remote_port);
@@ -388,7 +387,6 @@
   }
 
 #ifdef USE_WIN32_LDAP
-  ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto);
   rc = ldap_win_bind(data, server, user, passwd);
 #else
   rc = ldap_simple_bind_s(server, user, passwd);
@@ -991,45 +989,13 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
 
 #endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
-
-/*
- * LDAP
- */
-const struct Curl_scheme Curl_scheme_ldap = {
-  "ldap",                               /* scheme */
-#ifdef CURL_DISABLE_LDAP
-  ZERO_NULL,
-#else
-  &Curl_protocol_ldap,
-#endif
-  CURLPROTO_LDAP,                       /* protocol */
-  CURLPROTO_LDAP,                       /* family */
-  PROTOPT_SSL_REUSE,                    /* flags */
-  PORT_LDAP,                            /* defport */
-};
-
-/*
- * LDAPS
- */
-const struct Curl_scheme Curl_scheme_ldaps = {
-  "ldaps",                              /* scheme */
-#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_ldap,
-#endif
-  CURLPROTO_LDAPS,                      /* protocol */
-  CURLPROTO_LDAP,                       /* family */
-  PROTOPT_SSL,                          /* flags */
-  PORT_LDAPS,                           /* defport */
-};
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c
index 0e3084b..3ec85e4 100644
--- a/Utilities/cmcurl/lib/llist.c
+++ b/Utilities/cmcurl/lib/llist.c
@@ -40,7 +40,7 @@
 #define VERIFYNODE(x) x
 #endif
 /*
- * @unittest: 1300
+ * @unittest 1300
  */
 void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor)
 {
@@ -62,7 +62,7 @@
  *
  * The 'ne' argument should be a pointer into the object to store.
  *
- * @unittest: 1300
+ * @unittest 1300
  */
 void Curl_llist_insert_next(struct Curl_llist *list,
                             struct Curl_llist_node *e, /* may be NULL */
@@ -112,7 +112,7 @@
  *
  * The 'ne' argument should be a pointer into the object to store.
  *
- * @unittest: 1300
+ * @unittest 1300
  */
 void Curl_llist_append(struct Curl_llist *list, const void *p,
                        struct Curl_llist_node *ne)
@@ -168,11 +168,7 @@
   return ptr;
 }
 
-/*
- * @unittest: 1300
- */
-UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user);
-UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user)
+static void node_uremove(struct Curl_llist_node *e, void *user)
 {
   struct Curl_llist *list;
   void *ptr;
@@ -190,7 +186,7 @@
 
 void Curl_node_remove(struct Curl_llist_node *e)
 {
-  Curl_node_uremove(e, NULL);
+  node_uremove(e, NULL);
 }
 
 void Curl_llist_destroy(struct Curl_llist *list, void *user)
@@ -198,7 +194,7 @@
   if(list) {
     DEBUGASSERT(list->_init == LLISTINIT);
     while(list->_size > 0)
-      Curl_node_uremove(list->_tail, user);
+      node_uremove(list->_tail, user);
   }
 }
 
@@ -212,9 +208,13 @@
 }
 
 #ifdef UNITTESTS
-/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which
-   might be NULL */
-struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list)
+/* llist_tail() returns the last 'struct Curl_llist_node *', which might be
+   NULL
+
+   @unittest 1300
+*/
+UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list);
+UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list)
 {
   DEBUGASSERT(list);
   DEBUGASSERT(list->_init == LLISTINIT);
@@ -248,16 +248,18 @@
 }
 
 #ifdef UNITTESTS
+/* llist_node_prev() returns the previous element in a list from a given
+   Curl_llist_node
 
-/* Curl_node_prev() returns the previous element in a list from a given
-   Curl_llist_node */
-struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n)
+   @unittest 1300
+*/
+UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n);
+UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n)
 {
   DEBUGASSERT(n);
   DEBUGASSERT(n->_init == NODEINIT);
   return VERIFYNODE(n->_prev);
 }
-
 #endif
 
 struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n)
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h
index 09c695d..28e958d 100644
--- a/Utilities/cmcurl/lib/llist.h
+++ b/Utilities/cmcurl/lib/llist.h
@@ -62,10 +62,6 @@
    might be NULL */
 struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list);
 
-/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which
-   might be NULL */
-struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list);
-
 /* Curl_llist_count() returns a size_t the number of nodes in the list */
 size_t Curl_llist_count(struct Curl_llist *list);
 
@@ -73,17 +69,13 @@
 void *Curl_node_elem(struct Curl_llist_node *n);
 
 /* Remove the node from the list and return the custom data
- * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */
+ * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */
 void *Curl_node_take_elem(struct Curl_llist_node *e);
 
 /* Curl_node_next() returns the next element in a list from a given
    Curl_llist_node */
 struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n);
 
-/* Curl_node_prev() returns the previous element in a list from a given
-   Curl_llist_node */
-struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n);
-
 /* Curl_node_llist() return the list the node is in or NULL. */
 struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n);
 
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index 1ac6ef1..0213483 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -33,7 +33,7 @@
 /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
 #define OPENSSL_NO_MD4
 #else
-/* Cover also OPENSSL_NO_MD4 configured in openssl */
+/* Cover also OPENSSL_NO_MD4 configured in OpenSSL */
 #include <openssl/opensslconf.h>
 #endif
 #endif /* USE_OPENSSL */
@@ -44,19 +44,41 @@
 
 /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */
 
-#if defined(USE_WOLFSSL) && !defined(NO_MD4)
-#include <wolfssl/openssl/md4.h>
-#define VOID_MD4_INIT
+#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+#include <openssl/md4.h>
 
-#ifdef OPENSSL_COEXIST
-#  define MD4_CTX    WOLFSSL_MD4_CTX
-#  define MD4_Init   wolfSSL_MD4_Init
-#  define MD4_Update wolfSSL_MD4_Update
-#  define MD4_Final  wolfSSL_MD4_Final
+/* Map directly to OpenSSL implementation */
+#define my_md4_ctx    MD4_CTX
+#define my_md4_init   MD4_Init
+#define my_md4_update MD4_Update
+#define my_md4_final  MD4_Final
+
+#elif defined(USE_WOLFSSL) && !defined(NO_MD4)
+#include <wolfssl/wolfcrypt/md4.h>
+#include <wolfssl/version.h>
+
+#if LIBWOLFSSL_VERSION_HEX >= 0x05007006
+typedef wc_Md4 my_md4_ctx;
+#else
+typedef Md4 my_md4_ctx;
 #endif
 
-#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
-#include <openssl/md4.h>
+static int my_md4_init(my_md4_ctx *ctx)
+{
+  wc_InitMd4(ctx);
+  return 1;
+}
+
+static void my_md4_update(my_md4_ctx *ctx,
+                          const void *input, unsigned long len)
+{
+  wc_Md4Update(ctx, input, (word32)len);
+}
+
+static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx)
+{
+  wc_Md4Final(ctx, digest);
+}
 
 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
               (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \
@@ -68,19 +90,20 @@
               (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000))
 #include <CommonCrypto/CommonDigest.h>
 
-typedef CC_MD4_CTX MD4_CTX;
+typedef CC_MD4_CTX my_md4_ctx;
 
-static int MD4_Init(MD4_CTX *ctx)
+static int my_md4_init(my_md4_ctx *ctx)
 {
   return CC_MD4_Init(ctx);
 }
 
-static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len)
+static void my_md4_update(my_md4_ctx *ctx,
+                          const void *input, unsigned long len)
 {
   (void)CC_MD4_Update(ctx, input, (CC_LONG)len);
 }
 
-static void MD4_Final(unsigned char *digest, MD4_CTX *ctx)
+static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx)
 {
   (void)CC_MD4_Final(digest, ctx);
 }
@@ -92,9 +115,9 @@
   HCRYPTPROV hCryptProv;
   HCRYPTHASH hHash;
 };
-typedef struct md4_ctx MD4_CTX;
+typedef struct md4_ctx my_md4_ctx;
 
-static int MD4_Init(MD4_CTX *ctx)
+static int my_md4_init(my_md4_ctx *ctx)
 {
   ctx->hCryptProv = 0;
   ctx->hHash = 0;
@@ -112,12 +135,13 @@
   return 1;
 }
 
-static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len)
+static void my_md4_update(my_md4_ctx *ctx,
+                          const void *input, unsigned long len)
 {
   CryptHashData(ctx->hHash, (const BYTE *)input, (unsigned int)len, 0);
 }
 
-static void MD4_Final(unsigned char *digest, MD4_CTX *ctx)
+static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx)
 {
   unsigned long length = 0;
 
@@ -135,20 +159,21 @@
 #elif defined(USE_GNUTLS)
 #include <nettle/md4.h>
 
-typedef struct md4_ctx MD4_CTX;
+typedef struct md4_ctx my_md4_ctx;
 
-static int MD4_Init(MD4_CTX *ctx)
+static int my_md4_init(my_md4_ctx *ctx)
 {
   md4_init(ctx);
   return 1;
 }
 
-static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len)
+static void my_md4_update(my_md4_ctx *ctx,
+                          const void *input, unsigned long len)
 {
   md4_update(ctx, len, input);
 }
 
-static void MD4_Final(unsigned char *digest, MD4_CTX *ctx)
+static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx)
 {
   md4_digest(ctx, MD4_DIGEST_SIZE, digest);
 }
@@ -187,7 +212,7 @@
   unsigned char buffer[64];
   uint32_t block[16];
 };
-typedef struct md4_ctx MD4_CTX;
+typedef struct md4_ctx my_md4_ctx;
 
 /*
  * The basic MD4 functions.
@@ -229,7 +254,7 @@
  * This processes one or more 64-byte data blocks, but does NOT update
  * the bit counters. There are no alignment requirements.
  */
-static const void *my_md4_body(MD4_CTX *ctx,
+static const void *my_md4_body(my_md4_ctx *ctx,
                                const void *input, unsigned long size)
 {
   const unsigned char *ptr;
@@ -320,7 +345,7 @@
   return ptr;
 }
 
-static int MD4_Init(MD4_CTX *ctx)
+static int my_md4_init(my_md4_ctx *ctx)
 {
   ctx->a = 0x67452301;
   ctx->b = 0xefcdab89;
@@ -333,7 +358,8 @@
   return 1;
 }
 
-static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len)
+static void my_md4_update(my_md4_ctx *ctx,
+                          const void *input, unsigned long len)
 {
   uint32_t saved_lo;
   unsigned long used;
@@ -368,7 +394,7 @@
   memcpy(ctx->buffer, input, len);
 }
 
-static void MD4_Final(unsigned char *digest, MD4_CTX *ctx)
+static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx)
 {
   unsigned long used, available;
 
@@ -424,17 +450,11 @@
 CURLcode Curl_md4it(unsigned char *output,
                     const unsigned char *input, const size_t len)
 {
-  MD4_CTX ctx;
-
-#ifdef VOID_MD4_INIT
-  MD4_Init(&ctx);
-#else
-  if(!MD4_Init(&ctx))
+  my_md4_ctx ctx;
+  if(!my_md4_init(&ctx))
     return CURLE_FAILED_INIT;
-#endif
-
-  MD4_Update(&ctx, input, curlx_uztoui(len));
-  MD4_Final(output, &ctx);
+  my_md4_update(&ctx, input, curlx_uztoui(len));
+  my_md4_final(output, &ctx);
   return CURLE_OK;
 }
 
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index c2bd176..4dd0d7c 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -67,14 +67,9 @@
   md5_digest(ctx, 16, digest);
 }
 
-#elif (defined(USE_OPENSSL) && \
-  !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)) || \
-  (defined(USE_WOLFSSL) && !defined(NO_MD5) && !defined(OPENSSL_COEXIST))
-#ifdef USE_OPENSSL
+#elif defined(USE_OPENSSL) && \
+  !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)
 #include <openssl/md5.h>
-#else
-#include <wolfssl/openssl/md5.h>
-#endif
 
 typedef MD5_CTX my_md5_ctx;
 
@@ -98,27 +93,26 @@
 }
 
 #elif defined(USE_WOLFSSL) && !defined(NO_MD5)
-#include <wolfssl/openssl/md5.h>
+#include <wolfssl/wolfcrypt/md5.h>
 
-typedef WOLFSSL_MD5_CTX my_md5_ctx;
+typedef wc_Md5 my_md5_ctx;
 
 static CURLcode my_md5_init(void *ctx)
 {
-  if(!wolfSSL_MD5_Init(ctx))
+  if(wc_InitMd5(ctx))
     return CURLE_OUT_OF_MEMORY;
-
   return CURLE_OK;
 }
 
 static void my_md5_update(void *ctx,
                           const unsigned char *input, unsigned int len)
 {
-  (void)wolfSSL_MD5_Update(ctx, input, len);
+  (void)wc_Md5Update(ctx, input, (word32)len);
 }
 
 static void my_md5_final(unsigned char *digest, void *ctx)
 {
-  (void)wolfSSL_MD5_Final(digest, ctx);
+  (void)wc_Md5Final(ctx, digest);
 }
 
 #elif defined(USE_MBEDTLS) && \
@@ -546,14 +540,19 @@
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_md5it(unsigned char *output,
-                    const unsigned char *input, const size_t len)
+                    const unsigned char *input, size_t len)
 {
   CURLcode result;
   my_md5_ctx ctx;
 
   result = my_md5_init(&ctx);
   if(!result) {
-    my_md5_update(&ctx, input, curlx_uztoui(len));
+    do {
+      unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX);
+      my_md5_update(&ctx, input, ilen);
+      input += ilen;
+      len -= ilen;
+    } while(len);
     my_md5_final(output, &ctx);
   }
   return result;
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c
index af4fc9d..6eda7a8 100644
--- a/Utilities/cmcurl/lib/memdebug.c
+++ b/Utilities/cmcurl/lib/memdebug.c
@@ -65,14 +65,14 @@
 static char membuf[10000];
 static size_t memwidx = 0; /* write index */
 
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#ifdef USE_MUTEX
 static bool dbg_mutex_init = 0;
 static curl_mutex_t dbg_mutex;
 #endif
 
 static bool curl_dbg_lock(void)
 {
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#ifdef USE_MUTEX
   if(dbg_mutex_init) {
     Curl_mutex_acquire(&dbg_mutex);
     return TRUE;
@@ -83,7 +83,7 @@
 
 static void curl_dbg_unlock(bool was_locked)
 {
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#ifdef USE_MUTEX
   if(was_locked)
     Curl_mutex_release(&dbg_mutex);
 #else
@@ -99,6 +99,7 @@
    _exit() comes after the atexit handlers are called. curl/curl#6620 */
 static void curl_dbg_cleanup(void)
 {
+  bool locked = curl_dbg_lock();
   if(curl_dbg_logfile &&
      curl_dbg_logfile != stderr &&
      curl_dbg_logfile != stdout) {
@@ -108,7 +109,8 @@
     fclose(curl_dbg_logfile);
   }
   curl_dbg_logfile = NULL;
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+  curl_dbg_unlock(locked);
+#ifdef USE_MUTEX
   if(dbg_mutex_init) {
     Curl_mutex_destroy(&dbg_mutex);
     dbg_mutex_init = FALSE;
@@ -157,7 +159,7 @@
       setbuf(curl_dbg_logfile, (char *)NULL);
 #endif
   }
-#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#ifdef USE_MUTEX
   if(!dbg_mutex_init) {
     dbg_mutex_init = TRUE;
     Curl_mutex_init(&dbg_mutex);
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index 37ea514..b7e51aa 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -44,11 +44,14 @@
 #include "slist.h"
 #include "curlx/dynbuf.h"
 
+#define MAX_MIME_LEVELS 40 /* avoid deep nesting */
+
 #define READ_ERROR   ((size_t)-1)
 #define STOP_FILLING ((size_t)-2)
 
 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
-                                 void *instream, bool *hasread);
+                                 void *instream, bool *hasread,
+                                 size_t call_depth);
 static curl_off_t mime_size(curl_mimepart *part);
 
 /* Quoted-printable character class table.
@@ -162,19 +165,20 @@
 static FILE *vmsfopenread(const char *file, const char *mode)
 {
   curlx_struct_stat statbuf;
-  int result;
+  int res = curlx_stat(file, &statbuf);
 
-  result = curlx_stat(file, &statbuf);
-
-  switch(statbuf.st_fab_rfm) {
-  case FAB$C_VAR:
-  case FAB$C_VFC:
-  case FAB$C_STMCR:
-    return curlx_fopen(file, FOPEN_READTEXT); /* VMS */
-    break;
-  default:
-    return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
+  if(res != -1) {
+    switch(statbuf.st_fab_rfm) {
+    case FAB$C_VAR:
+    case FAB$C_VFC:
+    case FAB$C_STMCR:
+      return curlx_fopen(file, FOPEN_READTEXT); /* VMS */
+      break;
+    default:
+      return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm");
+    }
   }
+  return NULL;
 }
 
 #define fopen_read vmsfopenread
@@ -599,7 +603,7 @@
 
 static void mime_mem_free(void *ptr)
 {
-  Curl_safefree(((curl_mimepart *)ptr)->data);
+  curlx_safefree(((curl_mimepart *)ptr)->data);
 }
 
 /* Named file callbacks. */
@@ -650,7 +654,7 @@
     curlx_fclose(part->fp);
     part->fp = NULL;
   }
-  Curl_safefree(part->data);
+  curlx_safefree(part->data);
 }
 
 /* Subparts callbacks. */
@@ -686,11 +690,16 @@
 }
 
 /* Read a non-encoded part content. */
-static size_t read_part_content(curl_mimepart *part,
-                                char *buffer, size_t bufsize, bool *hasread)
+
+static size_t read_part_content(curl_mimepart *part, char *buffer,
+                                size_t bufsize, bool *hasread,
+                                size_t call_depth)
 {
   size_t sz = 0;
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   switch(part->lastreadstatus) {
   case 0:
   case CURL_READFUNC_ABORT:
@@ -713,7 +722,8 @@
        * Cannot be processed as other kinds since read function requires
        * an additional parameter and is highly recursive.
        */
-      sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread);
+      sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread,
+                              call_depth);
       break;
     case MIMEKIND_FILE:
       if(part->fp && feof(part->fp))
@@ -752,13 +762,17 @@
 
 /* Read and encode part content. */
 static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
-                                        size_t bufsize, bool *hasread)
+                                        size_t bufsize, bool *hasread,
+                                        size_t call_depth)
 {
   struct mime_encoder_state *st = &part->encstate;
   size_t cursize = 0;
   size_t sz;
   bool ateof = FALSE;
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   for(;;) {
     if(st->bufbeg < st->bufend || ateof) {
       /* Encode buffered data. */
@@ -791,7 +805,7 @@
     if(st->bufend >= sizeof(st->buf))
       return cursize ? cursize : READ_ERROR;    /* Buffer full. */
     sz = read_part_content(part, st->buf + st->bufend,
-                           sizeof(st->buf) - st->bufend, hasread);
+                           sizeof(st->buf) - st->bufend, hasread, call_depth);
     switch(sz) {
     case 0:
       ateof = TRUE;
@@ -812,12 +826,15 @@
 
 /* Readback a mime part. */
 static size_t readback_part(curl_mimepart *part,
-                            char *buffer, size_t bufsize, bool *hasread)
+                            char *buffer, size_t bufsize, bool *hasread,
+                            size_t call_depth)
 {
   size_t cursize = 0;
 
-  /* Readback from part. */
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
 
+  /* Readback from part. */
   while(bufsize) {
     size_t sz = 0;
     struct curl_slist *hdr = (struct curl_slist *)part->state.ptr;
@@ -860,9 +877,10 @@
       break;
     case MIMESTATE_CONTENT:
       if(part->encoder)
-        sz = read_encoded_part_content(part, buffer, bufsize, hasread);
+        sz = read_encoded_part_content(part, buffer, bufsize, hasread,
+                                       call_depth);
       else
-        sz = read_part_content(part, buffer, bufsize, hasread);
+        sz = read_part_content(part, buffer, bufsize, hasread, call_depth);
       switch(sz) {
       case 0:
         mimesetstate(&part->state, MIMESTATE_END, NULL);
@@ -896,12 +914,16 @@
 
 /* Readback from mime. Warning: not a read callback function. */
 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
-                                 void *instream, bool *hasread)
+                                 void *instream, bool *hasread,
+                                 size_t call_depth)
 {
   curl_mime *mime = (curl_mime *)instream;
   size_t cursize = 0;
   (void)size;  /* Always 1 */
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   while(nitems) {
     size_t sz = 0;
     curl_mimepart *part = mime->state.ptr;
@@ -936,7 +958,7 @@
         mimesetstate(&mime->state, MIMESTATE_END, NULL);
         break;
       }
-      sz = readback_part(part, buffer, nitems, hasread);
+      sz = readback_part(part, buffer, nitems, hasread, call_depth);
       switch(sz) {
       case CURL_READFUNC_ABORT:
       case CURL_READFUNC_PAUSE:
@@ -1001,7 +1023,7 @@
 {
   curl_mime *mime = (curl_mime *)instream;
   curl_mimepart *part;
-  int result = CURL_SEEKFUNC_OK;
+  int rc = CURL_SEEKFUNC_OK;
 
   if(whence != SEEK_SET || offset)
     return CURL_SEEKFUNC_CANTSEEK;    /* Only support full rewind. */
@@ -1012,13 +1034,13 @@
   for(part = mime->firstpart; part; part = part->nextpart) {
     int res = mime_part_rewind(part);
     if(res != CURL_SEEKFUNC_OK)
-      result = res;
+      rc = res;
   }
 
-  if(result == CURL_SEEKFUNC_OK)
+  if(rc == CURL_SEEKFUNC_OK)
     mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL);
 
-  return result;
+  return rc;
 }
 
 /* Release part content. */
@@ -1071,9 +1093,9 @@
     curl_slist_free_all(part->curlheaders);
     if(part->flags & MIME_USERHEADERS_OWNER)
       curl_slist_free_all(part->userheaders);
-    Curl_safefree(part->mimetype);
-    Curl_safefree(part->name);
-    Curl_safefree(part->filename);
+    curlx_safefree(part->mimetype);
+    curlx_safefree(part->name);
+    curlx_safefree(part->filename);
     Curl_mime_initpart(part);
   }
 }
@@ -1241,7 +1263,7 @@
   if(!part)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  Curl_safefree(part->name);
+  curlx_safefree(part->name);
 
   if(name) {
     part->name = curlx_strdup(name);
@@ -1258,7 +1280,7 @@
   if(!part)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  Curl_safefree(part->filename);
+  curlx_safefree(part->filename);
 
   if(filename) {
     part->filename = curlx_strdup(filename);
@@ -1350,7 +1372,7 @@
   if(!part)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  Curl_safefree(part->mimetype);
+  curlx_safefree(part->mimetype);
 
   if(mimetype) {
     part->mimetype = curlx_strdup(mimetype);
@@ -1505,7 +1527,7 @@
    * adding any data and this loops infinitely. */
   do {
     hasread = FALSE;
-    ret = readback_part(part, buffer, nitems, &hasread);
+    ret = readback_part(part, buffer, nitems, &hasread, 0);
     /*
      * If this is not possible to get some data without calling more than
      * one read callback (probably because a content encoder is not able to
@@ -1680,7 +1702,7 @@
   const char *boundary = NULL;
   char *customct;
   const char *cte = NULL;
-  CURLcode ret = CURLE_OK;
+  CURLcode result = CURLE_OK;
 
   /* Get rid of previously prepared headers. */
   curl_slist_free_all(part->curlheaders);
@@ -1742,35 +1764,35 @@
       if(part->name) {
         name = escape_string(data, part->name, strategy);
         if(!name)
-          ret = CURLE_OUT_OF_MEMORY;
+          result = CURLE_OUT_OF_MEMORY;
       }
-      if(!ret && part->filename) {
+      if(!result && part->filename) {
         filename = escape_string(data, part->filename, strategy);
         if(!filename)
-          ret = CURLE_OUT_OF_MEMORY;
+          result = CURLE_OUT_OF_MEMORY;
       }
-      if(!ret)
-        ret = Curl_mime_add_header(&part->curlheaders,
-                                   "Content-Disposition: %s%s%s%s%s%s%s",
-                                   disposition,
-                                   name ? "; name=\"" : "",
-                                   name ? name : "",
-                                   name ? "\"" : "",
-                                   filename ? "; filename=\"" : "",
-                                   filename ? filename : "",
-                                   filename ? "\"" : "");
-      Curl_safefree(name);
-      Curl_safefree(filename);
-      if(ret)
-        return ret;
+      if(!result)
+        result = Curl_mime_add_header(&part->curlheaders,
+                                      "Content-Disposition: %s%s%s%s%s%s%s",
+                                      disposition,
+                                      name ? "; name=\"" : "",
+                                      name ? name : "",
+                                      name ? "\"" : "",
+                                      filename ? "; filename=\"" : "",
+                                      filename ? filename : "",
+                                      filename ? "\"" : "");
+      curlx_safefree(name);
+      curlx_safefree(filename);
+      if(result)
+        return result;
     }
   }
 
   /* Issue Content-Type header. */
   if(contenttype) {
-    ret = add_content_type(&part->curlheaders, contenttype, boundary);
-    if(ret)
-      return ret;
+    result = add_content_type(&part->curlheaders, contenttype, boundary);
+    if(result)
+      return result;
   }
 
   /* Content-Transfer-Encoding header. */
@@ -1782,10 +1804,10 @@
             part->kind != MIMEKIND_MULTIPART)
       cte = "8bit";
     if(cte) {
-      ret = Curl_mime_add_header(&part->curlheaders,
-                                 "Content-Transfer-Encoding: %s", cte);
-      if(ret)
-        return ret;
+      result = Curl_mime_add_header(&part->curlheaders,
+                                    "Content-Transfer-Encoding: %s", cte);
+      if(result)
+        return result;
     }
   }
 
@@ -1802,13 +1824,13 @@
     if(content_type_match(contenttype, STRCONST("multipart/form-data")))
       disposition = "form-data";
     for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) {
-      ret = Curl_mime_prepare_headers(data, subpart, NULL,
-                                      disposition, strategy);
-      if(ret)
-        return ret;
+      result = Curl_mime_prepare_headers(data, subpart, NULL,
+                                         disposition, strategy);
+      if(result)
+        return result;
     }
   }
-  return ret;
+  return result;
 }
 
 /* Recursively reset paused status in the given part. */
@@ -1929,14 +1951,14 @@
   else
     nread = Curl_mime_read(buf, 1, blen, ctx->part);
 
-  CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd",
+  CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zu",
                 blen, nread);
 
   switch(nread) {
   case 0:
     if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
       failf(data, "client mime read EOF fail, "
-            "only %"FMT_OFF_T"/%"FMT_OFF_T
+            "only %" FMT_OFF_T "/%" FMT_OFF_T
             " of needed bytes read", ctx->read_len, ctx->total_len);
       return CURLE_READ_ERROR;
     }
@@ -1991,7 +2013,7 @@
   }
 
   CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T
-                ", read=%"FMT_OFF_T") -> %d, %zu, %d",
+                ", read=%" FMT_OFF_T ") -> %d, %zu, %d",
                 blen, ctx->total_len, ctx->read_len, result, *pnread, *peos);
   return result;
 }
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index c6a4a49..6eaea66 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -39,11 +39,11 @@
 /* Upper-case digits. */
 const unsigned char Curl_udigits[] = "0123456789ABCDEF";
 
-#define OUTCHAR(x)                                       \
-  do {                                                   \
-    if(stream((unsigned char)(x), userp))                \
-      return TRUE;                                       \
-    (*donep)++;                                          \
+#define OUTCHAR(x)                        \
+  do {                                    \
+    if(stream((unsigned char)(x), userp)) \
+      return TRUE;                        \
+    (*donep)++;                           \
   } while(0)
 
 /* Data type to read from the arglist */
@@ -169,6 +169,261 @@
 #define PFMT_PRECARG    10 /* attempted to use same arg twice, for prec */
 #define PFMT_MANYSEGS   11 /* maxed out output segments */
 
+static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar,
+                       int *precp, int *widthp)
+{
+  const char *fmt = *fmtp;
+  bool loopit = TRUE;
+  unsigned int flags = 0;
+  int width = 0;
+  int precision = 0;
+
+  /* Handle the flags */
+  do {
+    switch(*fmt++) {
+    case ' ':
+      flags |= FLAGS_SPACE;
+      break;
+    case '+':
+      flags |= FLAGS_SHOWSIGN;
+      break;
+    case '-':
+      flags |= FLAGS_LEFT;
+      flags &= ~(unsigned int)FLAGS_PAD_NIL;
+      break;
+    case '#':
+      flags |= FLAGS_ALT;
+      break;
+    case '.':
+      if('*' == *fmt) {
+        /* The precision is picked from a specified parameter */
+        flags |= FLAGS_PRECPARAM;
+        fmt++;
+
+        if(use_dollar == DOLLAR_USE) {
+          precision = dollarstring(fmt, &fmt);
+          if(precision < 0)
+            /* illegal combo */
+            return PFMT_DOLLARPREC;
+        }
+        else
+          /* get it from the next argument */
+          precision = -1;
+      }
+      else {
+        bool is_neg;
+        curl_off_t num;
+        flags |= FLAGS_PREC;
+        is_neg = ('-' == *fmt);
+        if(is_neg)
+          fmt++;
+        if(curlx_str_number(&fmt, &num, INT_MAX))
+          return PFMT_PREC;
+        precision = (int)num;
+        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 PFMT_PRECMIX;
+      break;
+    case 'h':
+      flags |= FLAGS_SHORT;
+      break;
+#ifdef _WIN32
+    case 'I':
+      /* Non-ANSI integer extensions I32 I64 */
+      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;
+#else
+        flags |= FLAGS_LONG;
+#endif
+      }
+      break;
+#endif /* _WIN32 */
+    case 'l':
+      if(flags & FLAGS_LONG)
+        flags |= FLAGS_LONGLONG;
+      else
+        flags |= FLAGS_LONG;
+      break;
+    case 'L':
+      flags |= FLAGS_LONGDOUBLE;
+      break;
+    case 'q':
+      flags |= FLAGS_LONGLONG;
+      break;
+    case 'z':
+      /* the code below generates a warning if -Wunreachable-code is
+         used */
+#if (SIZEOF_SIZE_T > SIZEOF_LONG)
+      flags |= FLAGS_LONGLONG;
+#else
+      flags |= FLAGS_LONG;
+#endif
+      break;
+    case 'O':
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
+      flags |= FLAGS_LONGLONG;
+#else
+      flags |= FLAGS_LONG;
+#endif
+      break;
+    case '0':
+      if(!(flags & FLAGS_LEFT))
+        flags |= FLAGS_PAD_NIL;
+      FALLTHROUGH();
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9': {
+      curl_off_t num;
+      flags |= FLAGS_WIDTH;
+      fmt--;
+      if(curlx_str_number(&fmt, &num, INT_MAX))
+        return PFMT_WIDTH;
+      width = (int)num;
+      break;
+    }
+    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(loopit); /* do */
+  *flagsp = flags;
+  *precp = precision;
+  *widthp = width;
+  *fmtp = fmt;
+  return PFMT_OK;
+}
+
+static bool parse_conversion(const char f, unsigned int *flagp,
+                             FormatType *typep)
+{
+  unsigned int flags = *flagp;
+  FormatType type;
+  switch(f) {
+  case 'S':
+    flags |= FLAGS_ALT;
+    type = MTYPE_STRING;
+    break;
+  case 's':
+    type = MTYPE_STRING;
+    break;
+  case 'n':
+    type = MTYPE_INTPTR;
+    break;
+  case 'p':
+    type = MTYPE_PTR;
+    break;
+  case 'd':
+  case 'i':
+    if(flags & FLAGS_LONGLONG)
+      type = MTYPE_LONGLONG;
+    else if(flags & FLAGS_LONG)
+      type = MTYPE_LONG;
+    else
+      type = MTYPE_INT;
+    break;
+  case 'u':
+    if(flags & FLAGS_LONGLONG)
+      type = MTYPE_LONGLONGU;
+    else if(flags & FLAGS_LONG)
+      type = MTYPE_LONGU;
+    else
+      type = MTYPE_INTU;
+    flags |= FLAGS_UNSIGNED;
+    break;
+  case 'o':
+    if(flags & FLAGS_LONGLONG)
+      type = MTYPE_LONGLONGU;
+    else if(flags & FLAGS_LONG)
+      type = MTYPE_LONGU;
+    else
+      type = MTYPE_INTU;
+    flags |= FLAGS_OCTAL | FLAGS_UNSIGNED;
+    break;
+  case 'x':
+    if(flags & FLAGS_LONGLONG)
+      type = MTYPE_LONGLONGU;
+    else if(flags & FLAGS_LONG)
+      type = MTYPE_LONGU;
+    else
+      type = MTYPE_INTU;
+    flags |= FLAGS_HEX | FLAGS_UNSIGNED;
+    break;
+  case 'X':
+    if(flags & FLAGS_LONGLONG)
+      type = MTYPE_LONGLONGU;
+    else if(flags & FLAGS_LONG)
+      type = MTYPE_LONGU;
+    else
+      type = MTYPE_INTU;
+    flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED;
+    break;
+  case 'c':
+    type = MTYPE_INT;
+    flags |= FLAGS_CHAR;
+    break;
+  case 'f':
+    type = MTYPE_DOUBLE;
+    break;
+  case 'e':
+    type = MTYPE_DOUBLE;
+    flags |= FLAGS_FLOATE;
+    break;
+  case 'E':
+    type = MTYPE_DOUBLE;
+    flags |= FLAGS_FLOATE | FLAGS_UPPER;
+    break;
+  case 'g':
+    type = MTYPE_DOUBLE;
+    flags |= FLAGS_FLOATG;
+    break;
+  case 'G':
+    type = MTYPE_DOUBLE;
+    flags |= FLAGS_FLOATG | FLAGS_UPPER;
+    break;
+  default:
+    /* invalid instruction, disregard and continue */
+    return TRUE;
+  } /* switch */
+
+  *flagp |= flags;
+  *typep = type;
+  return FALSE;
+}
+
+
 static int parsefmt(const char *format,
                     struct outsegment *out,
                     struct va_input *in,
@@ -192,12 +447,12 @@
   while(*fmt) {
     if(*fmt == '%') {
       struct va_input *iptr;
-      bool loopit = TRUE;
       FormatType type;
       unsigned int flags = 0;
       int width = 0;
       int precision = 0;
       int param = -1;
+      int rc;
       fmt++;
       outlen = (size_t)(fmt - start - 1);
       if(*fmt == '%') {
@@ -232,233 +487,12 @@
           use_dollar = DOLLAR_USE;
       }
 
-      /* Handle the flags */
-      while(loopit) {
-        switch(*fmt++) {
-        case ' ':
-          flags |= FLAGS_SPACE;
-          break;
-        case '+':
-          flags |= FLAGS_SHOWSIGN;
-          break;
-        case '-':
-          flags |= FLAGS_LEFT;
-          flags &= ~(unsigned int)FLAGS_PAD_NIL;
-          break;
-        case '#':
-          flags |= FLAGS_ALT;
-          break;
-        case '.':
-          if('*' == *fmt) {
-            /* The precision is picked from a specified parameter */
-            flags |= FLAGS_PRECPARAM;
-            fmt++;
+      rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width);
+      if(rc)
+        return rc;
 
-            if(use_dollar == DOLLAR_USE) {
-              precision = dollarstring(fmt, &fmt);
-              if(precision < 0)
-                /* illegal combo */
-                return PFMT_DOLLARPREC;
-            }
-            else
-              /* get it from the next argument */
-              precision = -1;
-          }
-          else {
-            bool is_neg;
-            curl_off_t num;
-            flags |= FLAGS_PREC;
-            is_neg = ('-' == *fmt);
-            if(is_neg)
-              fmt++;
-            if(curlx_str_number(&fmt, &num, INT_MAX))
-              return PFMT_PREC;
-            precision = (int)num;
-            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 PFMT_PRECMIX;
-          break;
-        case 'h':
-          flags |= FLAGS_SHORT;
-          break;
-#ifdef _WIN32
-        case 'I':
-          /* Non-ANSI integer extensions I32 I64 */
-          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;
-#else
-            flags |= FLAGS_LONG;
-#endif
-          }
-          break;
-#endif /* _WIN32 */
-        case 'l':
-          if(flags & FLAGS_LONG)
-            flags |= FLAGS_LONGLONG;
-          else
-            flags |= FLAGS_LONG;
-          break;
-        case 'L':
-          flags |= FLAGS_LONGDOUBLE;
-          break;
-        case 'q':
-          flags |= FLAGS_LONGLONG;
-          break;
-        case 'z':
-          /* the code below generates a warning if -Wunreachable-code is
-             used */
-#if (SIZEOF_SIZE_T > SIZEOF_LONG)
-          flags |= FLAGS_LONGLONG;
-#else
-          flags |= FLAGS_LONG;
-#endif
-          break;
-        case 'O':
-#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
-          flags |= FLAGS_LONGLONG;
-#else
-          flags |= FLAGS_LONG;
-#endif
-          break;
-        case '0':
-          if(!(flags & FLAGS_LEFT))
-            flags |= FLAGS_PAD_NIL;
-          FALLTHROUGH();
-        case '1':
-        case '2':
-        case '3':
-        case '4':
-        case '5':
-        case '6':
-        case '7':
-        case '8':
-        case '9': {
-          curl_off_t num;
-          flags |= FLAGS_WIDTH;
-          fmt--;
-          if(curlx_str_number(&fmt, &num, INT_MAX))
-            return PFMT_WIDTH;
-          width = (int)num;
-          break;
-        }
-        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();
-      case 's':
-        type = MTYPE_STRING;
-        break;
-      case 'n':
-        type = MTYPE_INTPTR;
-        break;
-      case 'p':
-        type = MTYPE_PTR;
-        break;
-      case 'd':
-      case 'i':
-        if(flags & FLAGS_LONGLONG)
-          type = MTYPE_LONGLONG;
-        else if(flags & FLAGS_LONG)
-          type = MTYPE_LONG;
-        else
-          type = MTYPE_INT;
-        break;
-      case 'u':
-        if(flags & FLAGS_LONGLONG)
-          type = MTYPE_LONGLONGU;
-        else if(flags & FLAGS_LONG)
-          type = MTYPE_LONGU;
-        else
-          type = MTYPE_INTU;
-        flags |= FLAGS_UNSIGNED;
-        break;
-      case 'o':
-        if(flags & FLAGS_LONGLONG)
-          type = MTYPE_LONGLONGU;
-        else if(flags & FLAGS_LONG)
-          type = MTYPE_LONGU;
-        else
-          type = MTYPE_INTU;
-        flags |= FLAGS_OCTAL | FLAGS_UNSIGNED;
-        break;
-      case 'x':
-        if(flags & FLAGS_LONGLONG)
-          type = MTYPE_LONGLONGU;
-        else if(flags & FLAGS_LONG)
-          type = MTYPE_LONGU;
-        else
-          type = MTYPE_INTU;
-        flags |= FLAGS_HEX | FLAGS_UNSIGNED;
-        break;
-      case 'X':
-        if(flags & FLAGS_LONGLONG)
-          type = MTYPE_LONGLONGU;
-        else if(flags & FLAGS_LONG)
-          type = MTYPE_LONGU;
-        else
-          type = MTYPE_INTU;
-        flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED;
-        break;
-      case 'c':
-        type = MTYPE_INT;
-        flags |= FLAGS_CHAR;
-        break;
-      case 'f':
-        type = MTYPE_DOUBLE;
-        break;
-      case 'e':
-        type = MTYPE_DOUBLE;
-        flags |= FLAGS_FLOATE;
-        break;
-      case 'E':
-        type = MTYPE_DOUBLE;
-        flags |= FLAGS_FLOATE | FLAGS_UPPER;
-        break;
-      case 'g':
-        type = MTYPE_DOUBLE;
-        flags |= FLAGS_FLOATG;
-        break;
-      case 'G':
-        type = MTYPE_DOUBLE;
-        flags |= FLAGS_FLOATG | FLAGS_UPPER;
-        break;
-      default:
-        /* invalid instruction, disregard and continue */
+      if(parse_conversion(*fmt, &flags, &type))
         continue;
-      } /* switch */
 
       if(flags & FLAGS_WIDTHPARAM) {
         if(width < 0)
@@ -672,22 +706,19 @@
 
   /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
      output characters */
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
 #ifdef _WIN32
   curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum);
-#elif defined(HAVE_SNPRINTF)
+#else
   /* !checksrc! disable BANNEDFUNC 1 */
   /* !checksrc! disable LONGLINE */
   /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */
   snprintf(work, BUFFSIZE, fmt, dnum);
-#else
-  /* float and double outputs do not work without snprintf support */
-  work[0] = 0;
 #endif
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic pop
 #endif
   DEBUGASSERT(strlen(work) < BUFFSIZE);
@@ -1074,7 +1105,7 @@
 }
 
 int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
-                    va_list ap_save)
+                    va_list args)
 {
   int retcode;
   struct nsprintf info;
@@ -1083,7 +1114,7 @@
   info.length = 0;
   info.max = maxlength;
 
-  retcode = formatf(&info, addbyter, format, ap_save);
+  retcode = formatf(&info, addbyter, format, args);
   if(info.max) {
     /* we terminate this with a zero byte */
     if(info.max == info.length) {
@@ -1101,10 +1132,10 @@
 int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
 {
   int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-  retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
-  va_end(ap_save);
+  va_list args; /* argument pointer */
+  va_start(args, format);
+  retcode = curl_mvsnprintf(buffer, maxlength, format, args);
+  va_end(args);
   return retcode;
 }
 
@@ -1121,13 +1152,13 @@
 }
 
 /* appends the formatted string, returns MERR error code */
-int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args)
 {
   struct asprintf info;
   info.b = dyn;
   info.merr = MERR_OK;
 
-  (void)formatf(&info, alloc_addbyter, format, ap_save);
+  (void)formatf(&info, alloc_addbyter, format, args);
   if(info.merr) {
     curlx_dyn_free(info.b);
     return info.merr;
@@ -1135,7 +1166,7 @@
   return 0;
 }
 
-char *curl_mvaprintf(const char *format, va_list ap_save)
+char *curl_mvaprintf(const char *format, va_list args)
 {
   struct asprintf info;
   struct dynbuf dyn;
@@ -1143,7 +1174,7 @@
   curlx_dyn_init(info.b, DYN_APRINTF);
   info.merr = MERR_OK;
 
-  (void)formatf(&info, alloc_addbyter, format, ap_save);
+  (void)formatf(&info, alloc_addbyter, format, args);
   if(info.merr) {
     curlx_dyn_free(info.b);
     return NULL;
@@ -1155,11 +1186,11 @@
 
 char *curl_maprintf(const char *format, ...)
 {
-  va_list ap_save;
+  va_list args;
   char *s;
-  va_start(ap_save, format);
-  s = curl_mvaprintf(format, ap_save);
-  va_end(ap_save);
+  va_start(args, format);
+  s = curl_mvaprintf(format, args);
+  va_end(args);
   return s;
 }
 
@@ -1173,11 +1204,11 @@
 
 int curl_msprintf(char *buffer, const char *format, ...)
 {
-  va_list ap_save; /* argument pointer */
+  va_list args; /* argument pointer */
   int retcode;
-  va_start(ap_save, format);
-  retcode = formatf(&buffer, storebuffer, format, ap_save);
-  va_end(ap_save);
+  va_start(args, format);
+  retcode = formatf(&buffer, storebuffer, format, args);
+  va_end(args);
   *buffer = 0; /* we terminate this with a zero byte */
   return retcode;
 }
@@ -1193,36 +1224,36 @@
 int curl_mprintf(const char *format, ...)
 {
   int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-  retcode = formatf(stdout, fputc_wrapper, format, ap_save);
-  va_end(ap_save);
+  va_list args; /* argument pointer */
+  va_start(args, format);
+  retcode = formatf(stdout, fputc_wrapper, format, args);
+  va_end(args);
   return retcode;
 }
 
-int curl_mfprintf(FILE *whereto, const char *format, ...)
+int curl_mfprintf(FILE *fd, const char *format, ...)
 {
   int retcode;
-  va_list ap_save; /* argument pointer */
-  va_start(ap_save, format);
-  retcode = formatf(whereto, fputc_wrapper, format, ap_save);
-  va_end(ap_save);
+  va_list args; /* argument pointer */
+  va_start(args, format);
+  retcode = formatf(fd, fputc_wrapper, format, args);
+  va_end(args);
   return retcode;
 }
 
-int curl_mvsprintf(char *buffer, const char *format, va_list ap_save)
+int curl_mvsprintf(char *buffer, const char *format, va_list args)
 {
-  int retcode = formatf(&buffer, storebuffer, format, ap_save);
+  int retcode = formatf(&buffer, storebuffer, format, args);
   *buffer = 0; /* we terminate this with a zero byte */
   return retcode;
 }
 
-int curl_mvprintf(const char *format, va_list ap_save)
+int curl_mvprintf(const char *format, va_list args)
 {
-  return formatf(stdout, fputc_wrapper, format, ap_save);
+  return formatf(stdout, fputc_wrapper, format, args);
 }
 
-int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
+int curl_mvfprintf(FILE *fd, const char *format, va_list args)
 {
-  return formatf(whereto, fputc_wrapper, format, ap_save);
+  return formatf(fd, fputc_wrapper, format, args);
 }
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index e591cfc..84fd272 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -351,8 +351,8 @@
 end:
   if(packet)
     curlx_free(packet);
-  Curl_safefree(data->state.aptr.user);
-  Curl_safefree(data->state.aptr.passwd);
+  curlx_safefree(data->state.aptr.user);
+  curlx_safefree(data->state.aptr.passwd);
   return result;
 }
 
@@ -964,7 +964,7 @@
 /*
  * MQTTS protocol.
  */
-static const struct Curl_protocol Curl_protocol_mqtts = {
+const struct Curl_protocol Curl_protocol_mqtts = {
   mqtt_setup_conn,                    /* setup_connection */
   mqtt_do,                            /* do_it */
   mqtt_done,                          /* done */
@@ -979,7 +979,7 @@
   ZERO_NULL,                          /* disconnect */
   ZERO_NULL,                          /* write_resp */
   ZERO_NULL,                          /* write_resp_hd */
-  ZERO_NULL,                          /* connection_check */
+  ZERO_NULL,                          /* connection_is_dead */
   ZERO_NULL,                          /* attach connection */
   ZERO_NULL,                          /* follow */
 };
@@ -989,7 +989,7 @@
 /*
  * MQTT protocol.
  */
-static const struct Curl_protocol Curl_protocol_mqtt = {
+const struct Curl_protocol Curl_protocol_mqtt = {
   mqtt_setup_conn,                    /* setup_connection */
   mqtt_do,                            /* do_it */
   mqtt_done,                          /* done */
@@ -1004,38 +1004,9 @@
   ZERO_NULL,                          /* disconnect */
   ZERO_NULL,                          /* write_resp */
   ZERO_NULL,                          /* write_resp_hd */
-  ZERO_NULL,                          /* connection_check */
+  ZERO_NULL,                          /* connection_is_dead */
   ZERO_NULL,                          /* attach connection */
   ZERO_NULL,                          /* follow */
 };
 
 #endif /* CURL_DISABLE_MQTT */
-
-const struct Curl_scheme Curl_scheme_mqtts = {
-  "mqtts",                            /* scheme */
-#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_mqtts,
-#endif
-  CURLPROTO_MQTTS,                    /* protocol */
-  CURLPROTO_MQTT,                     /* family */
-  PROTOPT_SSL,                        /* flags */
-  PORT_MQTTS,                         /* defport */
-};
-
-/*
- * MQTT protocol.
- */
-const struct Curl_scheme Curl_scheme_mqtt = {
-  "mqtt",                             /* scheme */
-#ifdef CURL_DISABLE_MQTT
-  ZERO_NULL,
-#else
-  &Curl_protocol_mqtt,
-#endif
-  CURLPROTO_MQTT,                     /* protocol */
-  CURLPROTO_MQTT,                     /* family */
-  PROTOPT_NONE,                       /* flags */
-  PORT_MQTT,                          /* defport */
-};
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h
index 67d1df4..3d95293 100644
--- a/Utilities/cmcurl/lib/mqtt.h
+++ b/Utilities/cmcurl/lib/mqtt.h
@@ -23,7 +23,11 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_mqtt;
-extern const struct Curl_scheme Curl_scheme_mqtts;
+#ifndef CURL_DISABLE_MQTT
+extern const struct Curl_protocol Curl_protocol_mqtt;
+#ifdef USE_SSL
+extern const struct Curl_protocol Curl_protocol_mqtts;
+#endif
+#endif
 
 #endif /* HEADER_CURL_MQTT_H */
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index 6133736..7520253 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -74,7 +74,6 @@
 
 #define CURL_MULTI_HANDLE 0x000bab1e
 
-
 #ifdef DEBUGBUILD
 /* On a debug build, we want to fail hard on multi handles that
  * are not NULL, but no longer have the MAGIC touch. This gives
@@ -140,7 +139,6 @@
     NULL,              /* PENDING */
     NULL,              /* SETUP */
     Curl_init_CONNECT, /* CONNECT */
-    NULL,              /* RESOLVING */
     NULL,              /* CONNECTING */
     NULL,              /* PROTOCONNECT */
     NULL,              /* PROTOCONNECTING */
@@ -207,7 +205,7 @@
 static void ph_freeentry(void *p)
 {
   (void)p;
-  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+  /* Always FALSE. Cannot use a 0 assert here since compilers
    * are not in agreement if they then want a NORETURN attribute or
    * not. *sigh* */
   DEBUGASSERT(p == NULL);
@@ -254,6 +252,10 @@
   multi->multiplexing = TRUE;
   multi->max_concurrent_streams = 100;
   multi->last_timeout_ms = -1;
+#ifdef ENABLE_WAKEUP
+  multi->wakeup_pair[0] = CURL_SOCKET_BAD;
+  multi->wakeup_pair[1] = CURL_SOCKET_BAD;
+#endif
 
   if(Curl_mntfy_resize(multi) ||
      Curl_uint32_bset_resize(&multi->process, xfer_table_size) ||
@@ -294,22 +296,35 @@
   multi->wsa_event = WSACreateEvent();
   if(multi->wsa_event == WSA_INVALID_EVENT)
     goto error;
-#elif defined(ENABLE_WAKEUP)
-  if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) {
-    multi->wakeup_pair[0] = CURL_SOCKET_BAD;
-    multi->wakeup_pair[1] = CURL_SOCKET_BAD;
-  }
+#endif
+#ifdef ENABLE_WAKEUP
+  /* When enabled, rely on this to work. We ignore this in previous
+   * versions, but that seems an unnecessary complication. */
+  if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0)
+    goto error;
 #endif
 
-#ifdef USE_IPV6
   if(Curl_probeipv6(multi))
     goto error;
+
+#ifdef USE_RESOLV_THREADED
+  if(xfer_table_size < CURL_XFER_TABLE_SIZE) { /* easy multi */
+    if(Curl_async_thrdd_multi_init(multi, 0, 2, 10))
+      goto error;
+  }
+  else { /* real multi handle */
+    if(Curl_async_thrdd_multi_init(multi, 0, 20, 2000))
+      goto error;
+  }
 #endif
 
   return multi;
 
 error:
 
+#ifdef USE_RESOLV_THREADED
+  Curl_async_thrdd_multi_destroy(multi, TRUE);
+#endif
   Curl_multi_ev_cleanup(multi);
   Curl_hash_destroy(&multi->proto_hash);
   Curl_dnscache_destroy(&multi->dnscache);
@@ -319,6 +334,7 @@
   Curl_ssl_scache_destroy(multi->ssl_scache);
 #endif
   if(multi->admin) {
+    Curl_multi_ev_xfer_done(multi, multi->admin);
     multi->admin->multi = NULL;
     Curl_close(&multi->admin);
   }
@@ -329,6 +345,9 @@
   Curl_uint32_bset_destroy(&multi->pending);
   Curl_uint32_bset_destroy(&multi->msgsent);
   Curl_uint32_tbl_destroy(&multi->xfers);
+#ifdef ENABLE_WAKEUP
+  Curl_wakeup_destroy(multi->wakeup_pair);
+#endif
 
   curlx_free(multi);
   return NULL;
@@ -362,6 +381,17 @@
   return data->mstate < MSTATE_DO;
 }
 
+static CURLMcode multi_assess_wakeup(struct Curl_multi *multi)
+{
+#ifdef ENABLE_WAKEUP
+  if(multi->socket_cb)
+    return Curl_multi_ev_assess_xfer(multi, multi->admin);
+#else
+  (void)multi;
+#endif
+  return CURLM_OK;
+}
+
 static CURLMcode multi_xfers_add(struct Curl_multi *multi,
                                  struct Curl_easy *data)
 {
@@ -421,11 +451,11 @@
   return CURLM_OK;
 }
 
-CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
+CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl)
 {
   CURLMcode mresult;
   struct Curl_multi *multi = m;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -528,6 +558,12 @@
     data->set.server_response_timeout;
   multi->admin->set.no_signal = data->set.no_signal;
 
+  mresult = multi_assess_wakeup(multi);
+  if(mresult) {
+    failf(data, "error enabling wakeup listening: %d", mresult);
+    return mresult;
+  }
+
   CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u",
              data->mid, Curl_multi_xfers_running(multi),
              Curl_uint32_tbl_count(&multi->xfers));
@@ -600,22 +636,6 @@
                               void *userdata)
 {
   struct multi_done_ctx *mdctx = userdata;
-#ifdef CURLVERBOSE
-  const char *host =
-#ifndef CURL_DISABLE_PROXY
-        conn->bits.socksproxy ?
-        conn->socks_proxy.host.dispname :
-        conn->bits.httpproxy ? conn->http_proxy.host.dispname :
-#endif
-        conn->bits.conn_to_host ? conn->conn_to_host.dispname :
-        conn->host.dispname;
-  int port =
-#ifndef CURL_DISABLE_PROXY
-        conn->bits.httpproxy ? conn->http_proxy.port :
-#endif
-        conn->bits.conn_to_port ? conn->conn_to_port :
-        conn->remote_port;
-#endif /* CURLVERBOSE */
 
   Curl_detach_connection(data);
 
@@ -630,22 +650,21 @@
   data->state.done = TRUE; /* called now! */
   data->state.recent_conn_id = conn->connection_id;
 
-  Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
-  Curl_resolv_unlink(data, &data->state.dns[1]);
   Curl_dnscache_prune(data);
 
   if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) {
-    CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, "
+    CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s, "
                "forbid=%d, close=%d, premature=%d, conn_multiplex=%d",
-               conn->connection_id, host, port, data->set.reuse_forbid,
-               conn->bits.close, mdctx->premature,
+               conn->connection_id, conn->destination,
+               data->set.reuse_forbid, conn->bits.close, mdctx->premature,
                Curl_conn_is_multiplex(conn, FIRSTSOCKET));
     connclose(conn, "disconnecting");
     Curl_conn_terminate(data, conn, (bool)mdctx->premature);
   }
   else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
-    CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%d was shutdown"
-               " by server, not reusing", conn->connection_id, host, port);
+    CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown"
+               " by server, not reusing", conn->connection_id,
+               conn->destination);
     connclose(conn, "server shutdown");
     Curl_conn_terminate(data, conn, (bool)mdctx->premature);
   }
@@ -654,8 +673,8 @@
     if(Curl_cpool_conn_now_idle(data, conn)) {
       /* connection kept in the cpool */
       data->state.lastconnect_id = conn->connection_id;
-      infof(data, "Connection #%" FMT_OFF_T " to host %s:%d left intact",
-            conn->connection_id, host, port);
+      infof(data, "Connection #%" FMT_OFF_T " to host %s left intact",
+            conn->connection_id, conn->destination);
     }
     else {
       /* connection was removed from the cpool and destroyed. */
@@ -671,9 +690,6 @@
 {
   CURLcode result;
   struct connectdata *conn = data->conn;
-  struct multi_done_ctx mdctx;
-
-  memset(&mdctx, 0, sizeof(mdctx));
 
   CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
              (int)status, (int)premature, data->state.done);
@@ -683,11 +699,11 @@
     return CURLE_OK;
 
   /* Shut down any ongoing async resolver operation. */
-  Curl_async_shutdown(data);
+  Curl_resolv_shutdown_all(data);
 
   /* Cleanup possible redirect junk */
-  Curl_safefree(data->req.newurl);
-  Curl_safefree(data->req.location);
+  curlx_safefree(data->req.newurl);
+  curlx_safefree(data->req.location);
 
   switch(status) {
   case CURLE_ABORTED_BY_CALLBACK:
@@ -704,34 +720,43 @@
   }
 
   /* this calls the protocol-specific function pointer previously set */
-  if(conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT))
+  if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT))
     result = conn->scheme->run->done(data, status, premature);
   else
     result = status;
 
-  if(result != CURLE_ABORTED_BY_CALLBACK) {
-    /* avoid this if we already aborted by callback to avoid this calling
-       another callback */
+  if(data->mstate > MSTATE_CONNECTING &&
+     (result != CURLE_ABORTED_BY_CALLBACK)) {
+    /* avoid this if
+     * - the transfer has not connected
+     * - we already aborted by callback to avoid this calling another callback
+     */
     int rc = Curl_pgrsDone(data);
     if(!result && rc)
       result = CURLE_ABORTED_BY_CALLBACK;
   }
 
   /* Make sure that transfer client writes are really done now. */
-  result = Curl_1st_err(result, Curl_xfer_write_done(data, premature));
+  result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature));
 
   /* Inform connection filters that this transfer is done */
-  Curl_conn_ev_data_done(data, premature);
+  if(conn)
+    Curl_conn_ev_data_done(data, premature);
 
   process_pending_handles(data->multi); /* connection / multiplex */
 
   if(!result)
     result = Curl_req_done(&data->req, data, premature);
 
-  /* Under the potential connection pool's share lock, decide what to
-   * do with the transfer's connection. */
-  mdctx.premature = premature;
-  Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
+  if(conn) {
+    /* Under the potential connection pool's share lock, decide what to
+     * do with the transfer's connection. */
+    struct multi_done_ctx mdctx;
+
+    memset(&mdctx, 0, sizeof(mdctx));
+    mdctx.premature = premature;
+    Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
+  }
 
   /* flush the netrc cache */
   Curl_netrc_cleanup(&data->state.netrc);
@@ -744,14 +769,14 @@
 {
   (void)userdata;
   (void)data;
-  if(conn->connect_only)
+  if(conn->bits.connect_only)
     connclose(conn, "Removing connect-only easy handle");
 }
 
-CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
+CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
 {
   struct Curl_multi *multi = m;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   bool premature;
   struct Curl_llist_node *e;
   CURLMcode mresult;
@@ -886,6 +911,12 @@
   if(mresult)
     return mresult;
 
+  mresult = multi_assess_wakeup(multi);
+  if(mresult) {
+    failf(data, "error enabling wakeup listening: %d", mresult);
+    return mresult;
+  }
+
   CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u",
              mid, Curl_multi_xfers_running(multi),
              Curl_uint32_tbl_count(&multi->xfers));
@@ -1080,7 +1111,7 @@
   if(conn->scheme->run->perform_pollset)
     result = conn->scheme->run->perform_pollset(data, ps);
   else {
-    /* Default is to obey the data->req.keepon flags for send/recv */
+    /* Default is to obey the request flags for send/recv */
     if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) {
       result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]);
     }
@@ -1101,75 +1132,77 @@
 CURLMcode Curl_multi_pollset(struct Curl_easy *data,
                              struct easy_pollset *ps)
 {
-  CURLMcode mresult = CURLM_OK;
   CURLcode result = CURLE_OK;
 
+  Curl_pollset_reset(ps);
+#ifdef ENABLE_WAKEUP
+  /* The admin handle always listens on the wakeup socket when there
+   * are transfers alive. */
+  if(data->multi && (data == data->multi->admin) &&
+     data->multi->xfers_alive) {
+    result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]);
+  }
+#endif
   /* If the transfer has no connection, this is fine. Happens when
      called via curl_multi_remove_handle() => Curl_multi_ev_assess() =>
      Curl_multi_pollset(). */
-  Curl_pollset_reset(ps);
-  if(!data->conn)
-    return CURLM_OK;
+  if(!result && data->conn) {
+    switch(data->mstate) {
+    case MSTATE_INIT:
+    case MSTATE_PENDING:
+    case MSTATE_SETUP:
+    case MSTATE_CONNECT:
+      /* nothing to poll for yet */
+      break;
 
-  switch(data->mstate) {
-  case MSTATE_INIT:
-  case MSTATE_PENDING:
-  case MSTATE_SETUP:
-  case MSTATE_CONNECT:
-    /* nothing to poll for yet */
-    break;
+    case MSTATE_CONNECTING:
+      if(data->conn && !data->conn->bits.dns_resolved)
+        result = Curl_resolv_pollset(data, ps);
+      if(!result)
+        result = mstate_connecting_pollset(data, ps);
+      break;
 
-  case MSTATE_RESOLVING:
-    result = Curl_resolv_pollset(data, ps);
-    break;
+    case MSTATE_PROTOCONNECT:
+    case MSTATE_PROTOCONNECTING:
+      result = mstate_protocol_pollset(data, ps);
+      break;
 
-  case MSTATE_CONNECTING:
-    result = mstate_connecting_pollset(data, ps);
-    break;
+    case MSTATE_DO:
+    case MSTATE_DOING:
+      result = mstate_do_pollset(data, ps);
+      break;
 
-  case MSTATE_PROTOCONNECT:
-  case MSTATE_PROTOCONNECTING:
-    result = mstate_protocol_pollset(data, ps);
-    break;
+    case MSTATE_DOING_MORE:
+      result = mstate_domore_pollset(data, ps);
+      break;
 
-  case MSTATE_DO:
-  case MSTATE_DOING:
-    result = mstate_do_pollset(data, ps);
-    break;
+    case MSTATE_DID: /* same as PERFORMING in regard to polling */
+    case MSTATE_PERFORMING:
+      result = mstate_perform_pollset(data, ps);
+      break;
 
-  case MSTATE_DOING_MORE:
-    result = mstate_domore_pollset(data, ps);
-    break;
+    case MSTATE_RATELIMITING:
+      /* we need to let time pass, ignore socket(s) */
+      break;
 
-  case MSTATE_DID: /* same as PERFORMING in regard to polling */
-  case MSTATE_PERFORMING:
-    result = mstate_perform_pollset(data, ps);
-    break;
+    case MSTATE_DONE:
+    case MSTATE_COMPLETED:
+    case MSTATE_MSGSENT:
+      /* nothing more to poll for */
+      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;
+    default:
+      failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
+      DEBUGASSERT(0);
+      break;
+    }
   }
 
   if(result) {
     if(result == CURLE_OUT_OF_MEMORY)
-      mresult = CURLM_OUT_OF_MEMORY;
-    else {
-      failf(data, "error determining pollset: %d", result);
-      mresult = CURLM_INTERNAL_ERROR;
-    }
-    goto out;
+      return CURLM_OUT_OF_MEMORY;
+    failf(data, "error determining pollset: %d", result);
+    return CURLM_INTERNAL_ERROR;
   }
 
 #ifdef CURLVERBOSE
@@ -1208,8 +1241,7 @@
   }
 #endif
 
-out:
-  return mresult;
+  return CURLM_OK;
 }
 
 CURLMcode curl_multi_fdset(CURLM *m,
@@ -1328,7 +1360,167 @@
   if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM)
     swrite(s, NULL, 0);
 }
-#endif
+
+static CURLMcode multi_winsock_select(struct Curl_multi *multi,
+                                      struct curl_pollfds *cpfds,
+                                      unsigned int curl_nfds,
+                                      struct curl_waitfd extra_fds[],
+                                      unsigned int extra_nfds,
+                                      int timeout_ms,
+                                      bool extrawait,
+                                      int *pnevents)
+{
+  CURLMcode mresult = CURLM_OK;
+  WSANETWORKEVENTS wsa_events;
+  int nevents = 0;
+  size_t i;
+
+  DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
+
+  /* Set the WSA events based on the collected pollds */
+  for(i = 0; i < cpfds->n; i++) {
+    long mask = 0;
+    if(cpfds->pfds[i].events & POLLIN)
+      mask |= FD_READ | FD_ACCEPT | FD_CLOSE;
+    if(cpfds->pfds[i].events & POLLPRI)
+      mask |= FD_OOB;
+    if(cpfds->pfds[i].events & POLLOUT) {
+      mask |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+      reset_socket_fdwrite(cpfds->pfds[i].fd);
+    }
+    if(mask) {
+      if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) {
+        mresult = CURLM_OUT_OF_MEMORY;
+        goto out;
+      }
+    }
+  }
+
+  if(cpfds->n || extrawait) {
+    int pollrc = 0;
+
+    if(cpfds->n) {         /* pre-check with Winsock */
+      pollrc = Curl_poll(cpfds->pfds, cpfds->n, 0);
+      if(pollrc < 0) {
+        mresult = CURLM_UNRECOVERABLE_POLL;
+        goto out;
+      }
+      nevents = pollrc;
+    }
+
+    if(!nevents) {
+      /* now wait... if not ready during the pre-check (pollrc == 0) */
+      WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms,
+                               FALSE);
+    }
+
+    /* With Winsock, we have to run the following section unconditionally
+       to call WSAEventSelect(fd, event, 0) on all the sockets */
+    /* copy revents results from the poll to the curl_multi_wait poll
+       struct, the bit values of the actual underlying poll() implementation
+       may not be the same as the ones in the public libcurl API! */
+    for(i = 0; i < extra_nfds; i++) {
+      unsigned short mask = 0;
+      curl_socket_t s = extra_fds[i].fd;
+
+      wsa_events.lNetworkEvents = 0;
+      if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) {
+        if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
+          mask |= CURL_WAIT_POLLIN;
+        if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE))
+          mask |= CURL_WAIT_POLLOUT;
+        if(wsa_events.lNetworkEvents & FD_OOB)
+          mask |= CURL_WAIT_POLLPRI;
+        if(!pollrc && wsa_events.lNetworkEvents)
+          nevents++;
+      }
+      WSAEventSelect(s, multi->wsa_event, 0);
+      if(!pollrc) {
+        extra_fds[i].revents = (short)mask;
+        continue;
+      }
+      else {
+        unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents;
+        if(r & POLLIN)
+          mask |= CURL_WAIT_POLLIN;
+        if(r & POLLOUT)
+          mask |= CURL_WAIT_POLLOUT;
+        if(r & POLLPRI)
+          mask |= CURL_WAIT_POLLPRI;
+        extra_fds[i].revents = (short)mask;
+      }
+    }
+
+    /* Count up all our own sockets that had activity,
+       and remove them from the event. */
+    for(i = 0; i < curl_nfds; ++i) {
+      wsa_events.lNetworkEvents = 0;
+      if(WSAEnumNetworkEvents(cpfds->pfds[i].fd, NULL, &wsa_events) == 0) {
+        if(!pollrc && wsa_events.lNetworkEvents)
+          nevents++;
+      }
+      WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, 0);
+    }
+    WSAResetEvent(multi->wsa_event);
+  }
+
+out:
+  *pnevents = nevents;
+  return mresult;
+}
+
+#else /* USE_WINSOCK */
+
+static CURLMcode multi_posix_poll(struct Curl_multi *multi,
+                                  struct curl_pollfds *cpfds,
+                                  unsigned int curl_nfds,
+                                  struct curl_waitfd extra_fds[],
+                                  unsigned int extra_nfds,
+                                  int timeout_ms,
+                                  bool extrawait,
+                                  int *pnevents)
+{
+  CURLMcode mresult = CURLM_OK;
+  int nevents = 0;
+  size_t i;
+
+  (void)multi;
+  if(cpfds->n) {
+    int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */
+    if(pollrc < 0) {
+      mresult = CURLM_UNRECOVERABLE_POLL;
+      goto out;
+    }
+    nevents = pollrc;
+
+    /* copy revents results from the poll to the curl_multi_wait poll
+       struct, the bit values of the actual underlying poll() implementation
+       may not be the same as the ones in the public libcurl API! */
+    for(i = 0; i < extra_nfds; i++) {
+      unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents;
+      unsigned short mask = 0;
+      if(r & POLLIN)
+        mask |= CURL_WAIT_POLLIN;
+      if(r & POLLOUT)
+        mask |= CURL_WAIT_POLLOUT;
+      if(r & POLLPRI)
+        mask |= CURL_WAIT_POLLPRI;
+      extra_fds[i].revents = (short)mask;
+    }
+  }
+  else if(extrawait) {
+    /* No fds to poll, but asked to obey timeout_ms anyway. We cannot
+     * use Curl_poll() as it, on some platforms, returns immediately
+     * without fds. */
+    curlx_wait_ms(timeout_ms);
+  }
+
+out:
+  *pnevents = nevents;
+  return mresult;
+}
+
+#endif /* !USE_WINSOCK */
 
 #define NUM_POLLS_ON_STACK 10
 
@@ -1337,13 +1529,12 @@
                             unsigned int extra_nfds,
                             int timeout_ms,
                             int *ret,
-                            bool extrawait, /* when no socket, wait */
-                            bool use_wakeup)
+                            bool extrawait)  /* when no socket, wait */
 {
   size_t i;
   struct curltime expire_time;
   long timeout_internal;
-  int retcode = 0;
+  int nevents = 0;
   struct easy_pollset ps;
   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct curl_pollfds cpfds;
@@ -1351,13 +1542,8 @@
   struct Curl_easy *data = NULL;
   CURLMcode mresult = CURLM_OK;
   uint32_t mid;
-
-#ifdef USE_WINSOCK
-  WSANETWORKEVENTS wsa_events;
-  DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
-#endif
-#ifndef ENABLE_WAKEUP
-  (void)use_wakeup;
+#ifdef ENABLE_WAKEUP
+  int wakeup_idx = -1;
 #endif
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -1395,6 +1581,20 @@
     goto out;
   }
 
+#ifdef ENABLE_WAKEUP
+  /* If `extrawait` is TRUE *or* we have `extra_fds`to poll *or* we
+   * have transfer sockets to poll, we obey `timeout_ms`.
+   * Then we need to also monitor the multi's wakeup
+   * socket to catch calls to `curl_multi_wakeup()` during the wait. */
+  if(extrawait || cpfds.n || extra_nfds) {
+    wakeup_idx = cpfds.n;
+    if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) {
+      mresult = CURLM_OUT_OF_MEMORY;
+      goto out;
+    }
+  }
+#endif
+
   curl_nfds = cpfds.n; /* what curl internally uses in cpfds */
   /* Add external file descriptions from poll-like struct curl_waitfd */
   for(i = 0; i < extra_nfds; i++) {
@@ -1411,185 +1611,64 @@
     }
   }
 
-#ifdef USE_WINSOCK
-  /* Set the WSA events based on the collected pollds */
-  for(i = 0; i < cpfds.n; i++) {
-    long mask = 0;
-    if(cpfds.pfds[i].events & POLLIN)
-      mask |= FD_READ | FD_ACCEPT | FD_CLOSE;
-    if(cpfds.pfds[i].events & POLLPRI)
-      mask |= FD_OOB;
-    if(cpfds.pfds[i].events & POLLOUT) {
-      mask |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-      reset_socket_fdwrite(cpfds.pfds[i].fd);
-    }
-    if(mask) {
-      if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) {
-        mresult = CURLM_OUT_OF_MEMORY;
-        goto out;
-      }
-    }
-  }
-#endif
-
-#ifdef ENABLE_WAKEUP
-#ifndef USE_WINSOCK
-  if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
-    if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) {
-      mresult = CURLM_OUT_OF_MEMORY;
-      goto out;
-    }
-  }
-#endif
-#endif
-
   /* We check the internal timeout *AFTER* we collected all sockets to
    * poll. Collecting the sockets may install new timers by protocols
    * and connection filters.
-   * Use the shorter one of the internal and the caller requested timeout. */
+   * Use the shorter one of the internal and the caller requested timeout.
+   * If we are called with `!extrawait` and multi_timeout() reports no
+   * timeouts exist, do not wait. */
   multi_timeout(multi, &expire_time, &timeout_internal);
   if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
     timeout_ms = (int)timeout_internal;
 
   if(data)
-    CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld",
+    CURL_TRC_M(data, "multi_wait(fds=%u, timeout=%d) tinternal=%ld",
                cpfds.n, timeout_ms, timeout_internal);
-#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
-  if(cpfds.n || use_wakeup) {
-#else
-  if(cpfds.n) {
-#endif
-    int pollrc;
-#ifdef USE_WINSOCK
-    if(cpfds.n)         /* pre-check with Winsock */
-      pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0);
-    else
-      pollrc = 0;
-#else
-    pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */
-#endif
-    if(pollrc < 0) {
-      mresult = CURLM_UNRECOVERABLE_POLL;
-      goto out;
-    }
-
-    if(pollrc > 0) {
-      retcode = pollrc;
-#ifdef USE_WINSOCK
-    }
-    else { /* now wait... if not ready during the pre-check (pollrc == 0) */
-      WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms,
-                               FALSE);
-    }
-    /* With Winsock, we have to run the following section unconditionally
-       to call WSAEventSelect(fd, event, 0) on all the sockets */
-    {
-#endif
-      /* copy revents results from the poll to the curl_multi_wait poll
-         struct, the bit values of the actual underlying poll() implementation
-         may not be the same as the ones in the public libcurl API! */
-      for(i = 0; i < extra_nfds; i++) {
-        unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents;
-        unsigned short mask = 0;
-#ifdef USE_WINSOCK
-        curl_socket_t s = extra_fds[i].fd;
-        wsa_events.lNetworkEvents = 0;
-        if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) {
-          if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
-            mask |= CURL_WAIT_POLLIN;
-          if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE))
-            mask |= CURL_WAIT_POLLOUT;
-          if(wsa_events.lNetworkEvents & FD_OOB)
-            mask |= CURL_WAIT_POLLPRI;
-          if(ret && !pollrc && wsa_events.lNetworkEvents)
-            retcode++;
-        }
-        WSAEventSelect(s, multi->wsa_event, 0);
-        if(!pollrc) {
-          extra_fds[i].revents = (short)mask;
-          continue;
-        }
-#endif
-        if(r & POLLIN)
-          mask |= CURL_WAIT_POLLIN;
-        if(r & POLLOUT)
-          mask |= CURL_WAIT_POLLOUT;
-        if(r & POLLPRI)
-          mask |= CURL_WAIT_POLLPRI;
-        extra_fds[i].revents = (short)mask;
-      }
 
 #ifdef USE_WINSOCK
-      /* Count up all our own sockets that had activity,
-         and remove them from the event. */
-      for(i = 0; i < curl_nfds; ++i) {
-        wsa_events.lNetworkEvents = 0;
-        if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) {
-          if(ret && !pollrc && wsa_events.lNetworkEvents)
-            retcode++;
-        }
-        WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0);
-      }
-      WSAResetEvent(multi->wsa_event);
+  mresult = multi_winsock_select(multi, &cpfds, curl_nfds,
+                                 extra_fds, extra_nfds,
+                                 timeout_ms, extrawait, &nevents);
 #else
+  mresult = multi_posix_poll(multi, &cpfds, curl_nfds,
+                             extra_fds, extra_nfds,
+                             timeout_ms, extrawait, &nevents);
+#endif
+
 #ifdef ENABLE_WAKEUP
-      if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
-        if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) {
-          (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE);
-          /* do not count the wakeup socket into the returned value */
-          retcode--;
-        }
+    if(nevents && (wakeup_idx >= 0)) {
+      if(cpfds.pfds[wakeup_idx].revents & POLLIN) {
+        (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE);
+        /* do not count the wakeup socket into the returned value */
+        nevents--;
       }
-#endif
-#endif
     }
-  }
-
-  if(ret)
-    *ret = retcode;
-#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK)
-  if(extrawait && !cpfds.n && !use_wakeup) {
-#else
-  if(extrawait && !cpfds.n) {
 #endif
-    long sleep_ms = 0;
-
-    /* Avoid busy-looping when there is nothing particular to wait for */
-    multi_timeout(multi, &expire_time, &sleep_ms);
-    if(sleep_ms) {
-      if(sleep_ms > timeout_ms)
-        sleep_ms = timeout_ms;
-      /* when there are no easy handles in the multi, this holds a -1
-         timeout */
-      else if(sleep_ms < 0)
-        sleep_ms = timeout_ms;
-      curlx_wait_ms(sleep_ms);
-    }
-  }
 
 out:
   Curl_pollset_cleanup(&ps);
   Curl_pollfds_cleanup(&cpfds);
+  if(ret)
+    *ret = nevents;
   return mresult;
 }
 
-CURLMcode curl_multi_wait(CURLM *multi,
+CURLMcode curl_multi_wait(CURLM *m,
                           struct curl_waitfd extra_fds[],
                           unsigned int extra_nfds,
                           int timeout_ms,
                           int *ret)
 {
-  return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE,
-                    FALSE);
+  return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
 }
 
-CURLMcode curl_multi_poll(CURLM *multi,
+CURLMcode curl_multi_poll(CURLM *m,
                           struct curl_waitfd extra_fds[],
                           unsigned int extra_nfds,
                           int timeout_ms,
                           int *ret)
 {
-  return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, TRUE);
+  return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
 }
 
 CURLMcode curl_multi_wakeup(CURLM *m)
@@ -1598,27 +1677,24 @@
      it has to be careful only to access parts of the
      Curl_multi struct that are constant */
   struct Curl_multi *multi = m;
+  CURLMcode mresult = CURLM_WAKEUP_FAILURE;
 
   /* GOOD_MULTI_HANDLE can be safely called */
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
 
 #ifdef ENABLE_WAKEUP
-#ifdef USE_WINSOCK
-  if(WSASetEvent(multi->wsa_event))
-    return CURLM_OK;
-#else
   /* the wakeup_pair variable is only written during init and cleanup,
      making it safe to access from another thread after the init part
      and before cleanup */
-  if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
-    if(Curl_wakeup_signal(multi->wakeup_pair))
-      return CURLM_WAKEUP_FAILURE;
-    return CURLM_OK;
-  }
+  if(!Curl_wakeup_signal(multi->wakeup_pair))
+    mresult = CURLM_OK;
 #endif
+#ifdef USE_WINSOCK
+  if(WSASetEvent(multi->wsa_event))
+    mresult = CURLM_OK;
 #endif
-  return CURLM_WAKEUP_FAILURE;
+  return mresult;
 }
 
 /*
@@ -1660,7 +1736,6 @@
 
   mresult = curl_multi_add_handle(multi, data);
   if(!mresult) {
-    struct SingleRequest *k = &data->req;
     CURLcode result;
 
     /* pass in NULL for 'conn' here since we do not want to init the
@@ -1674,7 +1749,7 @@
     /* take this handle to the perform state right away */
     multistate(data, MSTATE_PERFORMING);
     Curl_attach_connection(data, conn);
-    k->keepon |= KEEP_RECV; /* setup to receive! */
+    CURL_REQ_SET_RECV(data);
   }
   return mresult;
 }
@@ -1732,13 +1807,9 @@
       since = data->progress.t_startsingle;
     else
       since = data->progress.t_startop;
-    if(data->mstate == MSTATE_RESOLVING)
-      failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
-            " milliseconds",
-            curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
-    else if(data->mstate == MSTATE_CONNECTING)
-      failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
-            " milliseconds",
+    if(data->mstate == MSTATE_CONNECTING)
+      failf(data, "%s timed out after %" FMT_TIMEDIFF_T " milliseconds",
+            data->conn->bits.dns_resolved ? "Connection" : "Resolving",
             curlx_ptimediff_ms(Curl_pgrs_now(data), &since));
     else {
       struct SingleRequest *k = &data->req;
@@ -1867,7 +1938,7 @@
  * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string
  * as given by the remote server and set up the new URL to request.
  *
- * This function DOES NOT FREE the given url.
+ * This function DOES NOT FREE the given URL.
  */
 static CURLcode multi_follow(struct Curl_easy *data,
                              const struct Curl_scheme *handler,
@@ -1876,6 +1947,12 @@
 {
   if(handler && handler->run->follow)
     return handler->run->follow(data, newurl, type);
+
+  if(type == FOLLOW_RETRY)
+    /* Retries are generic and do not require protocol-specific redirect
+       handling. */
+    return CURLE_OK;
+
   return CURLE_TOO_MANY_REDIRECTS;
 }
 
@@ -2224,56 +2301,6 @@
   return mresult;
 }
 
-static CURLMcode state_resolving(struct Curl_multi *multi,
-                                 struct Curl_easy *data,
-                                 bool *stream_errorp,
-                                 CURLcode *resultp)
-{
-  struct Curl_dns_entry *dns = NULL;
-  CURLcode result;
-  CURLMcode mresult = CURLM_OK;
-
-  result = Curl_resolv_check(data, &dns);
-  CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s",
-               result, dns ? "found" : "missing");
-  /* Update sockets here, because the socket(s) may have been closed and the
-     application thus needs to be told, even if it is likely that the same
-     socket(s) will again be used further down. If the name has not yet been
-     resolved, it is likely that new sockets have been opened in an attempt to
-     contact another resolver. */
-  mresult = Curl_multi_ev_assess_xfer(multi, data);
-  if(mresult)
-    return mresult;
-
-  if(dns) {
-    bool connected;
-    /* Perform the next step in the connection phase, and then move on to the
-       WAITCONNECT state */
-    result = Curl_once_resolved(data, dns, &connected);
-
-    if(result)
-      /* if Curl_once_resolved() returns failure, the connection struct is
-         already freed and gone */
-      data->conn = NULL; /* no more connection */
-    else {
-      /* call again please so that we get the next socket setup */
-      mresult = CURLM_CALL_MULTI_PERFORM;
-      if(connected)
-        multistate(data, MSTATE_PROTOCONNECT);
-      else {
-        multistate(data, MSTATE_CONNECTING);
-      }
-    }
-  }
-
-  if(result)
-    /* failure detected */
-    *stream_errorp = TRUE;
-
-  *resultp = result;
-  return mresult;
-}
-
 static CURLMcode state_connect(struct Curl_multi *multi,
                                struct Curl_easy *data,
                                CURLcode *resultp)
@@ -2281,9 +2308,8 @@
   /* Connect. We want to get a connection identifier filled in. This state can
      be entered from SETUP and from PENDING. */
   bool connected;
-  bool async;
   CURLMcode mresult = CURLM_OK;
-  CURLcode result = Curl_connect(data, &async, &connected);
+  CURLcode result = Curl_connect(data, &connected);
   if(result == CURLE_NO_CONNECTION_AVAILABLE) {
     /* There was no connection available. We will go to the pending state and
        wait for an available connection. */
@@ -2299,26 +2325,21 @@
     process_pending_handles(data->multi);
 
   if(!result) {
-    if(async)
-      /* We are now waiting for an asynchronous name lookup */
-      multistate(data, MSTATE_RESOLVING);
-    else {
-      /* after the connect has been sent off, go WAITCONNECT unless the
-         protocol connect is already done and we can go directly to WAITDO or
-         DO! */
-      mresult = CURLM_CALL_MULTI_PERFORM;
+    /* after the connect has been sent off, go WAITCONNECT unless the
+       protocol connect is already done and we can go directly to WAITDO or
+       DO! */
+    mresult = CURLM_CALL_MULTI_PERFORM;
 
-      if(connected) {
-        if(!data->conn->bits.reuse &&
-           Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
-          /* new connection, can multiplex, wake pending handles */
-          process_pending_handles(data->multi);
-        }
-        multistate(data, MSTATE_PROTOCONNECT);
+    if(connected) {
+      if(!data->conn->bits.reuse &&
+         Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
+        /* new connection, can multiplex, wake pending handles */
+        process_pending_handles(data->multi);
       }
-      else {
-        multistate(data, MSTATE_CONNECTING);
-      }
+      multistate(data, MSTATE_PROTOCONNECT);
+    }
+    else {
+      multistate(data, MSTATE_CONNECTING);
     }
   }
   *resultp = result;
@@ -2367,7 +2388,7 @@
       return result;
     }
     /* if there is still a connection to use, call the progress function */
-    else if(data->conn) {
+    else if(data->conn && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) {
       result = Curl_pgrsUpdate(data);
       if(result) {
         /* aborted due to progress callback return code must close the
@@ -2424,6 +2445,8 @@
   Curl_uint32_bset_remove(&multi->pending, data->mid);
   Curl_uint32_bset_add(&multi->msgsent, data->mid);
   --multi->xfers_alive;
+  if(!multi->xfers_alive)
+    multi_assess_wakeup(multi);
 }
 
 static CURLMcode multi_runsingle(struct Curl_multi *multi,
@@ -2456,6 +2479,9 @@
   Curl_uint32_bset_remove(&multi->dirty, data->mid);
 
   if(data == multi->admin) {
+#ifdef USE_RESOLV_THREADED
+    Curl_async_thrdd_multi_process(multi);
+#endif
     Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx);
     return CURLM_OK;
   }
@@ -2519,15 +2545,14 @@
       mresult = state_connect(multi, data, &result);
       break;
 
-    case MSTATE_RESOLVING:
-      /* awaiting an asynch name resolve to complete */
-      mresult = state_resolving(multi, data, &stream_error, &result);
-      break;
-
     case MSTATE_CONNECTING:
       /* awaiting a completion of an asynch TCP connect */
-      DEBUGASSERT(data->conn);
-      if(!Curl_xfer_recv_is_paused(data)) {
+      if(!data->conn) {
+        DEBUGASSERT(0);
+        result = CURLE_FAILED_INIT;
+        break;
+      }
+      else if(!Curl_xfer_recv_is_paused(data)) {
         result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
         if(connected && !result) {
           if(!data->conn->bits.reuse &&
@@ -2540,6 +2565,7 @@
         }
         else if(result) {
           /* failure detected */
+          CURL_TRC_M(data, "connect failed -> %d", result);
           multi_posttransfer(data);
           multi_done(data, result, TRUE);
           stream_error = TRUE;
@@ -2885,6 +2911,9 @@
       } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry));
     }
 
+#ifdef USE_RESOLV_THREADED
+    Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit);
+#endif
     Curl_cpool_destroy(&multi->cpool);
     Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
     if(multi->admin) {
@@ -2906,11 +2935,10 @@
 
 #ifdef USE_WINSOCK
     WSACloseEvent(multi->wsa_event);
-#else
+#endif
 #ifdef ENABLE_WAKEUP
   Curl_wakeup_destroy(multi->wakeup_pair);
 #endif
-#endif
 
     multi_xfer_bufs_free(multi);
     Curl_mntfy_cleanup(multi);
@@ -3148,6 +3176,14 @@
        handles the case when the application asks libcurl to run the timeout
        prematurely. */
     memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts));
+
+    /* Applications may set `socket_cb` *after* having added transfers
+     * first. *Then* kick off processing with a
+     * curl_multi_socket_action(TIMEOUT) afterwards. Make sure our
+     * admin handle registers its pollset with the callbacks present. */
+    mresult = multi_assess_wakeup(multi);
+    if(mresult)
+      goto out;
   }
 
   multi_mark_expired_as_dirty(multi, multi_now(multi));
@@ -3255,6 +3291,11 @@
   }
   case CURLMOPT_NETWORK_CHANGED: {
     long val = va_arg(param, long);
+    if(val & CURLMNWC_CLEAR_ALL)
+      /* In the beginning, all values available to set were 1 by mistake. We
+         converted this to mean "all", thus setting all the bits
+         automatically */
+      val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS;
     if(val & CURLMNWC_CLEAR_DNS) {
       Curl_dnscache_clear(multi->admin);
     }
@@ -3269,6 +3310,34 @@
   case CURLMOPT_NOTIFYDATA:
     multi->ntfy.ntfy_cb_data = va_arg(param, void *);
     break;
+  case CURLMOPT_RESOLVE_THREADS_MAX:
+#ifdef USE_RESOLV_THREADED
+    uarg = va_arg(param, long);
+    if((uarg <= 0) || (uarg > UINT32_MAX))
+      mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+    else {
+      CURLcode result = Curl_async_thrdd_multi_set_props(
+        multi, 0, (uint32_t)uarg, 2000);
+      switch(result) {
+      case CURLE_OK:
+        mresult = CURLM_OK;
+        break;
+      case CURLE_BAD_FUNCTION_ARGUMENT:
+        mresult = CURLM_BAD_FUNCTION_ARGUMENT;
+        break;
+      case CURLE_OUT_OF_MEMORY:
+        mresult = CURLM_OUT_OF_MEMORY;
+        break;
+      default:
+        mresult = CURLM_INTERNAL_ERROR;
+        break;
+      }
+    }
+#endif
+    break;
+  case CURLMOPT_QUICK_EXIT:
+    multi->quick_exit = va_arg(param, long) ? 1 : 0;
+    break;
   default:
     mresult = CURLM_UNKNOWN_OPTION;
     break;
@@ -3652,14 +3721,14 @@
   }
 }
 
-CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s,
-                            void *hashp)
+CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd,
+                            void *sockp)
 {
   struct Curl_multi *multi = m;
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
 
-  return Curl_multi_ev_assign(multi, s, hashp);
+  return Curl_multi_ev_assign(multi, sockfd, sockp);
 }
 
 static void move_pending_to_connect(struct Curl_multi *multi,
@@ -3691,13 +3760,32 @@
 */
 static void process_pending_handles(struct Curl_multi *multi)
 {
-  uint32_t mid;
+  uint32_t mid = multi->last_pending_mid;
+
+  if(mid) {
+    while(Curl_uint32_bset_next(&multi->pending, mid, &mid)) {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      if(data) {
+        move_pending_to_connect(multi, data);
+        multi->last_pending_mid = mid;
+        return;
+      }
+      /* transfer no longer known, should not happen */
+      Curl_uint32_bset_remove(&multi->pending, mid);
+      DEBUGASSERT(0);
+    }
+    /* found no pending transfers with `mid` larger than `last_pending_mid`.
+     * Start at the beginning of the pending set again. */
+    multi->last_pending_mid = 0;
+  }
+
   if(Curl_uint32_bset_first(&multi->pending, &mid)) {
     do {
       struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
       if(data) {
         move_pending_to_connect(multi, data);
-        break;
+        multi->last_pending_mid = mid;
+        return;
       }
       /* transfer no longer known, should not happen */
       Curl_uint32_bset_remove(&multi->pending, mid);
@@ -3941,13 +4029,13 @@
 static void multi_xfer_bufs_free(struct Curl_multi *multi)
 {
   DEBUGASSERT(multi);
-  Curl_safefree(multi->xfer_buf);
+  curlx_safefree(multi->xfer_buf);
   multi->xfer_buf_len = 0;
   multi->xfer_buf_borrowed = FALSE;
-  Curl_safefree(multi->xfer_ulbuf);
+  curlx_safefree(multi->xfer_ulbuf);
   multi->xfer_ulbuf_len = 0;
   multi->xfer_ulbuf_borrowed = FALSE;
-  Curl_safefree(multi->xfer_sockbuf);
+  curlx_safefree(multi->xfer_sockbuf);
   multi->xfer_sockbuf_len = 0;
   multi->xfer_sockbuf_borrowed = FALSE;
 }
diff --git a/Utilities/cmcurl/lib/multi_ev.c b/Utilities/cmcurl/lib/multi_ev.c
index 696a012..937e7ce 100644
--- a/Utilities/cmcurl/lib/multi_ev.c
+++ b/Utilities/cmcurl/lib/multi_ev.c
@@ -253,7 +253,7 @@
   DEBUGASSERT(entry->writers + entry->readers);
 
   CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'"
-             " (%d/%d r/w)", s,
+             " (%u/%u r/w)", s,
              (last_action & CURL_POLL_IN) ? "IN" : "",
              (last_action & CURL_POLL_OUT) ? "OUT" : "",
              (cur_action & CURL_POLL_IN) ? "IN" : "",
@@ -603,10 +603,8 @@
                              struct Curl_easy *data)
 {
   DEBUGASSERT(!data->conn); /* transfer should have been detached */
-  if(data != multi->admin) {
-    (void)mev_assess(multi, data, NULL);
-    Curl_meta_remove(data, CURL_META_MEV_POLLSET);
-  }
+  (void)mev_assess(multi, data, NULL);
+  Curl_meta_remove(data, CURL_META_MEV_POLLSET);
 }
 
 void Curl_multi_ev_conn_done(struct Curl_multi *multi,
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index 7a56112..c5cdfbe 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -27,7 +27,7 @@
 #include "hash.h"
 #include "conncache.h"
 #include "cshutdn.h"
-#include "hostip.h"
+#include "dnscache.h"
 #include "multi_ev.h"
 #include "multi_ntfy.h"
 #include "psl.h"
@@ -53,7 +53,6 @@
   MSTATE_PENDING,      /* no connections, waiting for one */
   MSTATE_SETUP,        /* start a new transfer */
   MSTATE_CONNECT,      /* resolve/connect has been sent off */
-  MSTATE_RESOLVING,    /* awaiting the resolve to finalize */
   MSTATE_CONNECTING,   /* awaiting the TCP connect to finalize */
   MSTATE_PROTOCONNECT, /* initiate protocol connect procedure */
   MSTATE_PROTOCONNECTING, /* completing the protocol-specific connect phase */
@@ -71,7 +70,7 @@
 
 #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX)
 
-#ifndef CURL_DISABLE_SOCKETPAIR
+#if !defined(CURL_DISABLE_SOCKETPAIR) && !defined(USE_WINSOCK)
 #define ENABLE_WAKEUP
 #endif
 
@@ -109,6 +108,9 @@
 
   struct Curl_dnscache dnscache; /* DNS cache */
   struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
+#ifdef USE_RESOLV_THREADED
+  struct curl_thrdq *resolv_thrdq;
+#endif
 
 #ifdef USE_LIBPSL
   /* PSL cache. */
@@ -160,13 +162,12 @@
 
 #ifdef USE_WINSOCK
   WSAEVENT wsa_event; /* Winsock event used for waits */
-#else
+#endif
 #ifdef ENABLE_WAKEUP
   curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for
                                    wakeup 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 are allowed to grow the connection
@@ -174,6 +175,8 @@
 #ifdef DEBUGBUILD
   unsigned int now_access_count;
 #endif
+  uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */
+  uint32_t last_resolv_id; /* id of the last DNS resolve operation */
   BIT(ipv6_works);
   BIT(multiplexing);           /* multiplexing wanted */
   BIT(recheckstate);           /* see Curl_multi_connchanged */
@@ -187,6 +190,7 @@
   BIT(xfer_buf_borrowed);      /* xfer_buf is currently being borrowed */
   BIT(xfer_ulbuf_borrowed);    /* xfer_ulbuf is currently being borrowed */
   BIT(xfer_sockbuf_borrowed);  /* xfer_sockbuf is currently being borrowed */
+  BIT(quick_exit);             /* do not join threads on cleanup */
 #ifdef DEBUGBUILD
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
 #endif
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 1073cc5..72d8fee 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -72,35 +72,329 @@
 {
   NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
   FILE *file = curlx_fopen(filename, FOPEN_READTEXT);
-  struct dynbuf linebuf;
-  curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
 
   if(file) {
-    CURLcode result = CURLE_OK;
-    bool eof;
-    ret = NETRC_OK;
-    do {
-      const char *line;
-      result = Curl_get_line(&linebuf, file, &eof);
-      if(!result) {
-        line = curlx_dyn_ptr(&linebuf);
-        /* skip comments on load */
-        curlx_str_passblanks(&line);
-        if(*line == '#')
-          continue;
-        result = curlx_dyn_add(filebuf, line);
-      }
-      if(result) {
-        curlx_dyn_free(filebuf);
-        ret = curl2netrc(result);
+    curlx_struct_stat stat;
+    if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) {
+      CURLcode result = CURLE_OK;
+      bool eof;
+      struct dynbuf linebuf;
+      curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
+      ret = NETRC_OK;
+      do {
+        const char *line;
+        /* Curl_get_line always returns lines ending with a newline */
+        result = Curl_get_line(&linebuf, file, &eof);
+        if(!result) {
+          line = curlx_dyn_ptr(&linebuf);
+          /* skip comments on load */
+          curlx_str_passblanks(&line);
+          if(*line == '#')
+            continue;
+          result = curlx_dyn_add(filebuf, line);
+        }
+        if(result) {
+          curlx_dyn_free(filebuf);
+          ret = curl2netrc(result);
+          break;
+        }
+      } while(!eof);
+      curlx_dyn_free(&linebuf);
+    }
+    curlx_fclose(file);
+  }
+  return ret;
+}
+
+/* bundled parser state to keep function signatures compact */
+struct netrc_state {
+  char *login;
+  char *password;
+  enum host_lookup_state state;
+  enum found_state keyword;
+  NETRCcode retcode;
+  unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */
+  bool our_login;
+  bool done;
+  bool specific_login;
+};
+
+/*
+ * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t
+ * escape sequences. Advances *tok_endp past the closing '"'.
+ *
+ * Returns NETRC_OK or error.
+ */
+static NETRCcode netrc_quoted_token(const char **tok_endp,
+                                    struct dynbuf *token)
+{
+  bool escape = FALSE;
+  NETRCcode rc = NETRC_SYNTAX_ERROR;
+  const char *tok_end = *tok_endp;
+  tok_end++; /* pass the leading quote */
+  while(*tok_end) {
+    CURLcode result;
+    char s = *tok_end;
+    if(escape) {
+      escape = FALSE;
+      switch(s) {
+      case 'n':
+        s = '\n';
+        break;
+      case 'r':
+        s = '\r';
+        break;
+      case 't':
+        s = '\t';
         break;
       }
-    } while(!eof);
+    }
+    else if(s == '\\') {
+      escape = TRUE;
+      tok_end++;
+      continue;
+    }
+    else if(s == '\"') {
+      tok_end++; /* pass the ending quote */
+      rc = NETRC_OK;
+      break;
+    }
+    result = curlx_dyn_addn(token, &s, 1);
+    if(result) {
+      *tok_endp = tok_end;
+      return curl2netrc(result);
+    }
+    tok_end++;
   }
-  curlx_dyn_free(&linebuf);
-  if(file)
-    curlx_fclose(file);
-  return ret;
+  *tok_endp = tok_end;
+  return rc;
+}
+
+/*
+ * Gets the next token from the netrc buffer at *tokp. Writes the token into
+ * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input
+ * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE
+ * when the line is exhausted.
+ *
+ * Returns NETRC_OK or an error code.
+ */
+static NETRCcode netrc_get_token(const char **tokp,
+                                 const char **tok_endp,
+                                 struct dynbuf *token,
+                                 enum host_lookup_state *statep,
+                                 bool *lineend)
+{
+  const char *tok = *tokp;
+  const char *tok_end;
+
+  *lineend = FALSE;
+  curlx_dyn_reset(token);
+  curlx_str_passblanks(&tok);
+
+  /* tok is first non-space letter */
+  if(*statep == MACDEF) {
+    if((*tok == '\n') || (*tok == '\r'))
+      *statep = NOTHING; /* end of macro definition */
+    *lineend = TRUE;
+    *tokp = tok;
+    return NETRC_OK;
+  }
+
+  if(!*tok || (*tok == '\n')) {
+    /* end of line */
+    *lineend = TRUE;
+    *tokp = tok;
+    return NETRC_OK;
+  }
+
+  tok_end = tok;
+  if(*tok == '\"') {
+    /* quoted string */
+    NETRCcode ret = netrc_quoted_token(&tok_end, token);
+    if(ret)
+      return ret;
+  }
+  else {
+    /* unquoted token */
+    size_t len = 0;
+    CURLcode result;
+    while(*tok_end > ' ') {
+      tok_end++;
+      len++;
+    }
+    if(!len)
+      return NETRC_SYNTAX_ERROR;
+    result = curlx_dyn_addn(token, tok, len);
+    if(result)
+      return curl2netrc(result);
+  }
+
+  *tok_endp = tok_end;
+
+  if(curlx_dyn_len(token))
+    *tokp = curlx_dyn_ptr(token);
+  else
+    /* set it to blank to avoid NULL */
+    *tokp = "";
+
+  return NETRC_OK;
+}
+
+/*
+ * Reset parser for a new machine entry. Frees password and optionally login
+ * if it was not user-specified.
+ */
+static void netrc_new_machine(struct netrc_state *ns)
+{
+  ns->keyword = NONE;
+  ns->found = 0;
+  ns->our_login = FALSE;
+  curlx_safefree(ns->password);
+  if(!ns->specific_login)
+    curlx_safefree(ns->login);
+}
+
+/*
+ * Process a parsed token through the HOSTVALID state machine branch. This
+ * handles login/password values and keyword transitions for the matched host.
+ *
+ * Returns NETRC_OK or an error code.
+ */
+static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok)
+{
+  if(ns->keyword == LOGIN) {
+    if(ns->specific_login)
+      ns->our_login = !Curl_timestrcmp(ns->login, tok);
+    else {
+      ns->our_login = TRUE;
+      curlx_free(ns->login);
+      ns->login = curlx_strdup(tok);
+      if(!ns->login)
+        return NETRC_OUT_OF_MEMORY;
+    }
+    ns->found |= FOUND_LOGIN;
+    ns->keyword = NONE;
+  }
+  else if(ns->keyword == PASSWORD) {
+    curlx_free(ns->password);
+    ns->password = curlx_strdup(tok);
+    if(!ns->password)
+      return NETRC_OUT_OF_MEMORY;
+    ns->found |= FOUND_PASSWORD;
+    ns->keyword = NONE;
+  }
+  else if(curl_strequal("login", tok))
+    ns->keyword = LOGIN;
+  else if(curl_strequal("password", tok))
+    ns->keyword = PASSWORD;
+  else if(curl_strequal("machine", tok)) {
+    /* a new machine here */
+
+    if(ns->found & FOUND_PASSWORD &&
+      /* a password was provided for this host */
+
+       ((!ns->specific_login || ns->our_login) ||
+        /* either there was no specific login to search for, or this
+           is the specific one we wanted */
+        (ns->specific_login && !(ns->found & FOUND_LOGIN)))) {
+      /* or we look for a specific login, but that was not specified */
+
+      ns->done = TRUE;
+      return NETRC_OK;
+    }
+
+    ns->state = HOSTFOUND;
+    netrc_new_machine(ns);
+  }
+  else if(curl_strequal("default", tok)) {
+    ns->state = HOSTVALID;
+    ns->retcode = NETRC_OK;
+    netrc_new_machine(ns);
+  }
+  if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login)
+    ns->done = TRUE;
+  return NETRC_OK;
+}
+
+/*
+ * Process one parsed token through the netrc state
+ * machine. Updates the parser state in *ns.
+ * Returns NETRC_OK or an error code.
+ */
+static NETRCcode netrc_handle_token(struct netrc_state *ns,
+                                    const char *tok,
+                                    const char *host)
+{
+  switch(ns->state) {
+  case NOTHING:
+    if(curl_strequal("macdef", tok))
+      ns->state = MACDEF;
+    else if(curl_strequal("machine", tok)) {
+      ns->state = HOSTFOUND;
+      netrc_new_machine(ns);
+    }
+    else if(curl_strequal("default", tok)) {
+      ns->state = HOSTVALID;
+      ns->retcode = NETRC_OK;
+    }
+    break;
+  case MACDEF:
+    if(!*tok)
+      ns->state = NOTHING;
+    break;
+  case HOSTFOUND:
+    if(curl_strequal(host, tok)) {
+      ns->state = HOSTVALID;
+      ns->retcode = NETRC_OK;
+    }
+    else
+      ns->state = NOTHING;
+    break;
+  case HOSTVALID:
+    return netrc_hostvalid(ns, tok);
+  }
+  return NETRC_OK;
+}
+
+/*
+ * Finalize the parse result: fill in defaults and free
+ * resources on error.
+ */
+static NETRCcode netrc_finalize(struct netrc_state *ns,
+                                char **loginp,
+                                char **passwordp,
+                                struct store_netrc *store)
+{
+  NETRCcode retcode = ns->retcode;
+  if(!retcode) {
+    if(!ns->password && ns->our_login) {
+      /* success without a password, set a blank one */
+      ns->password = curlx_strdup("");
+      if(!ns->password)
+        retcode = NETRC_OUT_OF_MEMORY;
+    }
+    else if(!ns->login && !ns->password)
+      /* a default with no credentials */
+      retcode = NETRC_NO_MATCH;
+  }
+  if(!retcode) {
+    /* success */
+    if(!ns->specific_login)
+      *loginp = ns->login;
+
+    /* netrc_finalize() can return a password even when specific_login is set
+       but our_login is false (e.g., host matched but the requested login
+       never matched). See test 685. */
+    *passwordp = ns->password;
+  }
+  else {
+    curlx_dyn_free(&store->filebuf);
+    store->loaded = FALSE;
+    if(!ns->specific_login)
+      curlx_free(ns->login);
+    curlx_free(ns->password);
+  }
+  return retcode;
 }
 
 /*
@@ -108,23 +402,20 @@
  */
 static NETRCcode parsenetrc(struct store_netrc *store,
                             const char *host,
-                            char **loginp, /* might point to a username */
+                            char **loginp,
                             char **passwordp,
                             const char *netrcfile)
 {
-  NETRCcode retcode = NETRC_NO_MATCH;
-  char *login = *loginp;
-  char *password = NULL;
-  bool specific_login = !!login; /* points to something */
-  enum host_lookup_state state = NOTHING;
-  enum found_state keyword = NONE;
-  unsigned char found = 0; /* login + password found bits, as they can come in
-                              any order */
-  bool our_login = FALSE;  /* found our login name */
-  bool done = FALSE;
   const char *netrcbuffer;
   struct dynbuf token;
   struct dynbuf *filebuf = &store->filebuf;
+  struct netrc_state ns;
+
+  memset(&ns, 0, sizeof(ns));
+  ns.retcode = NETRC_NO_MATCH;
+  ns.login = *loginp;
+  ns.specific_login = !!ns.login;
+
   DEBUGASSERT(!*passwordp);
   curlx_dyn_init(&token, MAX_NETRC_TOKEN);
 
@@ -137,195 +428,32 @@
 
   netrcbuffer = curlx_dyn_ptr(filebuf);
 
-  while(!done) {
+  while(!ns.done) {
     const char *tok = netrcbuffer;
-    while(tok && !done) {
+    while(tok && !ns.done) {
       const char *tok_end;
-      bool quoted;
-      curlx_dyn_reset(&token);
-      curlx_str_passblanks(&tok);
-      /* tok is first non-space letter */
-      if(state == MACDEF) {
-        if((*tok == '\n') || (*tok == '\r'))
-          state = NOTHING; /* end of macro definition */
+      bool lineend;
+      NETRCcode ret;
+
+      ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend);
+      if(ret) {
+        ns.retcode = ret;
+        goto out;
       }
-
-      if(!*tok || (*tok == '\n'))
-        /* end of line */
+      if(lineend)
         break;
 
-      /* leading double-quote means quoted string */
-      quoted = (*tok == '\"');
-
-      tok_end = tok;
-      if(!quoted) {
-        size_t len = 0;
-        CURLcode result;
-        while(*tok_end > ' ') {
-          tok_end++;
-          len++;
-        }
-        if(!len) {
-          retcode = NETRC_SYNTAX_ERROR;
-          goto out;
-        }
-        result = curlx_dyn_addn(&token, tok, len);
-        if(result) {
-          retcode = curl2netrc(result);
-          goto out;
-        }
+      ret = netrc_handle_token(&ns, tok, host);
+      if(ret) {
+        ns.retcode = ret;
+        goto out;
       }
-      else {
-        bool escape = FALSE;
-        bool endquote = FALSE;
-        tok_end++; /* pass the leading quote */
-        while(*tok_end) {
-          CURLcode result;
-          char s = *tok_end;
-          if(escape) {
-            escape = FALSE;
-            switch(s) {
-            case 'n':
-              s = '\n';
-              break;
-            case 'r':
-              s = '\r';
-              break;
-            case 't':
-              s = '\t';
-              break;
-            }
-          }
-          else if(s == '\\') {
-            escape = TRUE;
-            tok_end++;
-            continue;
-          }
-          else if(s == '\"') {
-            tok_end++; /* pass the ending quote */
-            endquote = TRUE;
-            break;
-          }
-          result = curlx_dyn_addn(&token, &s, 1);
-          if(result) {
-            retcode = curl2netrc(result);
-            goto out;
-          }
-          tok_end++;
-        }
-        if(escape || !endquote) {
-          /* bad syntax, get out */
-          retcode = NETRC_SYNTAX_ERROR;
-          goto out;
-        }
-      }
-
-      if(curlx_dyn_len(&token))
-        tok = curlx_dyn_ptr(&token);
-      else
-        /* since tok might actually be NULL for no content, set it to blank
-           to avoid having to deal with it being NULL */
-        tok = "";
-
-      switch(state) {
-      case NOTHING:
-        if(curl_strequal("macdef", tok))
-          /* Define a macro. A macro is defined with the specified name; its
-             contents begin with the next .netrc line and continue until a
-             null line (consecutive new-line characters) is encountered. */
-          state = MACDEF;
-        else if(curl_strequal("machine", tok)) {
-          /* the next tok is the machine name, this is in itself the delimiter
-             that starts the stuff entered for this machine, after this we
-             need to search for 'login' and 'password'. */
-          state = HOSTFOUND;
-          keyword = NONE;
-          found = 0;
-          our_login = FALSE;
-          Curl_safefree(password);
-          if(!specific_login)
-            Curl_safefree(login);
-        }
-        else if(curl_strequal("default", tok)) {
-          state = HOSTVALID;
-          retcode = NETRC_OK; /* we did find our host */
-        }
-        break;
-      case MACDEF:
-        if(!*tok)
-          state = NOTHING;
-        break;
-      case HOSTFOUND:
-        if(curl_strequal(host, tok)) {
-          /* and yes, this is our host! */
-          state = HOSTVALID;
-          retcode = NETRC_OK; /* we did find our host */
-        }
-        else
-          /* not our host */
-          state = NOTHING;
-        break;
-      case HOSTVALID:
-        /* we are now parsing sub-keywords concerning "our" host */
-        if(keyword == LOGIN) {
-          if(specific_login)
-            our_login = !Curl_timestrcmp(login, tok);
-          else {
-            our_login = TRUE;
-            curlx_free(login);
-            login = curlx_strdup(tok);
-            if(!login) {
-              retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
-              goto out;
-            }
-          }
-          found |= FOUND_LOGIN;
-          keyword = NONE;
-        }
-        else if(keyword == PASSWORD) {
-          curlx_free(password);
-          password = curlx_strdup(tok);
-          if(!password) {
-            retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
-            goto out;
-          }
-          if(!specific_login || our_login)
-            found |= FOUND_PASSWORD;
-          keyword = NONE;
-        }
-        else if(curl_strequal("login", tok))
-          keyword = LOGIN;
-        else if(curl_strequal("password", tok))
-          keyword = PASSWORD;
-        else if(curl_strequal("machine", tok)) {
-          /* a new machine here */
-          if(found & FOUND_PASSWORD) {
-            done = TRUE;
-            break;
-          }
-          state = HOSTFOUND;
-          keyword = NONE;
-          found = 0;
-          Curl_safefree(password);
-          if(!specific_login)
-            Curl_safefree(login);
-        }
-        else if(curl_strequal("default", tok)) {
-          state = HOSTVALID;
-          retcode = NETRC_OK; /* we did find our host */
-          Curl_safefree(password);
-          if(!specific_login)
-            Curl_safefree(login);
-        }
-        if((found == (FOUND_PASSWORD | FOUND_LOGIN)) && our_login) {
-          done = TRUE;
-          break;
-        }
-        break;
-      } /* switch (state) */
+      /* tok_end cannot point to a null byte here since lines are always
+         newline terminated */
+      DEBUGASSERT(*tok_end);
       tok = ++tok_end;
     }
-    if(!done) {
+    if(!ns.done) {
       const char *nl = NULL;
       if(tok)
         nl = strchr(tok, '\n');
@@ -338,32 +466,7 @@
 
 out:
   curlx_dyn_free(&token);
-  if(!retcode) {
-    if(!password && our_login) {
-      /* success without a password, set a blank one */
-      password = curlx_strdup("");
-      if(!password)
-        retcode = NETRC_OUT_OF_MEMORY; /* out of memory */
-    }
-    else if(!login && !password)
-      /* a default with no credentials */
-      retcode = NETRC_NO_MATCH;
-  }
-  if(!retcode) {
-    /* success */
-    if(!specific_login)
-      *loginp = login;
-    *passwordp = password;
-  }
-  else {
-    curlx_dyn_free(filebuf);
-    store->loaded = FALSE;
-    if(!specific_login)
-      curlx_free(login);
-    curlx_free(password);
-  }
-
-  return retcode;
+  return netrc_finalize(&ns, loginp, passwordp, store);
 }
 
 const char *Curl_netrc_strerror(NETRCcode ret)
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h
index 061e1e4..90318c2 100644
--- a/Utilities/cmcurl/lib/netrc.h
+++ b/Utilities/cmcurl/lib/netrc.h
@@ -58,7 +58,6 @@
  */
 #else
 /* disabled */
-#define Curl_parsenetrc(a, b, c, d, e, f) 1
 #define Curl_netrc_init(x)
 #define Curl_netrc_cleanup(x)
 #endif
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c
index ee03fd3..05c59a0 100644
--- a/Utilities/cmcurl/lib/noproxy.c
+++ b/Utilities/cmcurl/lib/noproxy.c
@@ -38,12 +38,17 @@
 #endif
 
 /*
- * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the
+ * cidr4_match() returns TRUE if the given IPv4 address is within the
  * specified CIDR address range.
+ *
+ * @unittest 1614
  */
-UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
-                               const char *network, /* 1.2.3.4 address */
-                               unsigned int bits)
+UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                          const char *network, /* 1.2.3.4 address */
+                          unsigned int bits);
+UNITTEST bool cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                          const char *network, /* 1.2.3.4 address */
+                          unsigned int bits)
 {
   unsigned int address = 0;
   unsigned int check = 0;
@@ -74,9 +79,11 @@
   return address == check;
 }
 
-UNITTEST bool Curl_cidr6_match(const char *ipv6,
-                               const char *network,
-                               unsigned int bits)
+/* @unittest 1614 */
+UNITTEST bool cidr6_match(const char *ipv6, const char *network,
+                          unsigned int bits);
+UNITTEST bool cidr6_match(const char *ipv6, const char *network,
+                          unsigned int bits)
 {
 #ifdef USE_IPV6
   unsigned int bytes;
@@ -170,9 +177,9 @@
     *slash = 0; /* null-terminate there */
   }
   if(type == TYPE_IPV6)
-    return Curl_cidr6_match(name, checkip, bits);
+    return cidr6_match(name, checkip, bits);
   else
-    return Curl_cidr4_match(name, checkip, bits);
+    return cidr4_match(name, checkip, bits);
 }
 
 /****************************************************************
diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h
index 71ae7ea..e16c139 100644
--- a/Utilities/cmcurl/lib/noproxy.h
+++ b/Utilities/cmcurl/lib/noproxy.h
@@ -26,17 +26,6 @@
 #include "curl_setup.h"
 
 #ifndef CURL_DISABLE_PROXY
-
-#ifdef UNITTESTS
-
-UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
-                               const char *network, /* 1.2.3.4 address */
-                               unsigned int bits);
-UNITTEST bool Curl_cidr6_match(const char *ipv6,
-                               const char *network,
-                               unsigned int bits);
-#endif
-
 bool Curl_check_noproxy(const char *name, const char *no_proxy);
 #endif
 
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index 95f7681..48bf5b7 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -444,18 +444,18 @@
     struct connectdata *conn = data->conn;
     if(conn) {
       struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
-      CURLcode err = CURLE_RECV_ERROR;
+      CURLcode result = CURLE_RECV_ERROR;
       size_t nread;
 
       if(!li) {
         SET_SOCKERRNO(SOCKEINVAL);
         return -1;
       }
-      err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread);
-      if(err == CURLE_AGAIN) {
+      result = (li->recv)(data, FIRSTSOCKET, buf, len, &nread);
+      if(result == CURLE_AGAIN) {
         SET_SOCKERRNO(SOCKEWOULDBLOCK);
       }
-      ret = err ? -1 : (ber_slen_t)nread;
+      ret = result ? -1 : (ber_slen_t)nread;
     }
   }
   return ret;
@@ -470,18 +470,18 @@
     struct connectdata *conn = data->conn;
     if(conn) {
       struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
-      CURLcode err = CURLE_SEND_ERROR;
+      CURLcode result = CURLE_SEND_ERROR;
       size_t nwritten;
 
       if(!li) {
         SET_SOCKERRNO(SOCKEINVAL);
         return -1;
       }
-      err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten);
-      if(err == CURLE_AGAIN) {
+      result = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten);
+      if(result == CURLE_AGAIN) {
         SET_SOCKERRNO(SOCKEWOULDBLOCK);
       }
-      ret = err ? -1 : (ber_slen_t)nwritten;
+      ret = result ? -1 : (ber_slen_t)nwritten;
     }
   }
   return ret;
@@ -614,11 +614,10 @@
   if(result)
     goto out;
 
-  hosturl = curl_maprintf("%s://%s%s%s:%d",
+  hosturl = curl_maprintf("%s://%s:%d",
                           conn->scheme->name,
-                          conn->bits.ipv6_ip ? "[" : "",
-                          conn->host.name,
-                          conn->bits.ipv6_ip ? "]" : "",
+                          (data->state.up.hostname[0] == '[') ?
+                          data->state.up.hostname : conn->host.name,
                           conn->remote_port);
   if(!hosturl) {
     result = CURLE_OUT_OF_MEMORY;
@@ -694,13 +693,19 @@
 static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
                                        LDAPMessage *msg, int code)
 {
-  struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+  struct connectdata *conn;
+  struct ldapconninfo *li;
   int rc;
   BerElement *ber = NULL;
   CURLcode result = CURLE_OK;
   struct berval bv, *bvals;
 
+  if(!data)
+    return CURLE_FAILED_INIT;
+
+  conn = data->conn;
+  li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+
   if(!li)
     return CURLE_FAILED_INIT;
   switch(ldap_msgtype(msg)) {
@@ -1276,7 +1281,7 @@
   oldap_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c
index 7efedec..ce358bd 100644
--- a/Utilities/cmcurl/lib/parsedate.c
+++ b/Utilities/cmcurl/lib/parsedate.c
@@ -25,6 +25,7 @@
 
 #include "parsedate.h"
 #include "curlx/strparse.h"
+#include "curlx/strcopy.h"
 
 /*
   A brief summary of the date string formats this parser groks:
@@ -98,9 +99,7 @@
 #ifndef CURL_DISABLE_PARSEDATE
 
 #define PARSEDATE_LATER  1
-#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5)
 #define PARSEDATE_SOONER 2
-#endif
 
 static const char * const weekday[] = {
   "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
@@ -108,89 +107,83 @@
 
 struct tzinfo {
   char name[5];
-  int offset; /* +/- in minutes */
+  int16_t offset; /* +/- in minutes */
 };
 
-/* Here's a bunch of frequently used time zone names. These were supported
-   by the old getdate parser. */
 #define tDAYZONE (-60)         /* offset for daylight savings time */
+
+/* alpha-sorted list of time zones */
 static const struct tzinfo tz[] = {
-  { "GMT",     0 },            /* Greenwich Mean */
-  { "UT",      0 },            /* Universal Time */
-  { "UTC",     0 },            /* Universal (Coordinated) */
-  { "WET",     0 },            /* Western European */
-  { "BST",     0 + tDAYZONE }, /* British Summer */
-  { "WAT",    60 },            /* West Africa */
-  { "AST",   240 },            /* Atlantic Standard */
+  { "A", -1 * 60 },            /* Alpha */
   { "ADT",   240 + tDAYZONE }, /* Atlantic Daylight */
-  { "EST",   300 },            /* Eastern Standard */
-  { "EDT",   300 + tDAYZONE }, /* Eastern Daylight */
-  { "CST",   360 },            /* Central Standard */
-  { "CDT",   360 + tDAYZONE }, /* Central Daylight */
-  { "MST",   420 },            /* Mountain Standard */
-  { "MDT",   420 + tDAYZONE }, /* Mountain Daylight */
-  { "PST",   480 },            /* Pacific Standard */
-  { "PDT",   480 + tDAYZONE }, /* Pacific Daylight */
-  { "YST",   540 },            /* Yukon Standard */
-  { "YDT",   540 + tDAYZONE }, /* Yukon Daylight */
-  { "HST",   600 },            /* Hawaii Standard */
-  { "HDT",   600 + tDAYZONE }, /* Hawaii Daylight */
-  { "CAT",   600 },            /* Central Alaska */
   { "AHST",  600 },            /* Alaska-Hawaii Standard */
-  { "NT",    660 },            /* Nome */ /* spellchecker:disable-line */
-  { "IDLW",  720 },            /* International Date Line West */
+  { "AST",   240 },            /* Atlantic Standard */
+  { "B", -2 * 60 },            /* Bravo */
+  { "BST",     0 + tDAYZONE }, /* British Summer */
+  { "C", -3 * 60 },            /* Charlie */
+  { "CAT",   600 },            /* Central Alaska */
+  { "CCT",  -480 },            /* China Coast, USSR Zone 7 */
+  { "CDT",   360 + tDAYZONE }, /* Central Daylight */
+  { "CEST",  -60 + tDAYZONE }, /* Central European Summer */
   { "CET",   -60 },            /* Central European */
+  { "CST",   360 },            /* Central Standard */
+  { "D", -4 * 60 },            /* Delta */
+  { "E", -5 * 60 },            /* Echo */
+  { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */
+  { "EAST", -600 },            /* Eastern Australian Standard */
+  { "EDT",   300 + tDAYZONE }, /* Eastern Daylight */
+  { "EET",  -120 },            /* Eastern Europe, USSR Zone 1 */
+  { "EST",   300 },            /* Eastern Standard */
+  { "F", -6 * 60 },            /* Foxtrot */
+  { "FST",   -60 + tDAYZONE }, /* French Summer */
+  { "FWT",   -60 },            /* French Winter */
+  { "G", -7 * 60 },            /* Golf */
+  { "GMT",     0 },            /* Greenwich Mean */
+  { "GST",  -600 },            /* Guam Standard, USSR Zone 9 */
+  { "H", -8 * 60 },            /* Hotel */
+  { "HDT",   600 + tDAYZONE }, /* Hawaii Daylight */
+  { "HST",   600 },            /* Hawaii Standard */
+  { "I", -9 * 60 },            /* India */
+  { "IDLE", -720 },            /* International Date Line East */
+  { "IDLW",  720 },            /* International Date Line West */
+  { "JST",  -540 },            /* Japan Standard, USSR Zone 8 */
+  { "K", -10 * 60 },           /* Kilo */
+  { "L", -11 * 60 },           /* Lima */
+  { "M", -12 * 60 },           /* Mike */
+  { "MDT",   420 + tDAYZONE }, /* Mountain Daylight */
+  { "MEST",  -60 + tDAYZONE }, /* Middle European Summer */
+  { "MESZ",  -60 + tDAYZONE }, /* Middle European Summer */
   { "MET",   -60 },            /* Middle European */
   { "MEWT",  -60 },            /* Middle European Winter */
-  { "MEST",  -60 + tDAYZONE }, /* Middle European Summer */
-  { "CEST",  -60 + tDAYZONE }, /* Central European Summer */
-  { "MESZ",  -60 + tDAYZONE }, /* Middle European Summer */
-  { "FWT",   -60 },            /* French Winter */
-  { "FST",   -60 + tDAYZONE }, /* French Summer */
-  { "EET",  -120 },            /* Eastern Europe, USSR Zone 1 */
+  { "MST",   420 },            /* Mountain Standard */
+  { "N",      60 },            /* November */
+  { "NT",    660 },            /* Nome */ /* spellchecker:disable-line */
+  { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */
+  { "NZST", -720 },            /* New Zealand Standard */
+  { "NZT",  -720 },            /* New Zealand */
+  { "O",  2 * 60 },            /* Oscar */
+  { "P",  3 * 60 },            /* Papa */
+  { "PDT",   480 + tDAYZONE }, /* Pacific Daylight */
+  { "PST",   480 },            /* Pacific Standard */
+  { "Q",  4 * 60 },            /* Quebec */
+  { "R",  5 * 60 },            /* Romeo */
+  { "S",  6 * 60 },            /* Sierra */
+  { "T",  7 * 60 },            /* Tango */
+  { "U",  8 * 60 },            /* Uniform */
+  { "UT",      0 },            /* Universal Time */
+  { "UTC",     0 },            /* Universal (Coordinated) */
+  { "V",  9 * 60 },            /* Victor */
+  { "W", 10 * 60 },            /* Whiskey */
+  { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */
   { "WAST", -420 }, /* spellchecker:disable-line */
                                /* West Australian Standard */
-  { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */
-  { "CCT",  -480 },            /* China Coast, USSR Zone 7 */
-  { "JST",  -540 },            /* Japan Standard, USSR Zone 8 */
-  { "EAST", -600 },            /* Eastern Australian Standard */
-  { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */
-  { "GST",  -600 },            /* Guam Standard, USSR Zone 9 */
-  { "NZT",  -720 },            /* New Zealand */
-  { "NZST", -720 },            /* New Zealand Standard */
-  { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */
-  { "IDLE", -720 },            /* International Date Line East */
-  /* Next up: Military timezone names. RFC822 allowed these, but (as noted in
-     RFC 1123) had their signs wrong. Here we use the correct signs to match
-     actual military usage.
-   */
-  { "A",   1 * 60 },           /* Alpha */
-  { "B",   2 * 60 },           /* Bravo */
-  { "C",   3 * 60 },           /* Charlie */
-  { "D",   4 * 60 },           /* Delta */
-  { "E",   5 * 60 },           /* Echo */
-  { "F",   6 * 60 },           /* Foxtrot */
-  { "G",   7 * 60 },           /* Golf */
-  { "H",   8 * 60 },           /* Hotel */
-  { "I",   9 * 60 },           /* India */
-  /* "J", Juliet is not used as a timezone, to indicate the observer's local
-     time */
-  { "K",  10 * 60 },           /* Kilo */
-  { "L",  11 * 60 },           /* Lima */
-  { "M",  12 * 60 },           /* Mike */
-  { "N",  -1 * 60 },           /* November */
-  { "O",  -2 * 60 },           /* Oscar */
-  { "P",  -3 * 60 },           /* Papa */
-  { "Q",  -4 * 60 },           /* Quebec */
-  { "R",  -5 * 60 },           /* Romeo */
-  { "S",  -6 * 60 },           /* Sierra */
-  { "T",  -7 * 60 },           /* Tango */
-  { "U",  -8 * 60 },           /* Uniform */
-  { "V",  -9 * 60 },           /* Victor */
-  { "W", -10 * 60 },           /* Whiskey */
-  { "X", -11 * 60 },           /* X-ray */
-  { "Y", -12 * 60 },           /* Yankee */
-  { "Z", 0 },                  /* Zulu, zero meridian, a.k.a. UTC */
+  { "WAT",    60 },            /* West Africa */
+  { "WET",     0 },            /* Western European */
+  { "X", 11 * 60 },            /* X-ray */
+  { "Y", 12 * 60 },            /* Yankee */
+  { "YDT",   540 + tDAYZONE }, /* Yukon Daylight */
+  { "YST",   540 },            /* Yukon Standard */
+  { "Z",       0 },            /* Zulu, zero meridian, a.k.a. UTC */
 };
 
 /* returns:
@@ -233,22 +226,24 @@
   return -1; /* return the offset or -1, no real offset is -1 */
 }
 
-/* return the time zone offset between GMT and the input one, in number
-   of seconds or -1 if the timezone was not found/legal */
+static int tzcompare(const void *m1, const void *m2)
+{
+  const struct tzinfo *tz1 = m1;
+  const struct tzinfo *tz2 = m2;
+  return strcmp(tz1->name, tz2->name);
+}
 
+/* return the time zone offset between GMT and the input one, in number of
+   seconds or -1 if the timezone was not found/legal */
 static int checktz(const char *check, size_t len)
 {
-  unsigned int i;
-  const struct tzinfo *what = tz;
-  if(len > 4) /* longer than any valid timezone */
-    return -1;
-
-  for(i = 0; i < CURL_ARRAYSIZE(tz); i++) {
-    size_t ilen = strlen(what->name);
-    if((ilen == len) &&
-       curl_strnequal(check, what->name, len))
+  if(len <= 4) {
+    const struct tzinfo *what;
+    struct tzinfo find;
+    curlx_strcopy(find.name, sizeof(find.name), check, len);
+    what = bsearch(&find, tz, CURL_ARRAYSIZE(tz), sizeof(tz[0]), tzcompare);
+    if(what)
       return what->offset * 60;
-    what++;
   }
   return -1;
 }
@@ -260,29 +255,42 @@
     (*date)++;
 }
 
+/* each field is exactly -1 when unknown */
+struct when {
+  int wday;  /* day of the week, 0-6 (mon-sun) */
+  int mon;   /* month of the year, 0-11 */
+  int mday;  /* day of month, 1 - 31 */
+  int hour;  /* hour of day, 0 - 23 */
+  int min;   /* minute of hour, 0 - 59 */
+  int sec;   /* second of minute, 0 - 60 (leap second) */
+  int year;  /* year, >= 1583 */
+  int tzoff; /* time zone offset in seconds */
+};
+
 enum assume {
   DATE_MDAY,
   DATE_YEAR,
   DATE_TIME
 };
 
+/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */
+#define LEAP_DAYS_BEFORE_1969 477
+
 /*
  * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to
  * mktime but for GMT only.
  */
-static time_t time2epoch(int sec, int min, int hour,
-                         int mday, int mon, int year)
+static curl_off_t time2epoch(struct when *w)
 {
-  static const int month_days_cumulative[12] = {
+  static const int cumulative_days[12] = {
     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
   };
-  int leap_days = year - (mon <= 1);
-  leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400)
-               - (1969 / 4) + (1969 / 100) - (1969 / 400));
-  return ((((((((time_t)(year - 1970) * 365)
-    + leap_days + month_days_cumulative[mon] + mday - 1) * 24)
-    + hour) * 60)
-    + min) * 60) + sec;
+  int y = w->year - (w->mon <= 1);
+  int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969;
+  curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days +
+    cumulative_days[w->mon] + w->mday - 1;
+
+  return (((days * 24 + w->hour) * 60 + w->min) * 60) + w->sec;
 }
 
 /* Returns the value of a single-digit or two-digit decimal number, return
@@ -300,8 +308,7 @@
 }
 
 /* HH:MM:SS or HH:MM and accept single-digits too */
-static bool match_time(const char *date,
-                       int *h, int *m, int *s, char **endp)
+static bool match_time(const char *date, struct when *w, char **endp)
 {
   const char *p;
   int hh, mm, ss = 0;
@@ -324,9 +331,9 @@
   }
   return FALSE; /* not a time string */
 match:
-  *h = hh;
-  *m = mm;
-  *s = ss;
+  w->hour = hh;
+  w->min = mm;
+  w->sec = ss;
   *endp = (char *)CURL_UNCONST(p);
   return TRUE;
 }
@@ -345,209 +352,221 @@
 /* Wednesday is the longest name this parser knows about */
 #define NAME_LEN 12
 
+static void initwhen(struct when *w)
+{
+  w->wday = w->mon = w->mday = w->hour = w->min = w->sec = w->year = w->tzoff =
+    -1;
+}
+
+static int datestring(const char **datep, struct when *w)
+{
+  /* a name coming up */
+  size_t len = 0;
+  const char *p = *datep;
+  bool found = FALSE;
+  while(ISALPHA(*p) && (len < NAME_LEN)) {
+    p++;
+    len++;
+  }
+
+  if(len != NAME_LEN) {
+    if(w->wday == -1) {
+      w->wday = checkday(*datep, len);
+      if(w->wday != -1)
+        found = TRUE;
+    }
+    if(!found && (w->mon == -1)) {
+      w->mon = checkmonth(*datep, len);
+      if(w->mon != -1)
+        found = TRUE;
+    }
+
+    if(!found && (w->tzoff == -1)) {
+      /* this must be a time zone string */
+      w->tzoff = checktz(*datep, len);
+      if(w->tzoff != -1)
+        found = TRUE;
+    }
+  }
+  if(!found)
+    return PARSEDATE_FAIL; /* bad string */
+
+  *datep += len;
+  return PARSEDATE_OK;
+}
+
+static int datenum(const char *indate, const char **datep, struct when *w,
+                   enum assume *dignextp)
+{
+  /* a digit */
+  unsigned int val;
+  char *end;
+  const char *date = *datep;
+  enum assume dignext = *dignextp;
+
+  if((w->sec == -1) && match_time(date, w, &end)) {
+    /* time stamp */
+    date = end;
+  }
+  else {
+    bool found = FALSE;
+    curl_off_t lval;
+    int num_digits = 0;
+    const char *p = *datep;
+    if(curlx_str_number(&p, &lval, 99999999))
+      return PARSEDATE_FAIL;
+
+    /* we know num_digits cannot be larger than 8 */
+    num_digits = (int)(p - *datep);
+    val = (unsigned int)lval;
+
+    if((w->tzoff == -1) &&
+       (num_digits == 4) &&
+       (val <= 1400) &&
+       (indate < date) &&
+       (date[-1] == '+' || date[-1] == '-')) {
+      /* four digits and a value less than or equal to 1400 (to take into
+         account all sorts of funny time zone diffs) and it is preceded
+         with a plus or minus. This is a time zone indication. 1400 is
+         picked since +1300 is frequently used and +1400 is mentioned as
+         an edge number in the document "ISO C 200X Proposal: Timezone
+         Functions" at http://david.tribble.com/text/c0xtimezone.html If
+         anyone has a more authoritative source for the exact maximum time
+         zone offsets, please speak up! */
+      found = TRUE;
+      w->tzoff = ((val / 100 * 60) + (val % 100)) * 60;
+
+      /* the + and - prefix indicates the local time compared to GMT,
+         this we need their reversed math to get what we want */
+      w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff;
+    }
+
+    else if((num_digits == 8) && (w->year == -1) &&
+            (w->mon == -1) && (w->mday == -1)) {
+      /* 8 digits, no year, month or day yet. This is YYYYMMDD */
+      found = TRUE;
+      w->year = val / 10000;
+      w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
+      w->mday = val % 100;
+    }
+
+    if(!found && (dignext == DATE_MDAY) && (w->mday == -1)) {
+      if((val > 0) && (val < 32)) {
+        w->mday = val;
+        found = TRUE;
+      }
+      dignext = DATE_YEAR;
+    }
+
+    if(!found && (dignext == DATE_YEAR) && (w->year == -1)) {
+      w->year = val;
+      found = TRUE;
+      if(w->year < 100) {
+        if(w->year > 70)
+          w->year += 1900;
+        else
+          w->year += 2000;
+      }
+      if(w->mday == -1)
+        dignext = DATE_MDAY;
+    }
+
+    if(!found)
+      return PARSEDATE_FAIL;
+
+    date = p;
+  }
+  *datep = date;
+  *dignextp = dignext;
+  return PARSEDATE_OK;
+}
+
+static int datecheck(struct when *w)
+{
+  if(w->sec == -1)
+    w->sec = w->min = w->hour = 0; /* no time, make it zero */
+
+  if((w->mday == -1) || (w->mon == -1) || (w->year == -1))
+    /* lacks vital info, fail */
+    return PARSEDATE_FAIL;
+
+  /* The Gregorian calendar was introduced 1582 */
+  else if(w->year < 1583)
+    return PARSEDATE_FAIL;
+
+  else if((w->mday > 31) || (w->mon > 11) || (w->hour > 23) ||
+          (w->min > 59) || (w->sec > 60))
+    return PARSEDATE_FAIL; /* clearly an illegal date */
+
+  return PARSEDATE_OK;
+}
+
+static void tzadjust(curl_off_t *tp, struct when *w)
+{
+  if(w->tzoff == -1) /* unknown tz means no offset */
+    w->tzoff = 0;
+
+  /* Add the time zone diff between local time zone and GMT. */
+  if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff)))
+    *tp = CURL_OFF_T_MAX;
+  else
+    *tp += w->tzoff;
+  /* this needs no minimum check since we require a year > 1582 */
+}
+
+static int mktimet(curl_off_t seconds, time_t *output)
+{
+#if SIZEOF_TIME_T < 5
+  if(seconds > TIME_T_MAX) {
+    *output = TIME_T_MAX;
+    return PARSEDATE_LATER;
+  }
+  else if(seconds < TIME_T_MIN) {
+    *output = TIME_T_MIN;
+    return PARSEDATE_SOONER;
+  }
+#elif defined(HAVE_TIME_T_UNSIGNED)
+  if(seconds < 0) {
+    *output = 0;
+    return PARSEDATE_SOONER;
+  }
+#endif
+  *output = (time_t)seconds;
+  return PARSEDATE_OK;
+}
+
 static int parsedate(const char *date, time_t *output)
 {
-  time_t t = 0;
-  int wdaynum = -1;  /* day of the week number, 0-6 (mon-sun) */
-  int monnum = -1;   /* month of the year number, 0-11 */
-  int mdaynum = -1;  /* day of month, 1 - 31 */
-  int hournum = -1;
-  int minnum = -1;
-  int secnum = -1;
-  int yearnum = -1;
-  int tzoff = -1;
+  curl_off_t seconds = 0;
   enum assume dignext = DATE_MDAY;
   const char *indate = date; /* save the original pointer */
   int part = 0; /* max 6 parts */
+  int rc = 0;
+  struct when w;
+  initwhen(&w);
 
   while(*date && (part < 6)) {
-    bool found = FALSE;
-
     skip(&date);
 
-    if(ISALPHA(*date)) {
-      /* a name coming up */
-      size_t len = 0;
-      const char *p = date;
-      while(ISALPHA(*p) && (len < NAME_LEN)) {
-        p++;
-        len++;
-      }
-
-      if(len != NAME_LEN) {
-        if(wdaynum == -1) {
-          wdaynum = checkday(date, len);
-          if(wdaynum != -1)
-            found = TRUE;
-        }
-        if(!found && (monnum == -1)) {
-          monnum = checkmonth(date, len);
-          if(monnum != -1)
-            found = TRUE;
-        }
-
-        if(!found && (tzoff == -1)) {
-          /* this must be a time zone string */
-          tzoff = checktz(date, len);
-          if(tzoff != -1)
-            found = TRUE;
-        }
-      }
-      if(!found)
-        return PARSEDATE_FAIL; /* bad string */
-
-      date += len;
-    }
-    else if(ISDIGIT(*date)) {
-      /* a digit */
-      unsigned int val;
-      char *end;
-      if((secnum == -1) &&
-         match_time(date, &hournum, &minnum, &secnum, &end)) {
-        /* time stamp */
-        date = end;
-      }
-      else {
-        curl_off_t lval;
-        int num_digits = 0;
-        const char *p = date;
-        if(curlx_str_number(&p, &lval, 99999999))
-          return PARSEDATE_FAIL;
-
-        /* we know num_digits cannot be larger than 8 */
-        num_digits = (int)(p - date);
-        val = (unsigned int)lval;
-
-        if((tzoff == -1) &&
-           (num_digits == 4) &&
-           (val <= 1400) &&
-           (indate < date) &&
-           (date[-1] == '+' || date[-1] == '-')) {
-          /* four digits and a value less than or equal to 1400 (to take into
-             account all sorts of funny time zone diffs) and it is preceded
-             with a plus or minus. This is a time zone indication. 1400 is
-             picked since +1300 is frequently used and +1400 is mentioned as
-             an edge number in the document "ISO C 200X Proposal: Timezone
-             Functions" at http://david.tribble.com/text/c0xtimezone.html If
-             anyone has a more authoritative source for the exact maximum time
-             zone offsets, please speak up! */
-          found = TRUE;
-          tzoff = ((val / 100 * 60) + (val % 100)) * 60;
-
-          /* the + and - prefix indicates the local time compared to GMT,
-             this we need their reversed math to get what we want */
-          tzoff = date[-1] == '+' ? -tzoff : tzoff;
-        }
-
-        else if((num_digits == 8) &&
-                (yearnum == -1) &&
-                (monnum == -1) &&
-                (mdaynum == -1)) {
-          /* 8 digits, no year, month or day yet. This is YYYYMMDD */
-          found = TRUE;
-          yearnum = val / 10000;
-          monnum = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
-          mdaynum = val % 100;
-        }
-
-        if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) {
-          if((val > 0) && (val < 32)) {
-            mdaynum = val;
-            found = TRUE;
-          }
-          dignext = DATE_YEAR;
-        }
-
-        if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) {
-          yearnum = val;
-          found = TRUE;
-          if(yearnum < 100) {
-            if(yearnum > 70)
-              yearnum += 1900;
-            else
-              yearnum += 2000;
-          }
-          if(mdaynum == -1)
-            dignext = DATE_MDAY;
-        }
-
-        if(!found)
-          return PARSEDATE_FAIL;
-
-        date = p;
-      }
-    }
+    if(ISALPHA(*date))
+      rc = datestring(&date, &w);
+    else if(ISDIGIT(*date))
+      rc = datenum(indate, &date, &w, &dignext);
+    if(rc)
+      return rc;
 
     part++;
   }
 
-  if(secnum == -1)
-    secnum = minnum = hournum = 0; /* no time, make it zero */
+  rc = datecheck(&w);
+  if(rc)
+    return rc;
 
-  if((mdaynum == -1) ||
-     (monnum == -1) ||
-     (yearnum == -1))
-    /* lacks vital info, fail */
-    return PARSEDATE_FAIL;
+  seconds = time2epoch(&w); /* get number of seconds */
+  tzadjust(&seconds, &w); /* handle the time zone offset */
+  rc = mktimet(seconds, output); /* squeeze seconds into a time_t */
 
-#ifdef HAVE_TIME_T_UNSIGNED
-  if(yearnum < 1970) {
-    /* only positive numbers cannot return earlier */
-    *output = TIME_T_MIN;
-    return PARSEDATE_SOONER;
-  }
-#endif
-
-#if (SIZEOF_TIME_T < 5)
-
-#ifdef HAVE_TIME_T_UNSIGNED
-  /* an unsigned 32-bit time_t can only hold dates to 2106 */
-  if(yearnum > 2105) {
-    *output = TIME_T_MAX;
-    return PARSEDATE_LATER;
-  }
-#else
-  /* a signed 32-bit time_t can only hold dates to the beginning of 2038 */
-  if(yearnum > 2037) {
-    *output = TIME_T_MAX;
-    return PARSEDATE_LATER;
-  }
-  if(yearnum < 1903) {
-    *output = TIME_T_MIN;
-    return PARSEDATE_SOONER;
-  }
-#endif
-
-#else
-  /* The Gregorian calendar was introduced 1582 */
-  if(yearnum < 1583)
-    return PARSEDATE_FAIL;
-#endif
-
-  if((mdaynum > 31) || (monnum > 11) ||
-     (hournum > 23) || (minnum > 59) || (secnum > 60))
-    return PARSEDATE_FAIL; /* clearly an illegal date */
-
-  /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on
-     architectures that feature a 64 bits 'long' but ultimately time_t is the
-     correct data type to use.
-  */
-  t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum);
-
-  /* Add the time zone diff between local time zone and GMT. */
-  if(tzoff == -1)
-    tzoff = 0;
-
-  if((tzoff > 0) && (t > (time_t)(TIME_T_MAX - tzoff))) {
-    *output = TIME_T_MAX;
-    return PARSEDATE_LATER; /* time_t overflow */
-  }
-
-  t += tzoff;
-
-  *output = t;
-
-  return PARSEDATE_OK;
+  return rc;
 }
 #else
 /* disabled */
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 4ede0a5..44e4244 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -39,29 +39,21 @@
 #include "select.h"
 #include "progress.h"
 
-/* Returns timeout in ms. 0 or negative number means the timeout has already
-   triggered */
-timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
-                                 struct pingpong *pp)
+timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data,
+                                     struct pingpong *pp)
 {
-  timediff_t timeout_ms, xfer_timeout_ms;
-  timediff_t response_time = data->set.server_response_timeout ?
-    data->set.server_response_timeout : RESP_TIMEOUT;
+  timediff_t xfer_remain_ms;
+  timediff_t remain_ms = data->set.server_response_timeout ?
+    data->set.server_response_timeout : PINGPONG_TIMEOUT_MS;
 
-  /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
-     remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
-     supposed to govern the response for any given server response, not for
-     the time from connect to the given server response. */
-
-  /* Without a requested timeout, we only wait 'response_time' seconds for the
-     full response to arrive before we bail out */
-  timeout_ms = response_time -
-               curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response);
-  /* transfer timeout can be 0, which means no timeout applies */
-  xfer_timeout_ms = Curl_timeleft_ms(data);
-  if(xfer_timeout_ms && (xfer_timeout_ms < timeout_ms))
-    return xfer_timeout_ms;
-  return timeout_ms;
+  /* If the overall transfer has less time remaining than pingpong
+   * has otherwise for the state, return that. */
+  remain_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response);
+  /* transfer remaining time is 0, when it has no timeout. */
+  xfer_remain_ms = Curl_timeleft_ms(data);
+  if(xfer_remain_ms)
+    return CURLMIN(remain_ms, xfer_remain_ms);
+  return remain_ms;
 }
 
 /*
@@ -75,7 +67,7 @@
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int rc;
   timediff_t interval_ms;
-  timediff_t timeout_ms = Curl_pp_state_timeout(data, pp);
+  timediff_t timeout_ms = Curl_pp_state_timeleft_ms(data, pp);
   CURLcode result = CURLE_OK;
 
   if(timeout_ms <= 0) {
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h
index 33d2a92..864f2c6 100644
--- a/Utilities/cmcurl/lib/pingpong.h
+++ b/Utilities/cmcurl/lib/pingpong.h
@@ -70,6 +70,10 @@
                          read */
 };
 
+/* Default pingpong response timeout in milliseconds, unless a transfer
+ * has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */
+#define PINGPONG_TIMEOUT_MS      (60 * 1000)
+
 #define PINGPONG_SETUP(pp, s, e) \
   do {                           \
     (pp)->statemachine = s;      \
@@ -88,10 +92,10 @@
 /* initialize stuff to prepare for reading a fresh new response */
 void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow);
 
-/* Returns timeout in ms. 0 or negative number means the timeout has already
-   triggered */
-timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
-                                 struct pingpong *pp);
+/* Returns time remaining in ms. 0 or negative number means the
+  timeout has already triggered */
+timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data,
+                                     struct pingpong *pp);
 
 /***********************************************************************
  *
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index de704d1..317c04b 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -1075,7 +1075,6 @@
 {
   /* This code could be made into a special function in the handler struct */
   CURLcode result = CURLE_OK;
-  struct SingleRequest *k = &data->req;
   struct connectdata *conn = data->conn;
   struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   bool strip_dot = FALSE;
@@ -1185,7 +1184,7 @@
     message as per RFC-1939, sect. 3 */
     result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
 
-    k->keepon &= ~KEEP_RECV;
+    CURL_REQ_CLEAR_RECV(data);
     pop3c->eob = 0;
 
     return result;
@@ -1230,7 +1229,7 @@
      when there is no body to return. */
   pop3c->eob = 2;
 
-  /* But since this initial CR LF pair is not part of the actual body, we set
+  /* Since this initial CR LF pair is not part of the actual body, we set
      the strip counter here so that these bytes will not be delivered. */
   pop3c->strip = 2;
 
@@ -1480,8 +1479,8 @@
   }
 
   /* Cleanup our per-request based variables */
-  Curl_safefree(pop3->id);
-  Curl_safefree(pop3->custom);
+  curlx_safefree(pop3->id);
+  curlx_safefree(pop3->custom);
 
   /* Clear the transfer mode for the next request */
   pop3->transfer = PPTRANSFER_BODY;
@@ -1628,7 +1627,7 @@
   Curl_pp_disconnect(&pop3c->pp);
 
   /* Cleanup our connection based variables */
-  Curl_safefree(pop3c->apoptimestamp);
+  curlx_safefree(pop3c->apoptimestamp);
 
   return CURLE_OK;
 }
@@ -1656,8 +1655,8 @@
   (void)klen;
   DEBUGASSERT(pop3);
   /* Cleanup our per-request based variables */
-  Curl_safefree(pop3->id);
-  Curl_safefree(pop3->custom);
+  curlx_safefree(pop3->id);
+  curlx_safefree(pop3->custom);
   curlx_free(pop3);
 }
 
@@ -1668,7 +1667,7 @@
   (void)klen;
   DEBUGASSERT(pop3c);
   Curl_pp_disconnect(&pop3c->pp);
-  Curl_safefree(pop3c->apoptimestamp);
+  curlx_safefree(pop3c->apoptimestamp);
   curlx_free(pop3c);
 }
 
@@ -1692,7 +1691,7 @@
 /*
  * POP3 protocol.
  */
-static const struct Curl_protocol Curl_protocol_pop3 = {
+const struct Curl_protocol Curl_protocol_pop3 = {
   pop3_setup_connection,            /* setup_connection */
   pop3_do,                          /* do_it */
   pop3_done,                        /* done */
@@ -1707,43 +1706,9 @@
   pop3_disconnect,                  /* disconnect */
   pop3_write,                       /* write_resp */
   ZERO_NULL,                        /* write_resp_hd */
-  ZERO_NULL,                        /* connection_check */
+  ZERO_NULL,                        /* connection_is_dead */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* follow */
 };
 
 #endif /* CURL_DISABLE_POP3 */
-
-/*
- * POP3 protocol handler.
- */
-const struct Curl_scheme Curl_scheme_pop3 = {
-  "pop3",                           /* scheme */
-#ifdef CURL_DISABLE_POP3
-  ZERO_NULL,
-#else
-  &Curl_protocol_pop3,
-#endif
-  CURLPROTO_POP3,                   /* protocol */
-  CURLPROTO_POP3,                   /* family */
-  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
-  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
-  PORT_POP3,                        /* defport */
-};
-
-/*
- * POP3S protocol handler.
- */
-const struct Curl_scheme Curl_scheme_pop3s = {
-  "pop3s",                          /* scheme */
-#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_pop3,
-#endif
-  CURLPROTO_POP3S,                  /* protocol */
-  CURLPROTO_POP3,                   /* family */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
-  PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
-  PORT_POP3S,                       /* defport */
-};
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h
index 77e30f8..75c3a67 100644
--- a/Utilities/cmcurl/lib/pop3.h
+++ b/Utilities/cmcurl/lib/pop3.h
@@ -23,7 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_pop3;
-extern const struct Curl_scheme Curl_scheme_pop3s;
+#ifndef CURL_DISABLE_POP3
+extern const struct Curl_protocol Curl_protocol_pop3;
+#endif
 
 #endif /* HEADER_CURL_POP3_H */
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index 6cde567..919c151 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -33,7 +33,7 @@
 #ifndef CURL_DISABLE_PROGRESS_METER
 /* Provide a string that is 7 letters long (plus the zero byte).
 
-   Unit test 1636.
+   @unittest 1636
 */
 UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds);
 UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds)
@@ -83,7 +83,7 @@
    but never longer than 6 columns (+ one zero byte).
    Add suffix k, M, G when suitable...
 
-   Unit test 1636
+   @unittest 1636
 */
 UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen);
 UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen)
@@ -127,9 +127,11 @@
 }
 
 /*
- * @unittest: 1606
+ * @unittest 1606
  */
 UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data,
+                                  const struct curltime *pnow);
+UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data,
                                   const struct curltime *pnow)
 {
   if(!data->set.low_speed_time || !data->set.low_speed_limit ||
@@ -211,6 +213,7 @@
   Curl_pgrsSetUploadSize(data, -1);
   Curl_pgrsSetDownloadSize(data, -1);
   data->progress.speeder_c = 0; /* reset speed records */
+  data->progress.deliver = 0;
   pgrs_speedinit(data);
 }
 
@@ -339,6 +342,25 @@
   p->ul_size_known = FALSE;
 }
 
+/* check that the 'delta' amount of bytes are okay to deliver to the
+   application, or return error if not. */
+CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta)
+{
+  if(data->set.max_filesize &&
+     ((curl_off_t)delta > data->set.max_filesize - data->progress.deliver)) {
+    failf(data, "Would have exceeded max file size");
+    return CURLE_FILESIZE_EXCEEDED;
+  }
+  return CURLE_OK;
+}
+
+/* this counts how much data is delivered to the application, which
+   in compressed cases may differ from downloaded amount */
+void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta)
+{
+  data->progress.deliver += delta;
+}
+
 void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta)
 {
   if(delta) {
@@ -609,18 +631,18 @@
 static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress)
 {
   if(!data->progress.hide) {
+    int rc;
     if(data->set.fxferinfo) {
-      int result;
       /* There is a callback set, call that */
       Curl_set_in_callback(data, TRUE);
-      result = data->set.fxferinfo(data->set.progress_client,
-                                   data->progress.dl.total_size,
-                                   data->progress.dl.cur_size,
-                                   data->progress.ul.total_size,
-                                   data->progress.ul.cur_size);
+      rc = data->set.fxferinfo(data->set.progress_client,
+                               data->progress.dl.total_size,
+                               data->progress.dl.cur_size,
+                               data->progress.ul.total_size,
+                               data->progress.ul.cur_size);
       Curl_set_in_callback(data, FALSE);
-      if(result != CURL_PROGRESSFUNC_CONTINUE) {
-        if(result) {
+      if(rc != CURL_PROGRESSFUNC_CONTINUE) {
+        if(rc) {
           failf(data, "Callback aborted");
           return CURLE_ABORTED_BY_CALLBACK;
         }
@@ -628,17 +650,16 @@
       }
     }
     else if(data->set.fprogress) {
-      int result;
       /* The older deprecated callback is set, call that */
       Curl_set_in_callback(data, TRUE);
-      result = data->set.fprogress(data->set.progress_client,
-                                   (double)data->progress.dl.total_size,
-                                   (double)data->progress.dl.cur_size,
-                                   (double)data->progress.ul.total_size,
-                                   (double)data->progress.ul.cur_size);
+      rc = data->set.fprogress(data->set.progress_client,
+                               (double)data->progress.dl.total_size,
+                               (double)data->progress.dl.cur_size,
+                               (double)data->progress.ul.total_size,
+                               (double)data->progress.ul.cur_size);
       Curl_set_in_callback(data, FALSE);
-      if(result != CURL_PROGRESSFUNC_CONTINUE) {
-        if(result) {
+      if(rc != CURL_PROGRESSFUNC_CONTINUE) {
+        if(rc) {
           failf(data, "Callback aborted");
           return CURLE_ABORTED_BY_CALLBACK;
         }
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h
index 9cb0924..7d419ec 100644
--- a/Utilities/cmcurl/lib/progress.h
+++ b/Utilities/cmcurl/lib/progress.h
@@ -50,7 +50,8 @@
 void Curl_pgrsStartNow(struct Curl_easy *data);
 void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
 void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
-
+CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta);
+void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta);
 void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta);
 void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta);
 void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
@@ -82,9 +83,4 @@
 
 void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent);
 
-#ifdef UNITTESTS
-UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data,
-                                  const struct curltime *pnow);
-#endif
-
 #endif /* HEADER_CURL_PROGRESS_H */
diff --git a/Utilities/cmcurl/lib/protocol.c b/Utilities/cmcurl/lib/protocol.c
new file mode 100644
index 0000000..c8d4325
--- /dev/null
+++ b/Utilities/cmcurl/lib/protocol.c
@@ -0,0 +1,485 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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 "protocol.h"
+#include "strcase.h"
+
+#include "dict.h"
+#include "file.h"
+#include "ftp.h"
+#include "gopher.h"
+#include "http.h"
+#include "imap.h"
+#include "curl_ldap.h"
+#include "mqtt.h"
+#include "pop3.h"
+#include "rtsp.h"
+#include "smb.h"
+#include "smtp.h"
+#include "telnet.h"
+#include "tftp.h"
+#include "ws.h"
+#include "vssh/ssh.h"
+
+
+/* All URI schemes known to libcurl, but not necessarily implemented
+ * by protocol handlers. */
+const struct Curl_scheme Curl_scheme_dict = {
+  "dict",                               /* scheme */
+#ifdef CURL_DISABLE_DICT
+  ZERO_NULL,
+#else
+  &Curl_protocol_dict,
+#endif
+  CURLPROTO_DICT,                       /* protocol */
+  CURLPROTO_DICT,                       /* family */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY,    /* flags */
+  PORT_DICT,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_file = {
+  "file",                               /* scheme */
+#ifdef CURL_DISABLE_FILE
+  ZERO_NULL,
+#else
+  &Curl_protocol_file,
+#endif
+  CURLPROTO_FILE,                       /* protocol */
+  CURLPROTO_FILE,                       /* family */
+  PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */
+  0                                     /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_ftp = {
+  "ftp",                           /* scheme */
+#ifdef CURL_DISABLE_FTP
+  ZERO_NULL,
+#else
+  &Curl_protocol_ftp,
+#endif
+  CURLPROTO_FTP,                   /* protocol */
+  CURLPROTO_FTP,                   /* family */
+  PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
+  PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
+  PROTOPT_WILDCARD | PROTOPT_SSL_REUSE |
+  PROTOPT_CONN_REUSE, /* flags */
+  PORT_FTP,                        /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_ftps = {
+  "ftps",                          /* scheme */
+#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_ftp,
+#endif
+  CURLPROTO_FTPS,                  /* protocol */
+  CURLPROTO_FTP,                   /* family */
+  PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
+  PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD |
+  PROTOPT_CONN_REUSE, /* flags */
+  PORT_FTPS,                       /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_gopher = {
+  "gopher",                             /* scheme */
+#ifdef CURL_DISABLE_GOPHER
+  ZERO_NULL,
+#else
+  &Curl_protocol_gopher,
+#endif
+  CURLPROTO_GOPHER,                     /* protocol */
+  CURLPROTO_GOPHER,                     /* family */
+  PROTOPT_NONE,                         /* flags */
+  PORT_GOPHER,                          /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_gophers = {
+  "gophers",                            /* scheme */
+#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_gophers,
+#endif
+  CURLPROTO_GOPHERS,                    /* protocol */
+  CURLPROTO_GOPHER,                     /* family */
+  PROTOPT_SSL,                          /* flags */
+  PORT_GOPHER,                          /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_http = {
+  "http",                               /* scheme */
+#ifdef CURL_DISABLE_HTTP
+  ZERO_NULL,
+#else
+  &Curl_protocol_http,
+#endif
+  CURLPROTO_HTTP,                       /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
+  PORT_HTTP,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_https = {
+  "https",                              /* scheme */
+#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_http,
+#endif
+  CURLPROTO_HTTPS,                      /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
+  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
+  PORT_HTTPS,                           /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_imap = {
+  "imap",                           /* scheme */
+#ifdef CURL_DISABLE_IMAP
+  ZERO_NULL,
+#else
+  &Curl_protocol_imap,
+#endif
+  CURLPROTO_IMAP,                   /* protocol */
+  CURLPROTO_IMAP,                   /* family */
+  PROTOPT_CLOSEACTION |             /* flags */
+  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE |
+  PROTOPT_CONN_REUSE,
+  PORT_IMAP,                        /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_imaps = {
+  "imaps",                          /* scheme */
+#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_imap,
+#endif
+  CURLPROTO_IMAPS,                  /* protocol */
+  CURLPROTO_IMAP,                   /* family */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
+  PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
+  PORT_IMAPS,                       /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_ldap = {
+  "ldap",                               /* scheme */
+#ifdef CURL_DISABLE_LDAP
+  ZERO_NULL,
+#else
+  &Curl_protocol_ldap,
+#endif
+  CURLPROTO_LDAP,                       /* protocol */
+  CURLPROTO_LDAP,                       /* family */
+  PROTOPT_SSL_REUSE,                    /* flags */
+  PORT_LDAP,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_ldaps = {
+  "ldaps",                              /* scheme */
+#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_ldap,
+#endif
+  CURLPROTO_LDAPS,                      /* protocol */
+  CURLPROTO_LDAP,                       /* family */
+  PROTOPT_SSL,                          /* flags */
+  PORT_LDAPS,                           /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_mqtt = {
+  "mqtt",                             /* scheme */
+#ifdef CURL_DISABLE_MQTT
+  ZERO_NULL,
+#else
+  &Curl_protocol_mqtt,
+#endif
+  CURLPROTO_MQTT,                     /* protocol */
+  CURLPROTO_MQTT,                     /* family */
+  PROTOPT_NONE,                       /* flags */
+  PORT_MQTT,                          /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_mqtts = {
+  "mqtts",                            /* scheme */
+#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_mqtts,
+#endif
+  CURLPROTO_MQTTS,                    /* protocol */
+  CURLPROTO_MQTT,                     /* family */
+  PROTOPT_SSL,                        /* flags */
+  PORT_MQTTS,                         /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_pop3 = {
+  "pop3",                           /* scheme */
+#ifdef CURL_DISABLE_POP3
+  ZERO_NULL,
+#else
+  &Curl_protocol_pop3,
+#endif
+  CURLPROTO_POP3,                   /* protocol */
+  CURLPROTO_POP3,                   /* family */
+  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
+  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
+  PORT_POP3,                        /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_pop3s = {
+  "pop3s",                          /* scheme */
+#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_pop3,
+#endif
+  CURLPROTO_POP3S,                  /* protocol */
+  CURLPROTO_POP3,                   /* family */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
+  PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
+  PORT_POP3S,                       /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_rtsp = {
+  "rtsp",                               /* scheme */
+#ifdef CURL_DISABLE_RTSP
+  ZERO_NULL,
+#else
+  &Curl_protocol_rtsp,
+#endif
+  CURLPROTO_RTSP,                       /* protocol */
+  CURLPROTO_RTSP,                       /* family */
+  PROTOPT_CONN_REUSE,                   /* flags */
+  PORT_RTSP,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_sftp = {
+  "sftp",                               /* scheme */
+#ifndef USE_SSH
+  NULL,
+#else
+  &Curl_protocol_sftp,
+#endif
+  CURLPROTO_SFTP,                       /* protocol */
+  CURLPROTO_SFTP,                       /* family */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+  PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
+  PORT_SSH                              /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_scp = {
+  "scp",                                /* scheme */
+#ifndef USE_SSH
+  NULL,
+#else
+  &Curl_protocol_scp,
+#endif
+  CURLPROTO_SCP,                        /* protocol */
+  CURLPROTO_SCP,                        /* family */
+  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+  PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
+  PORT_SSH,                             /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_smb = {
+  "smb",                                /* scheme */
+#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE)
+  &Curl_protocol_smb,
+#else
+  ZERO_NULL,
+#endif
+  CURLPROTO_SMB,                        /* protocol */
+  CURLPROTO_SMB,                        /* family */
+  PROTOPT_NONE,                         /* flags */
+  PORT_SMB,                             /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_smbs = {
+  "smbs",                               /* scheme */
+#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) && defined(USE_SSL)
+  &Curl_protocol_smb,
+#else
+  ZERO_NULL,
+#endif
+  CURLPROTO_SMBS,                       /* protocol */
+  CURLPROTO_SMB,                        /* family */
+  PROTOPT_SSL,                          /* flags */
+  PORT_SMBS,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_smtp = {
+  "smtp",                           /* scheme */
+#ifdef CURL_DISABLE_SMTP
+  ZERO_NULL,
+#else
+  &Curl_protocol_smtp,
+#endif
+  CURLPROTO_SMTP,                   /* protocol */
+  CURLPROTO_SMTP,                   /* family */
+  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
+  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
+  PORT_SMTP,                        /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_smtps = {
+  "smtps",                          /* scheme */
+#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_smtp,
+#endif
+  CURLPROTO_SMTPS,                  /* protocol */
+  CURLPROTO_SMTP,                   /* family */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
+  PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
+  PORT_SMTPS,                       /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_telnet = {
+  "telnet",                             /* scheme */
+#ifdef CURL_DISABLE_TELNET
+  ZERO_NULL,
+#else
+  &Curl_protocol_telnet,
+#endif
+  CURLPROTO_TELNET,                     /* protocol */
+  CURLPROTO_TELNET,                     /* family */
+  PROTOPT_NONE | PROTOPT_NOURLQUERY,    /* flags */
+  PORT_TELNET,                          /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_tftp = {
+  "tftp",                               /* scheme */
+#ifdef CURL_DISABLE_TFTP
+  ZERO_NULL,
+#else
+  &Curl_protocol_tftp,
+#endif
+  CURLPROTO_TFTP,                       /* protocol */
+  CURLPROTO_TFTP,                       /* family */
+  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */
+  PORT_TFTP,                            /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_ws = {
+  "ws",                                 /* scheme */
+#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP)
+  ZERO_NULL,
+#else
+  &Curl_protocol_ws,
+#endif
+  CURLPROTO_WS,                         /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+  PROTOPT_USERPWDCTRL,
+  PORT_HTTP                             /* defport */
+};
+
+const struct Curl_scheme Curl_scheme_wss = {
+  "wss",                                /* scheme */
+#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) || \
+  !defined(USE_SSL)
+  ZERO_NULL,
+#else
+  &Curl_protocol_ws,
+#endif
+  CURLPROTO_WSS,                        /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
+  PROTOPT_USERPWDCTRL,
+  PORT_HTTPS                            /* defport */
+};
+
+/* Returns a struct scheme pointer if the name is a known scheme. Check the
+   ->run struct field for non-NULL to figure out if an implementation is
+   present. */
+const struct Curl_scheme *Curl_getn_scheme(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
+     */
+  static const struct Curl_scheme * const all_schemes[47] = {
+    &Curl_scheme_mqtt,
+    &Curl_scheme_smtp,
+    &Curl_scheme_tftp,
+    &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    &Curl_scheme_ldaps,
+    &Curl_scheme_dict, NULL,
+    &Curl_scheme_file, NULL,
+    &Curl_scheme_pop3s,
+    &Curl_scheme_ftp,
+    &Curl_scheme_scp,
+    &Curl_scheme_mqtts,
+    &Curl_scheme_imaps,
+    &Curl_scheme_ldap,
+    &Curl_scheme_http,
+    &Curl_scheme_smb, NULL, NULL,
+    &Curl_scheme_telnet,
+    &Curl_scheme_https,
+    &Curl_scheme_gopher,
+    &Curl_scheme_rtsp, NULL, NULL,
+    &Curl_scheme_wss, NULL,
+    &Curl_scheme_gophers,
+    &Curl_scheme_smtps,
+    &Curl_scheme_pop3,
+    &Curl_scheme_ws, NULL, NULL,
+    &Curl_scheme_sftp,
+    &Curl_scheme_ftps, NULL,
+    &Curl_scheme_smbs, NULL,
+  };
+
+  if(len && (len <= 7)) {
+    const char *s = scheme;
+    size_t l = len;
+    const struct Curl_scheme *h;
+    unsigned int c = 792;
+    while(l) {
+      c <<= 4;
+      c += (unsigned int)Curl_raw_tolower(*s);
+      s++;
+      l--;
+    }
+
+    h = all_schemes[c % 47];
+    if(h && curl_strnequal(scheme, h->name, len) && !h->name[len])
+      return h;
+  }
+  return NULL;
+}
+
+const struct Curl_scheme *Curl_get_scheme(const char *scheme)
+{
+  return Curl_getn_scheme(scheme, strlen(scheme));
+}
diff --git a/Utilities/cmcurl/lib/protocol.h b/Utilities/cmcurl/lib/protocol.h
new file mode 100644
index 0000000..f825409
--- /dev/null
+++ b/Utilities/cmcurl/lib/protocol.h
@@ -0,0 +1,276 @@
+#ifndef HEADER_CURL_PROTOCOL_H
+#define HEADER_CURL_PROTOCOL_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
+ *
+ ***************************************************************************/
+/* This file is for lib internal stuff */
+#include "curl_setup.h"
+
+struct Curl_easy;
+struct connectdata;
+struct easy_pollset;
+
+/* Known protocol default port numbers */
+#define PORT_FTP    21
+#define PORT_FTPS   990
+#define PORT_TELNET 23
+#define PORT_HTTP   80
+#define PORT_HTTPS  443
+#define PORT_DICT   2628
+#define PORT_LDAP   389
+#define PORT_LDAPS  636
+#define PORT_TFTP   69
+#define PORT_SSH    22
+#define PORT_IMAP   143
+#define PORT_IMAPS  993
+#define PORT_POP3   110
+#define PORT_POP3S  995
+#define PORT_SMB    445
+#define PORT_SMBS   445
+#define PORT_SMTP   25
+#define PORT_SMTPS  465 /* sometimes called SSMTP */
+#define PORT_RTSP   554
+#define PORT_GOPHER 70
+#define PORT_MQTT   1883
+#define PORT_MQTTS  8883
+
+/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
+ * the rest are internal information. If we use higher bits we only do this on
+ * platforms that have a >= 64-bit type and then we use such a type for the
+ * protocol fields in the scheme definition.
+ */
+#define CURLPROTO_WS     (1L << 30)
+#define CURLPROTO_WSS    ((curl_prot_t)1 << 31)
+#define CURLPROTO_MQTTS  (1LL << 32)
+
+#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff)
+
+/* the default protocols accepting a redirect to */
+#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \
+                         CURLPROTO_FTPS)
+
+typedef curl_off_t curl_prot_t;
+
+/* This mask is for all the old protocols that are provided and defined in the
+   public header and shall exclude protocols added since which are not exposed
+   in the API */
+#define CURLPROTO_MASK   0x3fffffff
+
+/* Convenience defines for checking protocols or their SSL based version. Each
+   protocol scheme should only ever have a single CURLPROTO_ in its protocol
+   field. */
+#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \
+                           CURLPROTO_WSS)
+#define PROTO_FAMILY_FTP  (CURLPROTO_FTP | CURLPROTO_FTPS)
+#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S)
+#define PROTO_FAMILY_SMB  (CURLPROTO_SMB | CURLPROTO_SMBS)
+#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP | CURLPROTO_SMTPS)
+#define PROTO_FAMILY_SSH  (CURLPROTO_SCP | CURLPROTO_SFTP)
+
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \
+  !defined(CURL_DISABLE_POP3)
+/* these protocols support CURLOPT_DIRLISTONLY */
+#define CURL_LIST_ONLY_PROTOCOL 1
+#endif
+
+/* When redirecting transfers. */
+typedef enum {
+  FOLLOW_NONE,  /* not used within the function, a placeholder to allow
+                   initing to this */
+  FOLLOW_FAKE,  /* only records stuff, not actually following */
+  FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
+                   redirect following */
+  FOLLOW_REDIR /* a full true redirect */
+} followtype;
+
+/*
+ * Specific protocol handler, an implementation of one or more URI schemes.
+ */
+struct Curl_protocol {
+  /* Complement to setup_connection_internals(). This is done before the
+     transfer "owns" the connection. */
+  CURLcode (*setup_connection)(struct Curl_easy *data,
+                               struct connectdata *conn);
+
+  /* These two functions MUST be set to be protocol dependent */
+  CURLcode (*do_it)(struct Curl_easy *data, bool *done);
+  CURLcode (*done)(struct Curl_easy *, CURLcode, bool);
+
+  /* If the curl_do() function is better made in two halves, this
+   * curl_do_more() function will be called afterwards, if set. For example
+   * for doing the FTP stuff after the PASV/PORT command.
+   */
+  CURLcode (*do_more)(struct Curl_easy *, int *);
+
+  /* This function *MAY* be set to a protocol-dependent function that is run
+   * after the connect() and everything is done, as a step in the connection.
+   * The 'done' pointer points to a bool that should be set to TRUE if the
+   * function completes before return. If it does not complete, the caller
+   * should call the ->connecting() function until it is.
+   */
+  CURLcode (*connect_it)(struct Curl_easy *data, bool *done);
+
+  /* See above. */
+  CURLcode (*connecting)(struct Curl_easy *data, bool *done);
+  CURLcode (*doing)(struct Curl_easy *data, bool *done);
+
+  /* Called from the multi interface during the PROTOCONNECT phase, and it
+     should then return a proper fd set */
+  CURLcode (*proto_pollset)(struct Curl_easy *data,
+                            struct easy_pollset *ps);
+
+  /* Called from the multi interface during the DOING phase, and it should
+     then return a proper fd set */
+  CURLcode (*doing_pollset)(struct Curl_easy *data,
+                            struct easy_pollset *ps);
+
+  /* Called from the multi interface during the DO_MORE phase, and it should
+     then return a proper fd set */
+  CURLcode (*domore_pollset)(struct Curl_easy *data,
+                            struct easy_pollset *ps);
+
+  /* Called from the multi interface during the DO_DONE, PERFORM and
+     WAITPERFORM phases, and it should then return a proper fd set. Not setting
+     this will make libcurl use the generic default one. */
+  CURLcode (*perform_pollset)(struct Curl_easy *data,
+                              struct easy_pollset *ps);
+
+  /* This function *MAY* be set to a protocol-dependent function that is run
+   * by the curl_disconnect(), as a step in the disconnection. If the handler
+   * is called because the connection has been considered dead,
+   * dead_connection is set to TRUE. The connection is (again) associated with
+   * the transfer here.
+   */
+  CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
+                         bool dead_connection);
+
+  /* If used, this function gets called from transfer.c to
+     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);
+
+  /* If used, this function gets called from transfer.c to
+     allow the protocol to do extra handling in writing a single response
+     header line to the client. */
+  CURLcode (*write_resp_hd)(struct Curl_easy *data,
+                            const char *hd, size_t hdlen, bool is_eos);
+
+  /* If used, this function checks for a connection managed by this
+    protocol and currently not in use, if it should be considered dead. */
+  bool (*connection_is_dead)(struct Curl_easy *data,
+                             struct connectdata *conn);
+
+  /* attach() attaches this transfer to this connection */
+  void (*attach)(struct Curl_easy *data, struct connectdata *conn);
+
+  /* return CURLE_OK if a redirect to `newurl` should be followed,
+     CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change
+     the way the follow request is performed. */
+  CURLcode (*follow)(struct Curl_easy *data, const char *newurl,
+                     followtype type);
+};
+
+#define PROTOPT_NONE 0             /* nothing extra */
+#define PROTOPT_SSL (1 << 0)       /* uses SSL */
+#define PROTOPT_DUAL (1 << 1)      /* this protocol uses two connections */
+#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */
+/* some protocols will have to call the underlying functions without regard to
+   what exact state the socket signals. IE even if the socket says "readable",
+   the send function might need to be called while uploading, or vice versa.
+*/
+#define PROTOPT_DIRLOCK (1 << 3)
+#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */
+#define PROTOPT_NEEDSPWD (1 << 5)  /* needs a password, and if none is set it
+                                      gets a default */
+#define PROTOPT_NOURLQUERY (1 << 6)  /* protocol cannot handle
+                                        URL query strings (?foo=bar) ! */
+#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per
+                                            request instead of per
+                                            connection */
+#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */
+/* (1 << 9) was PROTOPT_STREAM, now free */
+#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo
+                                        field of the URL */
+#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a
+                                           HTTP proxy as HTTP proxies may know
+                                           this protocol and act as
+                                           a gateway */
+#define PROTOPT_WILDCARD (1 << 12)  /* protocol supports wildcard matching */
+#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in
+                                         username and password */
+#define PROTOPT_NOTCPPROXY (1 << 14)  /* this protocol cannot proxy over TCP */
+#define PROTOPT_SSL_REUSE (1 << 15)   /* this protocol may reuse an existing
+                                         SSL connection in the same family
+                                         without having PROTOPT_SSL. */
+#define PROTOPT_CONN_REUSE (1 << 16)  /* this protocol can reuse connections */
+
+/* Everything about a URI scheme. */
+struct Curl_scheme {
+  const char *name;       /* URL scheme name in lowercase */
+  const struct Curl_protocol *run; /* implementation, optional */
+  curl_prot_t protocol;   /* See CURLPROTO_* - this needs to be the single
+                             specific protocol bit */
+  curl_prot_t family;     /* single bit for protocol family; the non-TLS name
+                             of the protocol this is */
+  uint32_t flags;         /* Extra particular characteristics, see PROTOPT_* */
+  uint16_t defport;       /* Default port. */
+};
+
+/* Get scheme definition for a URI scheme name
+ * @param scheme URI scheme name, case-insensitive
+ * @return NULL if scheme is not known
+ */
+const struct Curl_scheme *Curl_get_scheme(const char *scheme);
+const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len);
+
+/* For direct access to a URI scheme */
+extern const struct Curl_scheme Curl_scheme_dict;
+extern const struct Curl_scheme Curl_scheme_file;
+extern const struct Curl_scheme Curl_scheme_ftp;
+extern const struct Curl_scheme Curl_scheme_ftps;
+extern const struct Curl_scheme Curl_scheme_gopher;
+extern const struct Curl_scheme Curl_scheme_gophers;
+extern const struct Curl_scheme Curl_scheme_http;
+extern const struct Curl_scheme Curl_scheme_https;
+extern const struct Curl_scheme Curl_scheme_imap;
+extern const struct Curl_scheme Curl_scheme_imaps;
+extern const struct Curl_scheme Curl_scheme_ldap;
+extern const struct Curl_scheme Curl_scheme_ldaps;
+extern const struct Curl_scheme Curl_scheme_mqtt;
+extern const struct Curl_scheme Curl_scheme_mqtts;
+extern const struct Curl_scheme Curl_scheme_pop3;
+extern const struct Curl_scheme Curl_scheme_pop3s;
+extern const struct Curl_scheme Curl_scheme_rtsp;
+extern const struct Curl_scheme Curl_scheme_scp;
+extern const struct Curl_scheme Curl_scheme_sftp;
+extern const struct Curl_scheme Curl_scheme_smb;
+extern const struct Curl_scheme Curl_scheme_smbs;
+extern const struct Curl_scheme Curl_scheme_smtp;
+extern const struct Curl_scheme Curl_scheme_smtps;
+extern const struct Curl_scheme Curl_scheme_telnet;
+extern const struct Curl_scheme Curl_scheme_tftp;
+extern const struct Curl_scheme Curl_scheme_ws;
+extern const struct Curl_scheme Curl_scheme_wss;
+
+#endif /* HEADER_CURL_PROTOCOL_H */
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index 4232e81..dd82750 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -34,30 +34,20 @@
 #include "escape.h"
 
 #ifdef _WIN32
-
-#ifndef CURL_WINDOWS_UWP
-#  include <bcrypt.h>
-#  ifdef _MSC_VER
-#    pragma comment(lib, "bcrypt.lib")
-#  endif
-#  ifndef STATUS_SUCCESS
-#  define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
-#  endif
+#include <bcrypt.h>
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
 #endif
 
 CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
 {
   memset(entropy, 0, length);
 
-#ifndef CURL_WINDOWS_UWP
   if(BCryptGenRandom(NULL, entropy, (ULONG)length,
                      BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
     return CURLE_FAILED_INIT;
 
   return CURLE_OK;
-#else
-  return CURLE_NOT_BUILT_IN;
-#endif
 }
 #endif
 
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index 8676580..afccd0a 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 CURLcode Curl_rand_bytes(struct Curl_easy *data,
 #ifdef DEBUGBUILD
-                         bool allow_env_override,
+                         bool env_override,
 #endif
                          unsigned char *rnd, size_t num);
 
diff --git a/Utilities/cmcurl/lib/ratelimit.c b/Utilities/cmcurl/lib/ratelimit.c
index 2d88cdd..dc01375 100644
--- a/Utilities/cmcurl/lib/ratelimit.c
+++ b/Utilities/cmcurl/lib/ratelimit.c
@@ -157,8 +157,8 @@
   DEBUGASSERT(rate_per_sec >= 0);
   DEBUGASSERT(burst_per_sec >= rate_per_sec || !burst_per_sec);
   DEBUGASSERT(pts);
-  r->rate_per_step = rate_per_sec;
-  r->burst_per_step = burst_per_sec;
+  r->rate_per_step = r->rate_per_sec = rate_per_sec;
+  r->burst_per_step = r->burst_per_sec = burst_per_sec;
   r->step_us = CURL_US_PER_SEC;
   r->spare_us = 0;
   r->tokens = r->rate_per_step;
@@ -169,8 +169,13 @@
 void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts,
                        int64_t total_tokens)
 {
-  r->tokens = r->rate_per_step;
+  /* A start always resets the values to initial defaults, then
+   * fine tunes the intervals for the total_tokens expected. */
+  r->rate_per_step = r->rate_per_sec;
+  r->burst_per_step = r->burst_per_sec;
+  r->step_us = CURL_US_PER_SEC;
   r->spare_us = 0;
+  r->tokens = r->rate_per_step;
   r->ts = *pts;
   rlimit_tune_steps(r, total_tokens);
 }
diff --git a/Utilities/cmcurl/lib/ratelimit.h b/Utilities/cmcurl/lib/ratelimit.h
index 7563734..3c3e38b 100644
--- a/Utilities/cmcurl/lib/ratelimit.h
+++ b/Utilities/cmcurl/lib/ratelimit.h
@@ -55,6 +55,8 @@
  */
 
 struct Curl_rlimit {
+  int64_t rate_per_sec; /* rate tokens generated per second */
+  int64_t burst_per_sec; /* burst rate of tokens per second */
   int64_t rate_per_step; /* rate tokens generated per step us */
   int64_t burst_per_step; /* burst rate of tokens per step us */
   timediff_t step_us;     /* microseconds between token increases */
diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c
index 6607753..c414383 100644
--- a/Utilities/cmcurl/lib/request.c
+++ b/Utilities/cmcurl/lib/request.c
@@ -65,6 +65,11 @@
   req->httpversion = 0;
   req->sendbuf_hds_len = 0;
 
+  curlx_safefree(req->userpwd);
+#ifndef CURL_DISABLE_PROXY
+  curlx_safefree(req->proxyuserpwd);
+#endif
+
   result = Curl_client_start(data);
   if(result)
     return result;
@@ -102,9 +107,6 @@
   if(!aborted)
     (void)req_flush(data);
   Curl_client_reset(data);
-#ifndef CURL_DISABLE_DOH
-  Curl_doh_close(data);
-#endif
   return CURLE_OK;
 }
 
@@ -112,14 +114,20 @@
 {
   struct curltime t0 = { 0, 0 };
 
-  Curl_safefree(req->newurl);
+  curlx_safefree(req->newurl);
+  curlx_safefree(req->userpwd);
+#ifndef CURL_DISABLE_PROXY
+  curlx_safefree(req->proxyuserpwd);
+#endif
+#ifndef CURL_DISABLE_COOKIES
+  curlx_safefree(req->cookiehost);
+#endif
   Curl_client_reset(data);
   if(req->sendbuf_init)
     Curl_bufq_reset(&req->sendbuf);
 
-#ifndef CURL_DISABLE_DOH
-  Curl_doh_close(data);
-#endif
+  /* clear any resolve data */
+  Curl_resolv_destroy_all(data);
   /* Can no longer memset() this struct as we need to keep some state */
   req->size = -1;
   req->maxdownload = -1;
@@ -132,7 +140,7 @@
   req->headerline = 0;
   req->offset = 0;
   req->httpcode = 0;
-  req->keepon = 0;
+  req->io_flags = 0;
   req->upgr101 = UPGR101_NONE;
   req->sendbuf_hds_len = 0;
   req->timeofdoc = 0;
@@ -153,6 +161,7 @@
   req->ignorebody = FALSE;
   req->http_bodyless = FALSE;
   req->chunk = FALSE;
+  req->resp_trailer = FALSE;
   req->ignore_cl = FALSE;
   req->upload_chunky = FALSE;
   req->no_body = data->set.opt_no_body;
@@ -165,7 +174,11 @@
 
 void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
 {
-  Curl_safefree(req->newurl);
+  curlx_safefree(req->newurl);
+  curlx_safefree(req->userpwd);
+#ifndef CURL_DISABLE_PROXY
+  curlx_safefree(req->proxyuserpwd);
+#endif
   if(req->sendbuf_init)
     Curl_bufq_free(&req->sendbuf);
   Curl_client_cleanup(data);
@@ -256,7 +269,7 @@
 {
   DEBUGASSERT(!data->req.upload_done);
   data->req.upload_done = TRUE;
-  data->req.keepon &= ~KEEP_SEND; /* we are done sending */
+  CURL_REQ_CLEAR_SEND(data);
 
   Curl_pgrsTime(data, TIMER_POSTRANSFER);
   Curl_creader_done(data, data->req.upload_aborted);
@@ -419,22 +432,22 @@
 bool Curl_req_want_send(struct Curl_easy *data)
 {
   /* Not done and upload not blocked and either one of
-   * - KEEP_SEND
+   * - REQ_IO_SEND
    * - request has buffered data to send
    * - connection has pending data to send */
   return !data->req.done &&
          !Curl_rlimit_is_blocked(&data->progress.ul.rlimit) &&
-         ((data->req.keepon & KEEP_SEND) ||
+         (CURL_REQ_WANT_SEND(data) ||
           !Curl_req_sendbuf_empty(data) ||
           Curl_xfer_needs_flush(data));
 }
 
 bool Curl_req_want_recv(struct Curl_easy *data)
 {
-  /* Not done and download not blocked and KEEP_RECV */
+  /* Not done and download not blocked and want RECV */
   return !data->req.done &&
          !Curl_rlimit_is_blocked(&data->progress.dl.rlimit) &&
-         (data->req.keepon & KEEP_RECV);
+         CURL_REQ_WANT_RECV(data);
 }
 
 bool Curl_req_done_sending(struct Curl_easy *data)
@@ -470,7 +483,7 @@
   if(!data->req.upload_done) {
     Curl_bufq_reset(&data->req.sendbuf);
     data->req.upload_aborted = TRUE;
-    data->req.keepon &= ~KEEP_SEND;
+    CURL_REQ_CLEAR_SEND(data);
     return req_set_upload_done(data);
   }
   return CURLE_OK;
@@ -482,8 +495,8 @@
    * We might still be paused on receive client writes though, so
    * keep those bits around. */
   CURLcode result = CURLE_OK;
-  if(data->req.keepon & KEEP_SEND)
+  if(CURL_REQ_WANT_SEND(data))
     result = Curl_req_abort_sending(data);
-  data->req.keepon &= ~(KEEP_RECV | KEEP_SEND);
+  CURL_REQ_CLEAR_IO(data);
   return result;
 }
diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h
index 5332d48..6948d79 100644
--- a/Utilities/cmcurl/lib/request.h
+++ b/Utilities/cmcurl/lib/request.h
@@ -31,6 +31,25 @@
 /* forward declarations */
 struct UserDefined;
 
+/* Bits on the io_flags member of SingleRequest */
+#define REQ_IO_RECV       (1 << 0) /* there is or may be data to read */
+#define REQ_IO_SEND       (1 << 1) /* there is or may be data to write */
+
+/* Low level request receive/send io_flags checks. */
+#define CURL_REQ_WANT_SEND(d)  ((d)->req.io_flags & REQ_IO_SEND)
+#define CURL_REQ_WANT_RECV(d)  ((d)->req.io_flags & REQ_IO_RECV)
+#define CURL_REQ_WANT_IO(d)    \
+  ((d)->req.io_flags & (REQ_IO_RECV | REQ_IO_SEND))
+/* Low level request receive/send io_flags manipulations. */
+#define CURL_REQ_SET_SEND(d)   ((d)->req.io_flags |= REQ_IO_SEND)
+#define CURL_REQ_SET_RECV(d)   ((d)->req.io_flags |= REQ_IO_RECV)
+#define CURL_REQ_CLEAR_SEND(d) \
+  ((d)->req.io_flags &= (uint8_t)~REQ_IO_SEND)
+#define CURL_REQ_CLEAR_RECV(d) \
+  ((d)->req.io_flags &= (uint8_t)~REQ_IO_RECV)
+#define CURL_REQ_CLEAR_IO(d)  \
+  ((d)->req.io_flags &= (uint8_t)~(REQ_IO_RECV | REQ_IO_SEND))
+
 enum expect100 {
   EXP100_SEND_DATA,           /* enough waiting, send the body now */
   EXP100_AWAITING_CONTINUE,   /* waiting for the 100 Continue header */
@@ -59,6 +78,8 @@
                              -1 means unlimited */
   curl_off_t bytecount;         /* total number of bytes read */
   curl_off_t writebytecount;    /* number of bytes written */
+  curl_off_t offset;            /* possible resume offset read from the
+                                   Content-Range: header */
 
   struct curltime start;         /* transfer started at this time */
   unsigned int headerbytecount;  /* received server headers (not CONNECT
@@ -72,11 +93,8 @@
                                      in a CURLE_GOT_NOTHING error code */
   int headerline;               /* counts header lines to better track the
                                    first one */
-  curl_off_t offset;            /* possible resume offset read from the
-                                   Content-Range: header */
   int httpcode;                 /* error code from the 'HTTP/1.? XXX' or
                                    'RTSP/1.? XXX' line */
-  int keepon;
   unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */
   unsigned char httpversion;    /* Version in response (09, 10, 11, etc.) */
   enum upgrade101 upgr101;      /* 101 upgrade state */
@@ -94,7 +112,15 @@
                        header data */
   char *newurl;     /* Set to the new URL to use when a redirect or a retry is
                        wanted */
+  uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */
 
+  char *userpwd;      /* auth header */
+#ifndef CURL_DISABLE_PROXY
+  char *proxyuserpwd; /* proxy auth header */
+#endif
+#ifndef CURL_DISABLE_COOKIES
+  char *cookiehost;
+#endif
 #ifndef CURL_DISABLE_COOKIES
   unsigned char setcookies;
 #endif
@@ -109,8 +135,8 @@
   BIT(eos_sent);      /* iff EOS has been sent to the server */
   BIT(rewind_read);   /* iff reader needs rewind at next start */
   BIT(upload_done);   /* set to TRUE when all request data has been sent */
-  BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also
-                        * show `upload_done` as TRUE. */
+  BIT(upload_aborted); /* set to TRUE when upload was aborted. Also
+                        * shows `upload_done` as TRUE. */
   BIT(ignorebody);    /* we read a response-body but we ignore it! */
   BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
                          204 or 304 */
@@ -207,13 +233,13 @@
 
 /**
  * Stop sending any more request data to the server.
- * Will clear the send buffer and mark request sending as done.
+ * Clear the send buffer and mark request sending as done.
  */
 CURLcode Curl_req_abort_sending(struct Curl_easy *data);
 
 /**
  * Stop sending and receiving any more request data.
- * Will abort sending if not done.
+ * Abort sending if not done.
  */
 CURLcode Curl_req_stop_send_recv(struct Curl_easy *data);
 
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index 11848c8..b08767f 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -126,19 +126,13 @@
 /*
  * Function to check on various aspects of a connection.
  */
-static uint32_t rtsp_conncheck(struct Curl_easy *data,
-                               struct connectdata *conn,
-                               uint32_t checks_to_perform)
+static bool rtsp_conn_is_dead(struct Curl_easy *data,
+                              struct connectdata *conn)
 {
-  unsigned int ret_val = CONNRESULT_NONE;
-
-  if(checks_to_perform & CONNCHECK_ISDEAD) {
-    bool input_pending;
-    if(!Curl_conn_is_alive(data, conn, &input_pending))
-      ret_val |= CONNRESULT_DEAD;
-  }
-
-  return ret_val;
+  bool input_pending;
+  /* Contrary to default handling, this protocol allows pending
+   * input on an unused connection. */
+  return !Curl_conn_is_alive(data, conn, &input_pending);
 }
 
 static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
@@ -166,7 +160,7 @@
   struct rtsp_conn *rtspc =
     Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
   struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
-  CURLcode httpStatus;
+  CURLcode result;
 
   if(!rtspc || !rtsp)
     return CURLE_FAILED_INIT;
@@ -175,9 +169,9 @@
   if(data->set.rtspreq == RTSPREQ_RECEIVE)
     premature = TRUE;
 
-  httpStatus = Curl_http_done(data, status, premature);
+  result = Curl_http_done(data, status, premature);
 
-  if(!status && !httpStatus) {
+  if(!status && !result) {
     /* Check the sequence numbers */
     uint32_t CSeq_sent = rtsp->CSeq_sent;
     uint32_t CSeq_recv = rtsp->CSeq_recv;
@@ -197,7 +191,7 @@
     }
   }
 
-  return httpStatus;
+  return result;
 }
 
 static CURLcode rtsp_setup_body(struct Curl_easy *data,
@@ -240,7 +234,7 @@
       /* As stated in the http comments, it is probably not wise to
        * actually set a custom Content-Length in the headers */
       if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
-        result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n",
+        result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T "\r\n",
                                 req_clen);
         if(result)
           return result;
@@ -439,13 +433,13 @@
     }
   }
 
-  /* The User-Agent string might have been allocated in url.c already, because
+  /* The User-Agent string might have been allocated already, because
      it might have been used in the proxy connect, but if we have got a header
      with the user-agent string specified, we erase the previously made string
      here. */
   if(Curl_checkheaders(data, STRCONST("User-Agent")) &&
      data->state.aptr.uagent) {
-    Curl_safefree(data->state.aptr.uagent);
+    curlx_safefree(data->state.aptr.uagent);
   }
   else if(!Curl_checkheaders(data, STRCONST("User-Agent")) &&
           data->set.str[STRING_USERAGENT]) {
@@ -459,12 +453,12 @@
     goto out;
 
 #ifndef CURL_DISABLE_PROXY
-  p_proxyuserpwd = data->state.aptr.proxyuserpwd;
+  p_proxyuserpwd = data->req.proxyuserpwd;
 #endif
-  p_userpwd = data->state.aptr.userpwd;
+  p_userpwd = data->req.userpwd;
 
   /* Referrer */
-  Curl_safefree(data->state.aptr.ref);
+  curlx_safefree(data->state.aptr.ref);
   if(Curl_bufref_ptr(&data->state.referer) &&
      !Curl_checkheaders(data, STRCONST("Referer")))
     data->state.aptr.ref =
@@ -544,12 +538,6 @@
                           p_proxyuserpwd ? p_proxyuserpwd : "",
                           p_userpwd ? p_userpwd : "");
 
-  /*
-   * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
-   * with basic and digest, it will be freed anyway by the next request
-   */
-  Curl_safefree(data->state.aptr.userpwd);
-
   if(result)
     goto out;
 
@@ -686,7 +674,7 @@
             /* 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));
+                           "skipping %zu bytes of junk", *pconsumed));
             }
             rtspc->state = RTP_PARSE_SKIP;
             rtspc->in_header = TRUE;
@@ -1017,7 +1005,7 @@
      *
      * Allow any non whitespace content, up to the field separator or end of
      * line. RFC 2326 is not 100% clear on the session ID and for example
-     * gstreamer does url-encoded session ID's not covered by the standard.
+     * gstreamer does URL-encoded session ID's not covered by the standard.
      */
     end = start;
     while((*end > ' ') && (*end != ';'))
@@ -1057,7 +1045,7 @@
 /*
  * RTSP handler interface.
  */
-static const struct Curl_protocol Curl_protocol_rtsp = {
+const struct Curl_protocol Curl_protocol_rtsp = {
   rtsp_setup_connection,                /* setup_connection */
   rtsp_do,                              /* do_it */
   rtsp_done,                            /* done */
@@ -1072,25 +1060,9 @@
   ZERO_NULL,                            /* disconnect */
   rtsp_rtp_write_resp,                  /* write_resp */
   rtsp_rtp_write_resp_hd,               /* write_resp_hd */
-  rtsp_conncheck,                       /* connection_check */
+  rtsp_conn_is_dead,                    /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   Curl_http_follow,                     /* follow */
 };
 
 #endif /* CURL_DISABLE_RTSP */
-
-/*
- * RTSP handler interface.
- */
-const struct Curl_scheme Curl_scheme_rtsp = {
-  "rtsp",                               /* scheme */
-#ifdef CURL_DISABLE_RTSP
-  ZERO_NULL,
-#else
-  &Curl_protocol_rtsp,
-#endif
-  CURLPROTO_RTSP,                       /* protocol */
-  CURLPROTO_RTSP,                       /* family */
-  PROTOPT_CONN_REUSE,                   /* flags */
-  PORT_RTSP,                            /* defport */
-};
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h
index 92b28e5..dd5df3f 100644
--- a/Utilities/cmcurl/lib/rtsp.h
+++ b/Utilities/cmcurl/lib/rtsp.h
@@ -23,9 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_rtsp;
 #ifndef CURL_DISABLE_RTSP
 CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header);
+extern const struct Curl_protocol Curl_protocol_rtsp;
 #else
 #define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN
 #endif
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c
index 9a11924..888490f 100644
--- a/Utilities/cmcurl/lib/select.c
+++ b/Utilities/cmcurl/lib/select.c
@@ -75,17 +75,17 @@
 
 #ifdef USE_WINSOCK
   /* Winsock select() must not be called with an fd_set that contains zero
-    fd flags, or it will return WSAEINVAL. But, it also cannot be called
-    with no fd_sets at all!  From the documentation:
+     fd flags, or it will return WSAEINVAL. It also cannot be called with
+     no fd_sets at all!  From the documentation:
 
-    Any two of the parameters, readfds, writefds, or exceptfds, can be
-    given as null. At least one must be non-null, and any non-null
-    descriptor set must contain at least one handle to a socket.
+     Any two of the parameters, readfds, writefds, or exceptfds, can be
+     given as null. At least one must be non-null, and any non-null
+     descriptor set must contain at least one handle to a socket.
 
-    It is unclear why Winsock does not handle this for us instead of
-    calling this an error. Luckily, with Winsock, we can _also_ ask how
-    many bits are set on an fd_set. So, let's check it beforehand.
-  */
+     It is unclear why Winsock does not handle this for us instead of
+     calling this an error. Luckily, with Winsock, we can _also_ ask how
+     many bits are set on an fd_set. Therefore, let's check it beforehand.
+   */
   return select((int)maxfd + 1,
                 fds_read && fds_read->fd_count ? fds_read : NULL,
                 fds_write && fds_write->fd_count ? fds_write : NULL,
@@ -274,7 +274,7 @@
     if(ufds[i].fd == CURL_SOCKET_BAD)
       continue;
     VERIFY_SOCK(ufds[i].fd);
-    if(ufds[i].events & (POLLIN |POLLOUT |POLLPRI |
+    if(ufds[i].events & (POLLIN | POLLOUT | POLLPRI |
                          POLLRDNORM | POLLWRNORM | POLLRDBAND)) {
       if(ufds[i].fd > maxfd)
         maxfd = ufds[i].fd;
@@ -636,19 +636,25 @@
                           bool do_in, bool do_out)
 {
   return Curl_pollset_change(data, ps, sock,
-                             (do_in ? CURL_POLL_IN : 0)|
+                             (do_in ? CURL_POLL_IN : 0) |
                              (do_out ? CURL_POLL_OUT : 0),
-                             (!do_in ? CURL_POLL_IN : 0)|
+                             (!do_in ? CURL_POLL_IN : 0) |
                              (!do_out ? CURL_POLL_OUT : 0));
 }
 
+/*
+ * Return values:
+ *   -1 = error
+ *    0 = timeout
+ *    N = number of structures with non zero revent fields
+ */
 int Curl_pollset_poll(struct Curl_easy *data,
                       struct easy_pollset *ps,
                       timediff_t timeout_ms)
 {
   struct pollfd *pfds;
   unsigned int i, npfds;
-  int result;
+  int rc;
 
   (void)data;
   DEBUGASSERT(data);
@@ -677,9 +683,9 @@
     }
   }
 
-  result = Curl_poll(pfds, npfds, timeout_ms);
+  rc = Curl_poll(pfds, npfds, timeout_ms);
   curlx_free(pfds);
-  return result;
+  return rc;
 }
 
 void Curl_pollset_check(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h
index ffcd44c..87b6954 100644
--- a/Utilities/cmcurl/lib/select.h
+++ b/Utilities/cmcurl/lib/select.h
@@ -35,9 +35,7 @@
  * Definition of pollfd struct and constants for platforms lacking them.
  */
 
-#if !defined(HAVE_SYS_POLL_H) && \
-    !defined(HAVE_POLL_H) && \
-    !defined(POLLIN)
+#if !defined(HAVE_SYS_POLL_H) && !defined(HAVE_POLL_H) && !defined(POLLIN)
 
 #define POLLIN      0x01
 #define POLLPRI     0x02
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index 92e77b4..7c977d3 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -372,8 +372,8 @@
    The bit pattern defines to what "streams" to write to. Body and/or header.
    The defines are in sendf.h of course.
  */
-CURLcode Curl_client_write(struct Curl_easy *data,
-                           int type, const char *buf, size_t len)
+CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf,
+                           size_t len)
 {
   CURLcode result;
 
@@ -675,7 +675,7 @@
   case 0:
     if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
       failf(data, "client read function EOF fail, "
-            "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read",
+            "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read",
             ctx->read_len, ctx->total_len);
       result = CURLE_READ_ERROR;
       break;
@@ -729,8 +729,8 @@
     *peos = (bool)ctx->seen_eos;
     break;
   }
-  CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T
-                ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d",
+  CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T
+                ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d",
                 blen, ctx->total_len, ctx->read_len, result,
                 *pnread, *peos);
   return result;
@@ -1009,6 +1009,8 @@
 
     if(!nread || !memchr(buf, '\n', nread)) {
       /* nothing to convert, return this right away */
+      if(nread)
+        ctx->prev_cr = (buf[nread - 1] == '\r');
       if(ctx->read_eos)
         ctx->eos = TRUE;
       *pnread = nread;
@@ -1128,7 +1130,7 @@
   struct cr_in_ctx *ctx;
 
   result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
-  if(result)
+  if(result || !r)
     goto out;
   ctx = r->ctx;
   ctx->total_len = len;
@@ -1136,7 +1138,7 @@
   cl_reset_reader(data);
   result = do_init_reader_stack(data, r);
 out:
-  CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d",
+  CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d",
                 len, result);
   return result;
 }
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index 84f3e02..b8a6327 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -49,6 +49,7 @@
 #include "curlx/strdup.h"
 #include "escape.h"
 #include "bufref.h"
+#include "vauth/vauth.h"
 
 static CURLcode setopt_set_timeout_sec(timediff_t *ptimeout_ms, long secs)
 {
@@ -83,7 +84,7 @@
   /* Release the previous storage at `charp' and replace by a dynamic storage
      copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */
 
-  Curl_safefree(*charp);
+  curlx_safefree(*charp);
 
   if(s) {
     if(strlen(s) > CURL_MAX_INPUT_LENGTH)
@@ -103,7 +104,7 @@
   /* free the previous storage at `blobp' and replace by a dynamic storage
      copy of blob. If CURL_BLOB_COPY is set, the data is copied. */
 
-  Curl_safefree(*blobp);
+  curlx_safefree(*blobp);
 
   if(blob) {
     struct curl_blob *nblob;
@@ -137,8 +138,8 @@
   DEBUGASSERT(userp);
   DEBUGASSERT(passwdp);
 
-  /* Parse the login details if specified. It not then we treat NULL as a hint
-     to clear the existing data */
+  /* Parse the login details if specified. If not, then we treat NULL as a
+     hint to clear the existing data */
   if(option) {
     size_t len = strlen(option);
     CURLcode result;
@@ -488,7 +489,7 @@
   case CURLOPT_UPLOAD:
   case CURLOPT_PUT:
     /*
-     * We want to sent data to the remote host. If this is HTTP, that equals
+     * We want to send data to the remote host. If this is HTTP, that equals
      * using the PUT request.
      */
     if(enabled) {
@@ -613,7 +614,7 @@
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
   case CURLOPT_SOCKS5_GSSAPI_NEC:
     /*
-     * Set flag for NEC SOCK5 support
+     * Set flag for NEC SOCKS5 support
      */
     s->socks5_gssapi_nec = enabled;
     break;
@@ -1011,7 +1012,7 @@
   case CURLOPT_SSL_ENABLE_NPN:
     break;
   case CURLOPT_SSLENGINE_DEFAULT:
-    Curl_safefree(s->str[STRING_SSL_ENGINE]);
+    curlx_safefree(s->str[STRING_SSL_ENGINE]);
     result = Curl_ssl_set_engine_default(data);
     break;
   default:
@@ -1243,7 +1244,7 @@
       return CURLE_BAD_FUNCTION_ARGUMENT;
     if(s->postfieldsize < arg &&
        s->postfields == s->str[STRING_COPYPOSTFIELDS]) {
-      Curl_safefree(s->str[STRING_COPYPOSTFIELDS]);
+      curlx_safefree(s->str[STRING_COPYPOSTFIELDS]);
       s->postfields = NULL;
     }
     s->postfieldsize = arg;
@@ -1439,7 +1440,7 @@
     s->opt_no_body = FALSE; /* this is implied */
 #ifndef CURL_DISABLE_FORM_API
     Curl_mime_cleanpart(data->state.formp);
-    Curl_safefree(data->state.formp);
+    curlx_safefree(data->state.formp);
     data->state.mimepost = NULL;
 #endif
   }
@@ -1455,6 +1456,14 @@
   CURLcode result = CURLE_OK;
   struct UserDefined *s = &data->set;
   switch(option) {
+  case CURLOPT_CURLU:
+    /*
+     * pass CURLU to set URL
+     */
+    Curl_bufref_free(&data->state.url);
+    curlx_safefree(s->str[STRING_SET_URL]);
+    s->uh = va_arg(param, CURLU *);
+    break;
 #ifndef CURL_DISABLE_HTTP
 #ifndef CURL_DISABLE_FORM_API
   case CURLOPT_HTTPPOST:
@@ -1465,7 +1474,7 @@
     s->method = HTTPREQ_POST_FORM;
     s->opt_no_body = FALSE; /* this is implied */
     Curl_mime_cleanpart(data->state.formp);
-    Curl_safefree(data->state.formp);
+    curlx_safefree(data->state.formp);
     data->state.mimepost = NULL;
     break;
 #endif /* !CURL_DISABLE_FORM_API */
@@ -1490,67 +1499,17 @@
   case CURLOPT_SHARE: {
     struct Curl_share *set = va_arg(param, struct Curl_share *);
 
-    /* disconnect from old share, if any */
-    if(data->share) {
-      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
+    /* disconnect from old share, if any and possible */
+    result = Curl_share_easy_unlink(data);
+    if(result)
+      return result;
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(data->share->cookies == data->cookies)
-        data->cookies = NULL;
-#endif
-
-#ifndef CURL_DISABLE_HSTS
-      if(data->share->hsts == data->hsts)
-        data->hsts = NULL;
-#endif
-#ifdef USE_LIBPSL
-      if(data->psl == &data->share->psl)
-        data->psl = data->multi ? &data->multi->psl : NULL;
-#endif
-      if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
-        Curl_resolv_unlink(data, &data->state.dns[0]);
-        Curl_resolv_unlink(data, &data->state.dns[1]);
-      }
-
-      data->share->dirty--;
-
-      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-      data->share = NULL;
+    /* use new share if it set */
+    if(GOOD_SHARE_HANDLE(set)) {
+      result = Curl_share_easy_link(data, set);
+      if(result)
+        return result;
     }
-
-    if(GOOD_SHARE_HANDLE(set))
-      /* use new share if it set */
-      data->share = set;
-    if(data->share) {
-
-      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
-
-      data->share->dirty++;
-
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
-      if(data->share->cookies) {
-        /* use shared cookie list, first free own one if any */
-        Curl_cookie_cleanup(data->cookies);
-        /* enable cookies since we now use a share that uses cookies! */
-        data->cookies = data->share->cookies;
-      }
-#endif /* CURL_DISABLE_HTTP */
-#ifndef CURL_DISABLE_HSTS
-      if(data->share->hsts) {
-        /* first free the private one if any */
-        Curl_hsts_cleanup(&data->hsts);
-        data->hsts = data->share->hsts;
-      }
-#endif
-#ifdef USE_LIBPSL
-      if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
-        data->psl = &data->share->psl;
-#endif
-
-      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-    }
-    /* check for host cache not needed,
-     * it will be done by curl_easy_perform */
     break;
   }
 
@@ -1664,6 +1623,18 @@
 #endif
 
 #ifndef CURL_DISABLE_PROXY
+static CURLcode setproxy(struct Curl_easy *data, const char *proxy)
+{
+  if((data->set.str[STRING_PROXY] && proxy) &&
+     /* there was one set, is this a new one? */
+     !strcmp(data->set.str[STRING_PROXY], proxy))
+    return CURLE_OK; /* same one as before */
+
+  Curl_auth_digest_cleanup(&data->state.proxydigest);
+  memset(&data->state.authproxy, 0, sizeof(data->state.authproxy));
+  return Curl_setstropt(&data->set.str[STRING_PROXY], proxy);
+}
+
 static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option,
                                   const char *ptr)
 {
@@ -1680,12 +1651,12 @@
 
     /* URL decode the components */
     if(!result && u) {
-      Curl_safefree(s->str[STRING_PROXYUSERNAME]);
+      curlx_safefree(s->str[STRING_PROXYUSERNAME]);
       result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL,
                               REJECT_ZERO);
     }
     if(!result && p) {
-      Curl_safefree(s->str[STRING_PROXYPASSWORD]);
+      curlx_safefree(s->str[STRING_PROXYPASSWORD]);
       result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL,
                               REJECT_ZERO);
     }
@@ -1752,14 +1723,14 @@
     /*
      * Set proxy server:port to use as proxy.
      *
-     * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL)
-     * we explicitly say that we do not want to use a proxy
-     * (even though there might be environment variables saying so).
+     * If the proxy is set to "" (and CURLOPT_PRE_PROXY is set to "" or NULL)
+     * we explicitly say that we do not want to use a proxy (even though there
+     * might be environment variables saying so).
      *
      * Setting it to NULL, means no proxy but allows the environment variables
-     * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL).
+     * to decide for us (if CURLOPT_PRE_PROXY setting it to NULL).
      */
-    return Curl_setstropt(&s->str[STRING_PROXY], ptr);
+    return setproxy(data, ptr);
   case CURLOPT_PRE_PROXY:
     /*
      * Set proxy server:port to use as SOCKS proxy.
@@ -1950,7 +1921,7 @@
      */
     s->postfields = ptr;
     /* Release old copied data. */
-    Curl_safefree(s->str[STRING_COPYPOSTFIELDS]);
+    curlx_safefree(s->str[STRING_COPYPOSTFIELDS]);
     s->method = HTTPREQ_POST;
     break;
 #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */
@@ -2000,7 +1971,6 @@
      * String to set in the HTTP Referer: field.
      */
     result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr);
-    Curl_bufref_set(&data->state.referer, s->str[STRING_SET_REFERER], 0, NULL);
     break;
 
   case CURLOPT_USERAGENT:
@@ -2218,14 +2188,6 @@
      * What range of the file you want to transfer
      */
     return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr);
-  case CURLOPT_CURLU:
-    /*
-     * pass CURLU to set URL
-     */
-    Curl_bufref_free(&data->state.url);
-    Curl_safefree(s->str[STRING_SET_URL]);
-    s->uh = (CURLU *)ptr;
-    break;
   case CURLOPT_SSLCERT:
     /*
      * String that holds filename of the SSL certificate to use
@@ -2434,30 +2396,18 @@
     break;
 #endif
 #endif
-#ifdef CURLRES_ARES
+#ifdef USE_RESOLV_ARES
   case CURLOPT_DNS_SERVERS:
-    result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr);
-    if(result)
-      break;
-    return Curl_async_ares_set_dns_servers(data);
+    return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr);
 
   case CURLOPT_DNS_INTERFACE:
-    result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr);
-    if(result)
-      break;
-    return Curl_async_ares_set_dns_interface(data);
+    return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr);
 
   case CURLOPT_DNS_LOCAL_IP4:
-    result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr);
-    if(result)
-      break;
-    return Curl_async_ares_set_dns_local_ip4(data);
+    return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr);
 
   case CURLOPT_DNS_LOCAL_IP6:
-    result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr);
-    if(result)
-      break;
-    return Curl_async_ares_set_dns_local_ip6(data);
+    return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr);
 
 #endif
 #ifdef USE_UNIX_SOCKETS
@@ -2783,7 +2733,7 @@
     if(s->postfieldsize < offt &&
        s->postfields == s->str[STRING_COPYPOSTFIELDS]) {
       /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */
-      Curl_safefree(s->str[STRING_COPYPOSTFIELDS]);
+      curlx_safefree(s->str[STRING_COPYPOSTFIELDS]);
       s->postfields = NULL;
     }
     s->postfieldsize = offt;
@@ -2939,6 +2889,7 @@
     case CURLOPT_SHARE:            /* CURLSH * */
     case CURLOPT_STREAM_DEPENDS:   /* CURL * */
     case CURLOPT_STREAM_DEPENDS_E: /* CURL * */
+    case CURLOPT_CURLU:            /* CURLU * */
       return setopt_pointers(data, option, param);
     default:
       break;
@@ -2962,21 +2913,21 @@
  */
 
 #undef curl_easy_setopt
-CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...)
+CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
 {
   va_list arg;
   CURLcode result;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
 
   if(!data)
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  va_start(arg, tag);
+  va_start(arg, option);
 
-  result = Curl_vsetopt(data, tag, arg);
+  result = Curl_vsetopt(data, option, arg);
 
   va_end(arg);
   if(result == CURLE_BAD_FUNCTION_ARGUMENT)
-    failf(data, "setopt 0x%x got bad argument", tag);
+    failf(data, "setopt 0x%x got bad argument", option);
   return result;
 }
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h
index 084cd9f..b5bd733 100644
--- a/Utilities/cmcurl/lib/setup-os400.h
+++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -67,22 +67,22 @@
 
 /* GSSAPI wrappers. */
 
-extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status,
+extern OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status,
                                         gss_buffer_t in_name,
                                         gss_OID in_name_type,
-                                        gss_name_t * out_name);
+                                        gss_name_t *out_name);
 #define gss_import_name         Curl_gss_import_name_a
 
-extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status,
+extern OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status,
                                            OM_uint32 status_value,
                                            int status_type, gss_OID mech_type,
-                                           gss_msg_ctx_t * message_context,
+                                           gss_msg_ctx_t *message_context,
                                            gss_buffer_t status_string);
 #define gss_display_status      Curl_gss_display_status_a
 
-extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status,
+extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 *minor_status,
                                              gss_cred_id_t cred_handle,
-                                             gss_ctx_id_t * context_handle,
+                                             gss_ctx_id_t *context_handle,
                                              gss_name_t target_name,
                                              gss_OID mech_type,
                                              gss_flags_t req_flags,
@@ -90,10 +90,10 @@
                                              gss_channel_bindings_t
                                              input_chan_bindings,
                                              gss_buffer_t input_token,
-                                             gss_OID * actual_mech_type,
+                                             gss_OID *actual_mech_type,
                                              gss_buffer_t output_token,
-                                             gss_flags_t * ret_flags,
-                                             OM_uint32 * time_rec);
+                                             gss_flags_t *ret_flags,
+                                             OM_uint32 *time_rec);
 #define gss_init_sec_context    Curl_gss_init_sec_context_a
 
 extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status,
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c
index 7b06365..d97f45f 100644
--- a/Utilities/cmcurl/lib/sha256.c
+++ b/Utilities/cmcurl/lib/sha256.c
@@ -40,10 +40,11 @@
 /* Please keep the SSL backend-specific #if branches in this order:
  *
  * 1. USE_OPENSSL
- * 2. USE_GNUTLS
- * 3. USE_MBEDTLS
- * 4. USE_COMMON_CRYPTO
- * 5. USE_WIN32_CRYPTO
+ * 2. USE_WOLFSSL
+ * 3. USE_GNUTLS
+ * 4. USE_MBEDTLS
+ * 5. USE_COMMON_CRYPTO
+ * 6. USE_WIN32_CRYPTO
  *
  * This ensures that the same SSL branch gets activated throughout this source
  * file even if multiple backends are enabled at the same time.
@@ -86,6 +87,31 @@
   EVP_MD_CTX_destroy(ctx->openssl_ctx);
 }
 
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+
+typedef struct wc_Sha256 my_sha256_ctx;
+
+static CURLcode my_sha256_init(void *in)
+{
+  if(wc_InitSha256(in))
+    return CURLE_FAILED_INIT;
+  return CURLE_OK;
+}
+
+static void my_sha256_update(void *in,
+                             const unsigned char *data,
+                             unsigned int length)
+{
+  (void)wc_Sha256Update(in, data, (word32)length);
+}
+
+static void my_sha256_final(unsigned char *digest, void *in)
+{
+  (void)wc_Sha256Final(in, digest);
+}
+
 #elif defined(USE_GNUTLS)
 #include <nettle/sha.h>
 
@@ -229,7 +255,7 @@
     (a)[0] = (unsigned char)((((unsigned long)(val)) >> 24) & 0xff); \
     (a)[1] = (unsigned char)((((unsigned long)(val)) >> 16) & 0xff); \
     (a)[2] = (unsigned char)((((unsigned long)(val)) >>  8) & 0xff); \
-    (a)[3] = (unsigned char) (((unsigned long)(val)) & 0xff);        \
+    (a)[3] = (unsigned char)(((unsigned long)(val)) & 0xff);         \
   } while(0)
 
 #define WPA_PUT_BE64(a, val)                            \
@@ -452,14 +478,19 @@
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input,
-                       const size_t len)
+                       size_t len)
 {
   CURLcode result;
   my_sha256_ctx ctx;
 
   result = my_sha256_init(&ctx);
   if(!result) {
-    my_sha256_update(&ctx, input, curlx_uztoui(len));
+    do {
+      unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX);
+      my_sha256_update(&ctx, input, ilen);
+      len -= ilen;
+      input += ilen;
+    } while(len);
     my_sha256_final(output, &ctx);
   }
   return result;
diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c
index c6dcdc1..83fd291 100644
--- a/Utilities/cmcurl/lib/slist.c
+++ b/Utilities/cmcurl/lib/slist.c
@@ -132,7 +132,7 @@
   item = list;
   do {
     next = item->next;
-    Curl_safefree(item->data);
+    curlx_safefree(item->data);
     curlx_free(item);
     item = next;
   } while(next);
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 00297ad..6a97d2e 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -25,7 +25,7 @@
 #include "curl_setup.h"
 #include "urldata.h"
 
-#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
+#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE)
 
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>  /* for htons() */
@@ -376,7 +376,7 @@
   struct smb_request *req = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(req->path);
+  curlx_safefree(req->path);
   curlx_free(req);
 }
 
@@ -385,10 +385,10 @@
   struct smb_conn *smbc = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(smbc->share);
-  Curl_safefree(smbc->domain);
-  Curl_safefree(smbc->recv_buf);
-  Curl_safefree(smbc->send_buf);
+  curlx_safefree(smbc->share);
+  curlx_safefree(smbc->domain);
+  curlx_safefree(smbc->recv_buf);
+  curlx_safefree(smbc->send_buf);
   curlx_free(smbc);
 }
 
@@ -406,7 +406,7 @@
     return result;
 
   /* Parse the path for the share */
-  Curl_safefree(smbc->share);
+  curlx_safefree(smbc->share);
   smbc->share = curlx_strdup((*path == '/' || *path == '\\')
                              ? path + 1 : path);
   curlx_free(path);
@@ -419,7 +419,7 @@
 
   /* The share must be present */
   if(!slash) {
-    Curl_safefree(smbc->share);
+    curlx_safefree(smbc->share);
     failf(data, "missing share in URL path for SMB");
     return CURLE_URL_MALFORMAT;
   }
@@ -1208,7 +1208,7 @@
 /*
  * SMB handler interface
  */
-static const struct Curl_protocol Curl_protocol_smb = {
+const struct Curl_protocol Curl_protocol_smb = {
   smb_setup_connection,                 /* setup_connection */
   smb_do,                               /* do_it */
   ZERO_NULL,                            /* done */
@@ -1223,42 +1223,9 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
-#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
-
-/*
- * SMB handler interface
- */
-const struct Curl_scheme Curl_scheme_smb = {
-  "smb",                                /* scheme */
-#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE)
-  ZERO_NULL,
-#else
-  &Curl_protocol_smb,
-#endif
-  CURLPROTO_SMB,                        /* protocol */
-  CURLPROTO_SMB,                        /* family */
-  PROTOPT_CONN_REUSE,                   /* flags */
-  PORT_SMB,                             /* defport */
-};
-
-/*
- * SMBS handler interface
- */
-const struct Curl_scheme Curl_scheme_smbs = {
-  "smbs",                               /* scheme */
-#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) || \
-  !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_smb,
-#endif
-  CURLPROTO_SMBS,                       /* protocol */
-  CURLPROTO_SMB,                        /* family */
-  PROTOPT_SSL | PROTOPT_CONN_REUSE,     /* flags */
-  PORT_SMBS,                            /* defport */
-};
+#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h
index d375404..5a92ad1 100644
--- a/Utilities/cmcurl/lib/smb.h
+++ b/Utilities/cmcurl/lib/smb.h
@@ -24,7 +24,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_smb;
-extern const struct Curl_scheme Curl_scheme_smbs;
+#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE)
+extern const struct Curl_protocol Curl_protocol_smb;
+#endif
 
 #endif /* HEADER_CURL_SMB_H */
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 6088985..b5c425c 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -431,7 +431,7 @@
     ctx->eos = TRUE;
   }
   *peos = (bool)ctx->eos;
-  DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
+  DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d",
                blen, result, *pnread, *peos));
   return result;
 }
@@ -485,7 +485,7 @@
                            const char *line, size_t len, int *resp)
 {
   struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
-  bool result = FALSE;
+  bool end = FALSE;
   (void)data;
 
   DEBUGASSERT(smtpc);
@@ -504,7 +504,7 @@
     char tmpline[6];
     curl_off_t code;
     const char *p = tmpline;
-    result = TRUE;
+    end = TRUE;
     memcpy(tmpline, line, (len == 5 ? 5 : 3));
     tmpline[len == 5 ? 5 : 3] = 0;
     if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
@@ -518,11 +518,11 @@
   /* Do we have a multiline (continuation) response? */
   else if(line[3] == '-' &&
           (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
-    result = TRUE;
+    end = TRUE;
     *resp = 1;  /* Internal response code */
   }
 
-  return result;
+  return end;
 }
 
 /***********************************************************************
@@ -1724,7 +1724,7 @@
     return CURLE_OK;
 
   /* Cleanup our per-request based variables */
-  Curl_safefree(smtp->custom);
+  curlx_safefree(smtp->custom);
 
   if(status) {
     connclose(conn, "SMTP done with bad status"); /* marked for closure */
@@ -1953,7 +1953,7 @@
   (void)key;
   (void)klen;
   Curl_pp_disconnect(&smtpc->pp);
-  Curl_safefree(smtpc->domain);
+  curlx_safefree(smtpc->domain);
   curlx_free(smtpc);
 }
 
@@ -1984,7 +1984,7 @@
 /*
  * SMTP protocol handler.
  */
-static const struct Curl_protocol Curl_protocol_smtp = {
+const struct Curl_protocol Curl_protocol_smtp = {
   smtp_setup_connection,            /* setup_connection */
   smtp_do,                          /* do_it */
   smtp_done,                        /* done */
@@ -1999,43 +1999,9 @@
   smtp_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp_hd */
-  ZERO_NULL,                        /* connection_check */
+  ZERO_NULL,                        /* connection_is_dead */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* follow */
 };
 
 #endif /* CURL_DISABLE_SMTP */
-
-/*
- * SMTP protocol handler.
- */
-const struct Curl_scheme Curl_scheme_smtp = {
-  "smtp",                           /* scheme */
-#ifdef CURL_DISABLE_SMTP
-  ZERO_NULL,
-#else
-  &Curl_protocol_smtp,
-#endif
-  CURLPROTO_SMTP,                   /* protocol */
-  CURLPROTO_SMTP,                   /* family */
-  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
-  PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE,
-  PORT_SMTP,                        /* defport */
-};
-
-/*
- * SMTPS protocol handler.
- */
-const struct Curl_scheme Curl_scheme_smtps = {
-  "smtps",                          /* scheme */
-#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_smtp,
-#endif
-  CURLPROTO_SMTPS,                  /* protocol */
-  CURLPROTO_SMTP,                   /* family */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
-  PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE,
-  PORT_SMTPS,                       /* defport */
-};
diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h
index 39f8d78..75b8197 100644
--- a/Utilities/cmcurl/lib/smtp.h
+++ b/Utilities/cmcurl/lib/smtp.h
@@ -23,7 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_smtp;
-extern const struct Curl_scheme Curl_scheme_smtps;
+#ifndef CURL_DISABLE_SMTP
+extern const struct Curl_protocol Curl_protocol_smtp;
+#endif
 
 #endif /* HEADER_CURL_SMTP_H */
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h
index 2aa1733..0427e72 100644
--- a/Utilities/cmcurl/lib/socketpair.h
+++ b/Utilities/cmcurl/lib/socketpair.h
@@ -37,7 +37,7 @@
 CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all);
 
 #else
-#define Curl_wakeup_destroy(x)  Curl_nop_stmt
+#define Curl_wakeup_destroy(x) Curl_nop_stmt
 #endif
 
 #endif /* HEADER_CURL_SOCKETPAIR_H */
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index b7c3bd6..b3da5be 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -38,6 +38,7 @@
 #include "curl_trc.h"
 #include "select.h"
 #include "cfilters.h"
+#include "cf-dns.h"
 #include "connect.h"
 #include "socks.h"
 #include "curlx/inet_pton.h"
@@ -94,18 +95,21 @@
 #define SOCKS_CHUNKS        1
 
 
-struct socks_state {
+struct socks_ctx {
   enum socks_state_t state;
   struct bufq iobuf;
-  const char *hostname;
-  int remote_port;
-  const char *proxy_user;
-  const char *proxy_password;
+  uint16_t remote_port;
+  const char *user;
+  const char *passwd;
   CURLproxycode presult;
+  uint32_t resolv_id;
+  uint8_t ip_version;
+  uint8_t proxy_type;
   unsigned char version;
   BIT(resolve_local);
   BIT(start_resolving);
   BIT(socks4a);
+  char hostname[1];
 };
 
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@@ -127,13 +131,15 @@
   *pnread = 0;
   for(;;) {
     timediff_t timeout_ms = Curl_timeleft_ms(data);
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+
     if(timeout_ms < 0) {
       /* we already got the timeout */
       return CURLE_OPERATION_TIMEDOUT;
     }
     if(!timeout_ms)
       timeout_ms = TIMEDIFF_T_MAX;
-    if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0)
+    if(SOCKET_READABLE(sock, timeout_ms) <= 0)
       return CURLE_OPERATION_TIMEDOUT;
     result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread);
     if(result == CURLE_AGAIN)
@@ -162,7 +168,7 @@
 #endif
 
 /* always use this function to change state, to make debugging easier */
-static void socksstate(struct socks_state *sx,
+static void socksstate(struct socks_ctx *sx,
                        struct Curl_cfilter *cf,
                        struct Curl_easy *data,
                        enum socks_state_t state
@@ -189,7 +195,7 @@
 #endif
 }
 
-static CURLproxycode socks_failed(struct socks_state *sx,
+static CURLproxycode socks_failed(struct socks_ctx *sx,
                                   struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   CURLproxycode presult)
@@ -199,7 +205,7 @@
   return presult;
 }
 
-static CURLproxycode socks_flush(struct socks_state *sx,
+static CURLproxycode socks_flush(struct socks_ctx *sx,
                                  struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  bool *done)
@@ -223,7 +229,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks_recv(struct socks_state *sx,
+static CURLproxycode socks_recv(struct socks_ctx *sx,
                                 struct Curl_cfilter *cf,
                                 struct Curl_easy *data,
                                 size_t min_bytes,
@@ -257,7 +263,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks4_req_add_hd(struct socks_state *sx,
+static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx,
                                        struct Curl_easy *data)
 {
   unsigned char buf[4];
@@ -276,14 +282,14 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks4_req_add_user(struct socks_state *sx,
+static CURLproxycode socks4_req_add_user(struct socks_ctx *sx,
                                          struct Curl_easy *data)
 {
   CURLcode result;
   size_t nwritten;
 
-  if(sx->proxy_user) {
-    size_t plen = strlen(sx->proxy_user);
+  if(sx->user) {
+    size_t plen = strlen(sx->user);
     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
@@ -292,7 +298,7 @@
       return CURLPX_LONG_USER;
     }
     /* add proxy name WITH trailing zero */
-    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1,
+    result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1,
                               &nwritten);
     if(result || (nwritten != (plen + 1)))
       return CURLPX_SEND_REQUEST;
@@ -307,73 +313,58 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks4_resolving(struct socks_state *sx,
+static CURLproxycode socks4_resolving(struct socks_ctx *sx,
                                       struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
                                       bool *done)
 {
-  struct Curl_dns_entry *dns = NULL;
-  struct Curl_addrinfo *hp = NULL;
+  const struct Curl_addrinfo *ai = NULL;
   CURLcode result;
   size_t nwritten;
+  bool dns_done;
 
   *done = FALSE;
   if(sx->start_resolving) {
     /* need to resolve hostname to add destination address */
     sx->start_resolving = FALSE;
-    DEBUGASSERT(sx->hostname && *sx->hostname);
-
-    result = Curl_resolv(data, sx->hostname, sx->remote_port,
-                         cf->conn->ip_version, TRUE, &dns);
-    if(result == CURLE_AGAIN) {
-      CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname);
-      return CURLPX_OK;
+    result = Curl_cf_dns_insert_after(
+      cf, data, Curl_resolv_dns_queries(data, sx->ip_version),
+      sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE);
+    if(result) {
+      failf(data, "unable to create DNS filter for socks");
+      return CURLPX_UNKNOWN_FAIL;
     }
-    else if(result)
-      return CURLPX_RESOLVE_HOST;
-  }
-  else {
-    /* check if we have the name resolved by now */
-    result = Curl_resolv_check(data, &dns);
-    if(!result && !dns)
-      return CURLPX_OK;
   }
 
-  if(result || !dns) {
-    failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname);
-    if(dns)
-      Curl_resolv_unlink(data, &dns);
+  /* resolve the hostname by connecting the DNS filter */
+  result = Curl_conn_cf_connect(cf->next, data, &dns_done);
+  if(result) {
+    failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
+          sx->hostname);
     return CURLPX_RESOLVE_HOST;
   }
+  else if(!dns_done)
+    return CURLPX_OK;
 
-  /*
-   * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
-   * returns a Curl_addrinfo pointer that may not always look the same.
-   */
-  /* scan for the first IPv4 address */
-  hp = dns->addr;
-  while(hp && (hp->ai_family != AF_INET))
-    hp = hp->ai_next;
-
-  if(hp) {
+  ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0);
+  if(ai) {
     struct sockaddr_in *saddr_in;
     char ipbuf[64];
 
-    Curl_printable_address(hp, ipbuf, sizeof(ipbuf));
+    Curl_printable_address(ai, ipbuf, sizeof(ipbuf));
     CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)",
                 ipbuf);
 
-    saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
+    saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr;
     result = Curl_bufq_write(&sx->iobuf,
                              (unsigned char *)&saddr_in->sin_addr.s_addr, 4,
                              &nwritten);
 
-    Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
     if(result || (nwritten != 4))
       return CURLPX_SEND_REQUEST;
   }
   else {
-    Curl_resolv_unlink(data, &dns);
+    /* No ipv4 address resolved */
     failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
     return CURLPX_RESOLVE_HOST;
   }
@@ -382,7 +373,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks4_check_resp(struct socks_state *sx,
+static CURLproxycode socks4_check_resp(struct socks_ctx *sx,
                                        struct Curl_cfilter *cf,
                                        struct Curl_easy *data)
 {
@@ -431,7 +422,7 @@
           "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
           ", request rejected or failed.",
           resp[4], resp[5], resp[6], resp[7],
-          ((resp[2] << 8) | resp[3]), resp[1]);
+          (unsigned int)((resp[2] << 8) | resp[3]), resp[1]);
     return CURLPX_REQUEST_FAILED;
   case 92:
     failf(data,
@@ -439,7 +430,7 @@
           ", request rejected because SOCKS server cannot connect to "
           "identd on the client.",
           resp[4], resp[5], resp[6], resp[7],
-          ((resp[2] << 8) | resp[3]), resp[1]);
+          (unsigned int)((resp[2] << 8) | resp[3]), resp[1]);
     return CURLPX_IDENTD;
   case 93:
     failf(data,
@@ -447,14 +438,14 @@
           ", request rejected because the client program and identd "
           "report different user-ids.",
           resp[4], resp[5], resp[6], resp[7],
-          ((resp[2] << 8) | resp[3]), resp[1]);
+          (unsigned int)((resp[2] << 8) | resp[3]), resp[1]);
     return CURLPX_IDENTD_DIFFER;
   default:
     failf(data,
           "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)"
           ", Unknown.",
           resp[4], resp[5], resp[6], resp[7],
-          ((resp[2] << 8) | resp[3]), resp[1]);
+          (unsigned int)((resp[2] << 8) | resp[3]), resp[1]);
     return CURLPX_UNKNOWN_FAIL;
   }
 }
@@ -471,7 +462,7 @@
  *   Nonsupport "Identification Protocol (RFC1413)"
  */
 static CURLproxycode socks4_connect(struct Curl_cfilter *cf,
-                                    struct socks_state *sx,
+                                    struct socks_ctx *sx,
                                     struct Curl_easy *data)
 {
   size_t nwritten;
@@ -489,16 +480,14 @@
   case SOCKS4_ST_START:
     Curl_bufq_reset(&sx->iobuf);
     sx->start_resolving = FALSE;
-    sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A);
+    sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A);
     sx->resolve_local = !sx->socks4a;
     sx->presult = CURLPX_OK;
 
     /* SOCKS4 can only do IPv4, insist! */
-    cf->conn->ip_version = CURL_IPRESOLVE_V4;
-    CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d",
-                sx->socks4a ? "a" : "",
-                cf->conn->bits.httpproxy ? " HTTP proxy" : "",
-                sx->hostname, sx->remote_port);
+    sx->ip_version = CURL_IPRESOLVE_V4;
+    CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u",
+                sx->socks4a ? "a" : "", sx->hostname, sx->remote_port);
 
     /*
      * Compose socks4 request
@@ -591,7 +580,7 @@
 }
 
 static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf,
-                                      struct socks_state *sx,
+                                      struct socks_ctx *sx,
                                       struct Curl_easy *data)
 {
   const unsigned char auth = data->set.socks5auth;
@@ -613,7 +602,7 @@
           "CURLOPT_SOCKS5_AUTH: %u", auth);
   if(!(auth & CURLAUTH_BASIC))
     /* disable username/password auth */
-    sx->proxy_user = NULL;
+    sx->user = NULL;
 
   req[0] = 5;   /* version */
   nauths = 1;
@@ -624,7 +613,7 @@
     req[1 + nauths] = 1; /* GSS-API */
   }
 #endif
-  if(sx->proxy_user) {
+  if(sx->user) {
     ++nauths;
     req[1 + nauths] = 2; /* username/password */
   }
@@ -637,7 +626,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks5_check_resp0(struct socks_state *sx,
+static CURLproxycode socks5_check_resp0(struct socks_ctx *sx,
                                         struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
@@ -689,7 +678,7 @@
 }
 
 static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf,
-                                      struct socks_state *sx,
+                                      struct socks_ctx *sx,
                                       struct Curl_easy *data)
 {
   /* Needs username and password */
@@ -697,9 +686,9 @@
   unsigned char buf[2];
   CURLcode result;
 
-  if(sx->proxy_user && sx->proxy_password) {
-    ulen = strlen(sx->proxy_user);
-    plen = strlen(sx->proxy_password);
+  if(sx->user && sx->passwd) {
+    ulen = strlen(sx->user);
+    plen = strlen(sx->passwd);
     /* the lengths must fit in a single byte */
     if(ulen > 255) {
       failf(data, "Excessive username length for proxy auth");
@@ -724,7 +713,7 @@
   if(result || (nwritten != 2))
     return CURLPX_SEND_REQUEST;
   if(ulen) {
-    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten);
+    result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten);
     if(result || (nwritten != ulen))
       return CURLPX_SEND_REQUEST;
   }
@@ -733,7 +722,7 @@
   if(result || (nwritten != 1))
     return CURLPX_SEND_REQUEST;
   if(plen) {
-    result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten);
+    result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten);
     if(result || (nwritten != plen))
       return CURLPX_SEND_REQUEST;
   }
@@ -741,7 +730,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks5_check_auth_resp(struct socks_state *sx,
+static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx,
                                             struct Curl_cfilter *cf,
                                             struct Curl_easy *data)
 {
@@ -766,7 +755,7 @@
   return CURLPX_OK;
 }
 
-static CURLproxycode socks5_req1_init(struct socks_state *sx,
+static CURLproxycode socks5_req1_init(struct socks_ctx *sx,
                                       struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
@@ -790,7 +779,7 @@
 
   /* remote resolving, send what type+addr/string to resolve */
 #ifdef USE_IPV6
-  if(cf->conn->bits.ipv6_ip) {
+  if(strchr(sx->hostname, ':')) {
     desttype = 4;
     destination = ipbuf;
     destlen = 16;
@@ -806,6 +795,10 @@
   }
   else {
     const size_t hostname_len = strlen(sx->hostname);
+    /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so
+       this cast to unsigned char is safe. Assert to guard against future
+       refactoring that might remove or reorder that earlier check. */
+    DEBUGASSERT(hostname_len <= 255);
     desttype = 3;
     destination = (const unsigned char *)sx->hostname;
     destlen = (unsigned char)hostname_len; /* one byte length */
@@ -826,18 +819,17 @@
   result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten);
   if(result || (nwritten != 2))
     return CURLPX_SEND_REQUEST;
-  CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)",
+  CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)",
               sx->hostname, sx->remote_port);
   return CURLPX_OK;
 }
 
-static CURLproxycode socks5_resolving(struct socks_state *sx,
+static CURLproxycode socks5_resolving(struct socks_ctx *sx,
                                       struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
                                       bool *done)
 {
-  struct Curl_dns_entry *dns = NULL;
-  struct Curl_addrinfo *hp = NULL;
+  const struct Curl_addrinfo *ai = NULL;
   char dest[MAX_IPADR_LEN];  /* printable address */
   const unsigned char *destination = NULL;
   unsigned char desttype = 1, destlen = 4;
@@ -845,71 +837,62 @@
   CURLcode result;
   CURLproxycode presult = CURLPX_OK;
   size_t nwritten;
+  bool dns_done;
 
   *done = FALSE;
   if(sx->start_resolving) {
     /* need to resolve hostname to add destination address */
     sx->start_resolving = FALSE;
-    DEBUGASSERT(sx->hostname && *sx->hostname);
-
-    result = Curl_resolv(data, sx->hostname, sx->remote_port,
-                         cf->conn->ip_version, TRUE, &dns);
-    if(result == CURLE_AGAIN) {
-      CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname);
-      return CURLPX_OK;
+    result = Curl_cf_dns_insert_after(
+      cf, data, Curl_resolv_dns_queries(data, sx->ip_version),
+      sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE);
+    if(result) {
+      failf(data, "unable to create DNS filter for socks");
+      return CURLPX_UNKNOWN_FAIL;
     }
-    else if(result)
-      return CURLPX_RESOLVE_HOST;
-  }
-  else {
-    /* check if we have the name resolved by now */
-    result = Curl_resolv_check(data, &dns);
-    if(!result && !dns)
-      return CURLPX_OK;
   }
 
-  if(result || !dns) {
+  /* resolve the hostname by connecting the DNS filter */
+  result = Curl_conn_cf_connect(cf->next, data, &dns_done);
+  if(result) {
     failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname);
-    presult = CURLPX_RESOLVE_HOST;
-    goto out;
+    return CURLPX_RESOLVE_HOST;
   }
+  else if(!dns_done)
+    return CURLPX_OK;
 
-  if(dns)
-    hp = dns->addr;
 #ifdef USE_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;
-  }
+  if(data->set.ipver != CURL_IPRESOLVE_V4)
+    ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0);
 #endif
-  if(!hp) {
+  if(!ai)
+    ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0);
+
+  if(!ai) {
     failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname);
     presult = CURLPX_RESOLVE_HOST;
     goto out;
   }
 
-  Curl_printable_address(hp, dest, sizeof(dest));
+  Curl_printable_address(ai, dest, sizeof(dest));
 
-  if(hp->ai_family == AF_INET) {
+  if(ai->ai_family == AF_INET) {
     struct sockaddr_in *saddr_in;
     desttype = 1; /* ATYP: IPv4 = 1 */
     destlen = 4;
-    saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
+    saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr;
     destination = (const unsigned char *)&saddr_in->sin_addr.s_addr;
-    CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)",
+    CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)",
                 dest, sx->remote_port);
   }
 #ifdef USE_IPV6
-  else if(hp->ai_family == AF_INET6) {
+  else if(ai->ai_family == AF_INET6) {
     struct sockaddr_in6 *saddr_in6;
     desttype = 4; /* ATYP: IPv6 = 4 */
     destlen = 16;
-    saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
+    saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr;
     destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr;
-    CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)",
+    CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)",
                 dest, sx->remote_port);
   }
 #endif
@@ -941,13 +924,11 @@
   }
 
 out:
-  if(dns)
-    Curl_resolv_unlink(data, &dns);
   *done = (presult == CURLPX_OK);
   return presult;
 }
 
-static CURLproxycode socks5_recv_resp1(struct socks_state *sx,
+static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx,
                                        struct Curl_cfilter *cf,
                                        struct Curl_easy *data,
                                        bool *done)
@@ -1046,7 +1027,7 @@
  * destination server.
  */
 static CURLproxycode socks5_connect(struct Curl_cfilter *cf,
-                                    struct socks_state *sx,
+                                    struct socks_ctx *sx,
                                     struct Curl_easy *data)
 {
   CURLproxycode presult;
@@ -1056,14 +1037,13 @@
   switch(sx->state) {
   case SOCKS_ST_INIT:
     sx->version = 5;
-    sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5);
+    sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5);
     sxstate(sx, cf, data, SOCKS5_ST_START);
     FALLTHROUGH();
 
   case SOCKS5_ST_START:
-    if(cf->conn->bits.httpproxy)
-      CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d",
-                  sx->hostname, sx->remote_port);
+    CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u",
+                sx->hostname, sx->remote_port);
     presult = socks5_req0_init(cf, sx, data);
     if(presult)
       return socks_failed(sx, cf, data, presult);
@@ -1198,13 +1178,11 @@
   }
 }
 
-static void socks_proxy_cf_free(struct Curl_cfilter *cf)
+static void socks_proxy_ctx_free(struct socks_ctx *ctx)
 {
-  struct socks_state *sxstate = cf->ctx;
-  if(sxstate) {
-    Curl_bufq_free(&sxstate->iobuf);
-    curlx_free(sxstate);
-    cf->ctx = NULL;
+  if(ctx) {
+    Curl_bufq_free(&ctx->iobuf);
+    curlx_free(ctx);
   }
 }
 
@@ -1219,11 +1197,9 @@
                                        struct Curl_easy *data,
                                        bool *done)
 {
-  CURLcode result;
-  struct connectdata *conn = cf->conn;
-  int sockindex = cf->sockindex;
-  struct socks_state *sx = cf->ctx;
+  struct socks_ctx *ctx = cf->ctx;
   CURLproxycode pxresult = CURLPX_OK;
+  CURLcode result;
 
   if(cf->connected) {
     *done = TRUE;
@@ -1234,47 +1210,19 @@
   if(result || !*done)
     return result;
 
-  if(!sx) {
-    cf->ctx = sx = curlx_calloc(1, sizeof(*sx));
-    if(!sx) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
-
-    /* for the secondary socket (FTP), use the "connect to host"
-     * but ignore the "connect to port" (use the secondary port)
-     */
-    sx->hostname =
-      conn->bits.httpproxy ?
-      conn->http_proxy.host.name :
-      conn->bits.conn_to_host ?
-      conn->conn_to_host.name :
-      sockindex == SECONDARYSOCKET ?
-      conn->secondaryhostname : conn->host.name;
-    sx->remote_port =
-      conn->bits.httpproxy ? (int)conn->http_proxy.port :
-      sockindex == SECONDARYSOCKET ? conn->secondary_port :
-      conn->bits.conn_to_port ? conn->conn_to_port :
-      conn->remote_port;
-    sx->proxy_user = conn->socks_proxy.user;
-    sx->proxy_password = conn->socks_proxy.passwd;
-    Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
-                    BUFQ_OPT_SOFT_LIMIT);
-  }
-
-  switch(conn->socks_proxy.proxytype) {
+  switch(ctx->proxy_type) {
   case CURLPROXY_SOCKS5:
   case CURLPROXY_SOCKS5_HOSTNAME:
-    pxresult = socks5_connect(cf, sx, data);
+    pxresult = socks5_connect(cf, ctx, data);
     break;
 
   case CURLPROXY_SOCKS4:
   case CURLPROXY_SOCKS4A:
-    pxresult = socks4_connect(cf, sx, data);
+    pxresult = socks4_connect(cf, ctx, data);
     break;
 
   default:
-    failf(data, "unknown proxytype option given");
+    DEBUGASSERT(0); /* should not come here, checked it at creation time */
     result = CURLE_COULDNT_CONNECT;
     goto out;
   }
@@ -1284,7 +1232,7 @@
     data->info.pxcode = pxresult;
     goto out;
   }
-  else if(sx->state != SOCKS_ST_SUCCESS)
+  else if(ctx->state != SOCKS_ST_SUCCESS)
     goto out;
 
 #ifdef CURLVERBOSE
@@ -1292,22 +1240,25 @@
     struct ip_quadruple ipquad;
     bool is_ipv6;
     if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad))
-      infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u "
+      infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d "
             "(via %s port %u)",
-            (sockindex == SECONDARYSOCKET) ? "2nd " : "",
+            (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
             ipquad.local_ip, ipquad.local_port,
-            sx->hostname, sx->remote_port,
+            ctx->hostname, ctx->remote_port,
             ipquad.remote_ip, ipquad.remote_port);
     else
       infof(data, "Opened %sSOCKS connection",
-            (sockindex == SECONDARYSOCKET) ? "2nd " : "");
+            (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "");
   }
 #endif
-  socks_proxy_cf_free(cf);
   cf->connected = TRUE;
 
 out:
   *done = (bool)cf->connected;
+  if(*done || result) {
+    ctx->user = NULL;
+    ctx->passwd = NULL;
+  }
   return result;
 }
 
@@ -1315,7 +1266,7 @@
                                         struct Curl_easy *data,
                                         struct easy_pollset *ps)
 {
-  struct socks_state *sx = cf->ctx;
+  struct socks_ctx *sx = cf->ctx;
   CURLcode result = CURLE_OK;
 
   if(!cf->connected && sx) {
@@ -1342,24 +1293,24 @@
 static void socks_proxy_cf_close(struct Curl_cfilter *cf,
                                  struct Curl_easy *data)
 {
-  DEBUGASSERT(cf->next);
   cf->connected = FALSE;
-  socks_proxy_cf_free(cf);
-  cf->next->cft->do_close(cf->next, data);
+  if(cf->next)
+    cf->next->cft->do_close(cf->next, data);
 }
 
 static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
                                    struct Curl_easy *data)
 {
   (void)data;
-  socks_proxy_cf_free(cf);
+  socks_proxy_ctx_free(cf->ctx);
+  cf->ctx = NULL;
 }
 
 static CURLcode socks_cf_query(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
                                int query, int *pres1, void *pres2)
 {
-  struct socks_state *sx = cf->ctx;
+  struct socks_ctx *sx = cf->ctx;
 
   switch(query) {
   case CF_QUERY_HOST_PORT:
@@ -1402,15 +1353,53 @@
 };
 
 CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
-                                          struct Curl_easy *data)
+                                          struct Curl_easy *data,
+                                          const char *hostname,
+                                          uint16_t port,
+                                          uint8_t ip_version,
+                                          uint8_t proxy_type,
+                                          const char *user,
+                                          const char *passwd)
 {
   struct Curl_cfilter *cf;
+  struct socks_ctx *ctx;
+  size_t hostlen = hostname ? strlen(hostname) : 0;
   CURLcode result;
 
-  (void)data;
-  result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
+  if(!hostlen)
+    return CURLE_FAILED_INIT;
+
+  switch(proxy_type) {
+  case CURLPROXY_SOCKS5:
+  case CURLPROXY_SOCKS5_HOSTNAME:
+  case CURLPROXY_SOCKS4:
+  case CURLPROXY_SOCKS4A:
+    break; /* all supported */
+  default:
+    failf(data, "unknown proxytype %d option given", proxy_type);
+    return CURLE_COULDNT_CONNECT;
+  }
+
+  /* NUL byte already part of struct size */
+  ctx = curlx_calloc(1, sizeof(*ctx) + hostlen);
+  if(!ctx) {
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  memcpy(ctx->hostname, hostname, hostlen);
+  ctx->remote_port = port;
+  ctx->ip_version = ip_version;
+  ctx->proxy_type = proxy_type;
+  ctx->user = user;
+  ctx->passwd = passwd;
+  Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
+                  BUFQ_OPT_SOFT_LIMIT);
+
+  result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx);
   if(!result)
     Curl_conn_cf_insert_after(cf_at, cf);
+  else
+    socks_proxy_ctx_free(ctx);
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h
index 520fb75..ea36832 100644
--- a/Utilities/cmcurl/lib/socks.h
+++ b/Utilities/cmcurl/lib/socks.h
@@ -46,8 +46,19 @@
                                       struct Curl_easy *data);
 #endif
 
+/* Insert a SOCKS filter after `cf_at` for connecting to `hostname`
+ * and `port` with optional credentials.
+ * Credentials are NOT duplicated and are
+ * expected to exist during connect phase.
+ */
 CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
-                                          struct Curl_easy *data);
+                                          struct Curl_easy *data,
+                                          const char *hostname,
+                                          uint16_t port,
+                                          uint8_t ip_version,
+                                          uint8_t proxy_type,
+                                          const char *user,
+                                          const char *passwd);
 
 extern struct Curl_cftype Curl_cft_socks_proxy;
 
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index 962fcd0..32db070 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -35,7 +35,7 @@
 #include "socks.h"
 #include "curlx/strdup.h"
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -106,7 +106,7 @@
   CURLcode code;
   size_t actualread;
   size_t nwritten;
-  int result;
+  CURLcode result;
   OM_uint32 gss_major_status, gss_minor_status, gss_status;
   OM_uint32 gss_ret_flags;
   int gss_conf_state, gss_enc;
@@ -156,7 +156,7 @@
                                        GSS_C_NT_HOSTBASED_SERVICE, &server);
   }
 
-  Curl_safefree(service.value);
+  curlx_safefree(service.value);
   service.length = 0;
 
   if(check_gss_err(data, gss_major_status,
@@ -183,7 +183,7 @@
                                                  &gss_ret_flags);
 
     if(gss_token != GSS_C_NO_BUFFER) {
-      Curl_safefree(gss_recv_token.value);
+      curlx_safefree(gss_recv_token.value);
       gss_recv_token.length = 0;
     }
     if(check_gss_err(data, gss_major_status,
@@ -228,7 +228,7 @@
     if(gss_major_status != GSS_S_CONTINUE_NEEDED)
       break;
 
-    /* analyse response */
+    /* analyze response */
 
     /*   GSS-API response looks like
      * +----+------+-----+----------------+
@@ -266,6 +266,13 @@
     memcpy(&us_length, socksreq + 2, sizeof(short));
     us_length = ntohs(us_length);
 
+    if(!us_length) {
+      failf(data, "Invalid zero-length GSS-API authentication token.");
+      gss_release_name(&gss_status, &server);
+      Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
+      return CURLE_COULDNT_CONNECT;
+    }
+
     gss_recv_token.length = us_length;
     gss_recv_token.value = curlx_malloc(gss_recv_token.length);
     if(!gss_recv_token.value) {
@@ -283,7 +290,7 @@
     if(result || (actualread != us_length)) {
       failf(data, "Failed to receive GSS-API authentication token.");
       gss_release_name(&gss_status, &server);
-      Curl_safefree(gss_recv_token.value);
+      curlx_safefree(gss_recv_token.value);
       gss_recv_token.length = 0;
       Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
       return CURLE_COULDNT_CONNECT;
@@ -385,14 +392,14 @@
                                 &gss_conf_state, &gss_w_token);
 
     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) {
-      Curl_safefree(gss_send_token.value);
+      curlx_safefree(gss_send_token.value);
       gss_send_token.length = 0;
       gss_release_buffer(&gss_status, &gss_w_token);
       Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
       failf(data, "Failed to wrap GSS-API encryption value into token.");
       return CURLE_COULDNT_CONNECT;
     }
-    Curl_safefree(gss_send_token.value);
+    curlx_safefree(gss_send_token.value);
     gss_send_token.length = 0;
 
     us_length = htons((unsigned short)gss_w_token.length);
@@ -453,6 +460,12 @@
   memcpy(&us_length, socksreq + 2, sizeof(short));
   us_length = ntohs(us_length);
 
+  if(!us_length) {
+    failf(data, "Invalid zero-length GSS-API encryption token.");
+    Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
+    return CURLE_COULDNT_CONNECT;
+  }
+
   gss_recv_token.length = us_length;
   gss_recv_token.value = curlx_malloc(gss_recv_token.length);
   if(!gss_recv_token.value) {
@@ -464,7 +477,7 @@
 
   if(result || (actualread != us_length)) {
     failf(data, "Failed to receive GSS-API encryption type.");
-    Curl_safefree(gss_recv_token.value);
+    curlx_safefree(gss_recv_token.value);
     gss_recv_token.length = 0;
     Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
     return CURLE_COULDNT_CONNECT;
@@ -476,14 +489,14 @@
                                   0, GSS_C_QOP_DEFAULT);
 
     if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) {
-      Curl_safefree(gss_recv_token.value);
+      curlx_safefree(gss_recv_token.value);
       gss_recv_token.length = 0;
       gss_release_buffer(&gss_status, &gss_w_token);
       Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
       failf(data, "Failed to unwrap GSS-API encryption value into token.");
       return CURLE_COULDNT_CONNECT;
     }
-    Curl_safefree(gss_recv_token.value);
+    curlx_safefree(gss_recv_token.value);
     gss_recv_token.length = 0;
 
     if(gss_w_token.length != 1) {
@@ -501,14 +514,14 @@
     if(gss_recv_token.length != 1) {
       failf(data, "Invalid GSS-API encryption response length (%zu).",
             gss_recv_token.length);
-      Curl_safefree(gss_recv_token.value);
+      curlx_safefree(gss_recv_token.value);
       gss_recv_token.length = 0;
       Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL);
       return CURLE_COULDNT_CONNECT;
     }
 
     memcpy(socksreq, gss_recv_token.value, gss_recv_token.length);
-    Curl_safefree(gss_recv_token.value);
+    curlx_safefree(gss_recv_token.value);
     gss_recv_token.length = 0;
   }
 
@@ -526,7 +539,7 @@
   return CURLE_OK;
 }
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
 
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index cc0e043..385312a 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -159,7 +159,7 @@
                                              sspi_ret_flagsp, NULL);
 
     curlx_free(sname);
-    Curl_safefree(sspi_recv_token.pvBuffer);
+    curlx_safefree(sspi_recv_token.pvBuffer);
     sspi_recv_token.cbBuffer = 0;
 
     if(check_sspi_err(data, status, "InitializeSecurityContext")) {
@@ -196,7 +196,7 @@
       socks5_free_token(&sspi_send_token, CURLE_OK);
     sspi_send_token.cbBuffer = 0;
 
-    Curl_safefree(sspi_recv_token.pvBuffer);
+    curlx_safefree(sspi_recv_token.pvBuffer);
     sspi_recv_token.cbBuffer = 0;
 
     if(status != SEC_I_CONTINUE_NEEDED)
@@ -223,6 +223,11 @@
     memcpy(&us_length, socksreq + 2, sizeof(short));
     us_length = ntohs(us_length);
 
+    if(!us_length) {
+      failf(data, "Invalid zero-length SSPI authentication token.");
+      return CURLE_COULDNT_CONNECT;
+    }
+
     sspi_recv_token.cbBuffer = us_length;
     sspi_recv_token.pvBuffer = curlx_malloc(us_length);
 
@@ -247,9 +252,9 @@
 static CURLcode socks5_free(SecBuffer *sspi_w_token,
                             CURLcode result)
 {
-  Curl_safefree(sspi_w_token[0].pvBuffer);
-  Curl_safefree(sspi_w_token[1].pvBuffer);
-  Curl_safefree(sspi_w_token[2].pvBuffer);
+  curlx_safefree(sspi_w_token[0].pvBuffer);
+  curlx_safefree(sspi_w_token[1].pvBuffer);
+  curlx_safefree(sspi_w_token[2].pvBuffer);
   return result;
 }
 
@@ -400,6 +405,11 @@
   memcpy(&us_length, socksreq + 2, sizeof(short));
   us_length = ntohs(us_length);
 
+  if(!us_length) {
+    failf(data, "Invalid zero-length SSPI encryption token.");
+    return CURLE_COULDNT_CONNECT;
+  }
+
   sspi_w_token[0].cbBuffer = us_length;
   sspi_w_token[0].pvBuffer = curlx_malloc(us_length);
   if(!sspi_w_token[0].pvBuffer)
@@ -488,7 +498,7 @@
   if(result)
     goto error;
 
-  Curl_safefree(service_name);
+  curlx_safefree(service_name);
 
   status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle,
                                                    SECPKG_CRED_ATTR_NAMES,
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 5bae99e..c5ce9c2 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -1266,9 +1266,9 @@
 
 #ifdef USE_WINSOCK
   /* We want to wait for both stdin and the socket. Since
-  ** the select() function in Winsock only works on sockets
-  ** we have to use the WaitForMultipleObjects() call.
-  */
+   * the select() function in Winsock only works on sockets
+   * we have to use the WaitForMultipleObjects() call.
+   */
 
   /* First, create a sockets event object */
   event_handle = WSACreateEvent();
@@ -1569,7 +1569,7 @@
 /*
  * TELNET protocol handler.
  */
-static const struct Curl_protocol Curl_protocol_telnet = {
+const struct Curl_protocol Curl_protocol_telnet = {
   ZERO_NULL,                            /* setup_connection */
   telnet_do,                            /* do_it */
   telnet_done,                          /* done */
@@ -1584,25 +1584,9 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
 #endif /* !CURL_DISABLE_TELNET */
-
-/*
- * TELNET protocol handler.
- */
-const struct Curl_scheme Curl_scheme_telnet = {
-  "telnet",                             /* scheme */
-#ifdef CURL_DISABLE_TELNET
-  ZERO_NULL,
-#else
-  &Curl_protocol_telnet,
-#endif
-  CURLPROTO_TELNET,                     /* protocol */
-  CURLPROTO_TELNET,                     /* family */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY,    /* flags */
-  PORT_TELNET,                          /* defport */
-};
diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h
index c106179..3848fff 100644
--- a/Utilities/cmcurl/lib/telnet.h
+++ b/Utilities/cmcurl/lib/telnet.h
@@ -23,6 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_telnet;
+#ifndef CURL_DISABLE_TELNET
+extern const struct Curl_protocol Curl_protocol_telnet;
+#endif
 
 #endif /* HEADER_CURL_TELNET_H */
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index cc0769b..6cc672d 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -175,7 +175,7 @@
   /* Average reposting an ACK after 5 seconds */
   state->retry_max = (int)timeout / 5;
 
-  /* But bound the total number */
+  /* Bound the total number */
   if(state->retry_max < 3)
     state->retry_max = 3;
 
@@ -300,8 +300,8 @@
         return CURLE_TFTP_ILLEGAL;
       }
 
-      state->blksize = (int)blksize;
-      infof(data, "blksize parsed from OACK (%d) requested (%d)",
+      state->blksize = (unsigned int)blksize;
+      infof(data, "blksize parsed from OACK (%u) requested (%u)",
             state->blksize, state->requested_blksize);
     }
     else if(checkprefix(TFTP_OPTION_TSIZE, option)) {
@@ -370,9 +370,9 @@
       int rblock = getrpacketblock(&state->rpacket);
 
       if(rblock != state->block &&
-         /* There is a bug in tftpd-hpa that causes it to send us an ack for
-          * 65535 when the block number wraps to 0. So when we are expecting
-          * 0, also accept 65535. See
+         /* There is a bug in tftpd-hpa that causes it to send us an ACK for
+          * 65535 when the block number wraps to 0. To handle it, when we are
+          * expecting 0, also accept 65535. See
           * https://www.syslinux.org/archives/2010-September/015612.html
           * */
          !(state->block == 0 && rblock == 65535)) {
@@ -418,7 +418,7 @@
       return CURLE_OK;
     }
 
-    /* TFTP considers data block size < 512 bytes as an end of session. So
+    /* TFTP considers data block size < 512 bytes as an end of session, so
      * in some cases we must wait for additional data to build full (512 bytes)
      * data block.
      * */
@@ -717,7 +717,7 @@
         result = tftp_option_add(state, &sbytes, sbytes, buf);
 
       /* add blksize option */
-      curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize);
+      curl_msnprintf(buf, sizeof(buf), "%u", state->requested_blksize);
       if(result == CURLE_OK)
         result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE);
       if(result == CURLE_OK)
@@ -883,8 +883,8 @@
   struct tftp_conn *state = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(state->rpacket.data);
-  Curl_safefree(state->spacket.data);
+  curlx_safefree(state->rpacket.data);
+  curlx_safefree(state->spacket.data);
   curlx_free(state);
 }
 
@@ -1332,7 +1332,7 @@
 /*
  * TFTP protocol handler.
  */
-static const struct Curl_protocol Curl_protocol_tftp = {
+const struct Curl_protocol Curl_protocol_tftp = {
   tftp_setup_connection,                /* setup_connection */
   tftp_do,                              /* do_it */
   tftp_done,                            /* done */
@@ -1347,25 +1347,9 @@
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
 
 #endif
-
-/*
- * TFTP protocol handler.
- */
-const struct Curl_scheme Curl_scheme_tftp = {
-  "tftp",                               /* scheme */
-#ifdef CURL_DISABLE_TFTP
-  ZERO_NULL,
-#else
-  &Curl_protocol_tftp,
-#endif
-  CURLPROTO_TFTP,                       /* protocol */
-  CURLPROTO_TFTP,                       /* family */
-  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */
-  PORT_TFTP,                            /* defport */
-};
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h
index 91ecd29..3231010 100644
--- a/Utilities/cmcurl/lib/tftp.h
+++ b/Utilities/cmcurl/lib/tftp.h
@@ -23,7 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-extern const struct Curl_scheme Curl_scheme_tftp;
+#ifndef CURL_DISABLE_TFTP
+extern const struct Curl_protocol Curl_protocol_tftp;
+#endif
 
 #define TFTP_BLKSIZE_MIN 8
 #define TFTP_BLKSIZE_MAX 65464
diff --git a/Utilities/cmcurl/lib/thrdpool.c b/Utilities/cmcurl/lib/thrdpool.c
new file mode 100644
index 0000000..22faa39
--- /dev/null
+++ b/Utilities/cmcurl/lib/thrdpool.c
@@ -0,0 +1,481 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#ifdef USE_THREADS
+
+#include "llist.h"
+#include "curl_threads.h"
+#include "curlx/timeval.h"
+#include "thrdpool.h"
+#ifdef CURLVERBOSE
+#include "curl_trc.h"
+#include "urldata.h"
+#endif
+
+
+struct thrdslot {
+  struct Curl_llist_node node;
+  struct curl_thrdpool *tpool;
+  curl_thread_t thread;
+  curl_cond_t await;
+  struct curltime starttime;
+  const char *work_description;
+  timediff_t work_timeout_ms;
+  uint32_t id;
+  BIT(running);
+  BIT(idle);
+};
+
+struct curl_thrdpool {
+  char *name;
+  uint64_t refcount;
+  curl_mutex_t lock;
+  curl_cond_t await;
+  struct Curl_llist slots;
+  struct Curl_llist zombies;
+  Curl_thrdpool_take_item_cb *fn_take;
+  Curl_thrdpool_process_item_cb *fn_process;
+  Curl_thrdpool_return_item_cb *fn_return;
+  void *fn_user_data;
+  CURLcode fatal_err;
+  uint32_t min_threads;
+  uint32_t max_threads;
+  uint32_t idle_time_ms;
+  uint32_t next_id;
+  BIT(aborted);
+  BIT(detached);
+};
+
+static void thrdpool_join_zombies(struct curl_thrdpool *tpool);
+static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked);
+
+static void thrdslot_destroy(struct thrdslot *tslot)
+{
+  DEBUGASSERT(tslot->thread == curl_thread_t_null);
+  DEBUGASSERT(!tslot->running);
+  Curl_cond_destroy(&tslot->await);
+  curlx_free(tslot);
+}
+
+static void thrdslot_done(struct thrdslot *tslot)
+{
+  struct curl_thrdpool *tpool = tslot->tpool;
+
+  DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots);
+  Curl_node_remove(&tslot->node);
+  tslot->running = FALSE;
+  Curl_llist_append(&tpool->zombies, tslot, &tslot->node);
+  Curl_cond_signal(&tpool->await);
+}
+
+static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg)
+{
+  struct thrdslot *tslot = arg;
+  struct curl_thrdpool *tpool = tslot->tpool;
+  void *item;
+
+  Curl_mutex_acquire(&tpool->lock);
+  DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots);
+  for(;;) {
+    while(!tpool->aborted) {
+      tslot->work_description = NULL;
+      tslot->work_timeout_ms = 0;
+      item = tpool->fn_take(tpool->fn_user_data, &tslot->work_description,
+                            &tslot->work_timeout_ms);
+      if(!item)
+        break;
+      tslot->starttime = curlx_now();
+      tslot->idle = FALSE;
+      Curl_mutex_release(&tpool->lock);
+
+      tpool->fn_process(item);
+
+      Curl_mutex_acquire(&tpool->lock);
+      tslot->work_description = NULL;
+      tpool->fn_return(item, tpool->aborted ? NULL : tpool->fn_user_data);
+    }
+
+    if(tpool->aborted ||
+       (Curl_llist_count(&tpool->slots) > tpool->max_threads))
+      goto out;
+
+    tslot->idle = TRUE;
+    tslot->starttime = curlx_now();
+    thrdpool_join_zombies(tpool);
+    Curl_cond_signal(&tpool->await);
+    /* Only wait with idle timeout when we are above the minimum
+     * number of threads. Otherwise short idle timeouts will keep
+     * on activating threads that have no means to shut down. */
+    if((tpool->idle_time_ms > 0) &&
+       (Curl_llist_count(&tpool->slots) > tpool->min_threads)) {
+      CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock,
+                                       tpool->idle_time_ms);
+      if((r == CURLE_OPERATION_TIMEDOUT) &&
+         (Curl_llist_count(&tpool->slots) > tpool->min_threads)) {
+        goto out;
+      }
+    }
+    else {
+      Curl_cond_wait(&tslot->await, &tpool->lock);
+    }
+  }
+
+out:
+  thrdslot_done(tslot);
+  if(!thrdpool_unlink(tslot->tpool, TRUE)) {
+    /* tpool not destroyed */
+    Curl_mutex_release(&tpool->lock);
+  }
+  return 0;
+}
+
+static CURLcode thrdslot_start(struct curl_thrdpool *tpool)
+{
+  struct thrdslot *tslot;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+
+  tslot = curlx_calloc(1, sizeof(*tslot));
+  if(!tslot)
+    goto out;
+  tslot->id = tpool->next_id++;
+  tslot->tpool = tpool;
+  tslot->thread = curl_thread_t_null;
+  Curl_cond_init(&tslot->await);
+
+  tpool->refcount++;
+  tslot->running = TRUE;
+  tslot->thread = Curl_thread_create(thrdslot_run, tslot);
+  if(tslot->thread == curl_thread_t_null) { /* never started */
+    tslot->running = FALSE;
+    thrdpool_unlink(tpool, TRUE);
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  Curl_llist_append(&tpool->slots, tslot, &tslot->node);
+  tslot = NULL;
+  result = CURLE_OK;
+
+out:
+  if(tslot)
+    thrdslot_destroy(tslot);
+  return result;
+}
+
+static void thrdpool_wake_all(struct curl_thrdpool *tpool)
+{
+  struct Curl_llist_node *e;
+  for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) {
+    struct thrdslot *tslot = Curl_node_elem(e);
+    Curl_cond_signal(&tslot->await);
+  }
+}
+
+static void thrdpool_join_zombies(struct curl_thrdpool *tpool)
+{
+  struct Curl_llist_node *e;
+
+  for(e = Curl_llist_head(&tpool->zombies); e;
+      e = Curl_llist_head(&tpool->zombies)) {
+    struct thrdslot *tslot = Curl_node_elem(e);
+
+    Curl_node_remove(&tslot->node);
+    if(tslot->thread != curl_thread_t_null) {
+      Curl_mutex_release(&tpool->lock);
+      Curl_thread_join(&tslot->thread);
+      Curl_mutex_acquire(&tpool->lock);
+      tslot->thread = curl_thread_t_null;
+    }
+    thrdslot_destroy(tslot);
+  }
+}
+
+static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked)
+{
+  DEBUGASSERT(tpool->refcount);
+  if(tpool->refcount)
+    tpool->refcount--;
+  if(tpool->refcount)
+    return FALSE;
+
+  /* no more references, free */
+  DEBUGASSERT(tpool->aborted);
+  thrdpool_join_zombies(tpool);
+  if(locked)
+    Curl_mutex_release(&tpool->lock);
+  curlx_free(tpool->name);
+  Curl_cond_destroy(&tpool->await);
+  Curl_mutex_destroy(&tpool->lock);
+  curlx_free(tpool);
+  return TRUE;
+}
+
+static CURLcode thrdpool_signal(struct curl_thrdpool *tpool,
+                                uint32_t nthreads)
+{
+  struct Curl_llist_node *e, *n;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(!tpool->aborted);
+  thrdpool_join_zombies(tpool);
+
+  for(e = Curl_llist_head(&tpool->slots); e && nthreads; e = n) {
+    struct thrdslot *tslot = Curl_node_elem(e);
+    n = Curl_node_next(e);
+    if(tslot->idle) {
+      Curl_cond_signal(&tslot->await);
+      --nthreads;
+    }
+    else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) {
+      /* starting thread, queries for work soon. */
+      --nthreads;
+    }
+  }
+
+  while(nthreads && !result &&
+        Curl_llist_count(&tpool->slots) < tpool->max_threads) {
+    result = thrdslot_start(tpool);
+    if(result)
+      break;
+    --nthreads;
+  }
+
+  return result;
+}
+
+CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool,
+                                 uint32_t min_threads,
+                                 uint32_t max_threads,
+                                 uint32_t idle_time_ms)
+{
+  CURLcode result = CURLE_OK;
+  size_t running;
+
+  if(!max_threads || (min_threads > max_threads))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  Curl_mutex_acquire(&tpool->lock);
+  tpool->min_threads = min_threads;
+  tpool->max_threads = max_threads;
+  tpool->idle_time_ms = idle_time_ms;
+  running = Curl_llist_count(&tpool->slots);
+  if(tpool->min_threads > running) {
+    result = thrdpool_signal(tpool, tpool->min_threads - (uint32_t)running);
+  }
+  Curl_mutex_release(&tpool->lock);
+
+  return result;
+}
+
+CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool,
+                              const char *name,
+                              uint32_t min_threads,
+                              uint32_t max_threads,
+                              uint32_t idle_time_ms,
+                              Curl_thrdpool_take_item_cb *fn_take,
+                              Curl_thrdpool_process_item_cb *fn_process,
+                              Curl_thrdpool_return_item_cb *fn_return,
+                              void *user_data)
+{
+  struct curl_thrdpool *tpool;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+
+  tpool = curlx_calloc(1, sizeof(*tpool));
+  if(!tpool)
+    goto out;
+  tpool->refcount = 1;
+
+  Curl_mutex_init(&tpool->lock);
+  Curl_cond_init(&tpool->await);
+  Curl_llist_init(&tpool->slots, NULL);
+  Curl_llist_init(&tpool->zombies, NULL);
+  tpool->fn_take = fn_take;
+  tpool->fn_process = fn_process;
+  tpool->fn_return = fn_return;
+  tpool->fn_user_data = user_data;
+
+  tpool->name = curlx_strdup(name);
+  if(!tpool->name)
+    goto out;
+
+  result = Curl_thrdpool_set_props(tpool, min_threads, max_threads,
+                                   idle_time_ms);
+
+out:
+  if(result && tpool) {
+    tpool->aborted = TRUE;
+    thrdpool_unlink(tpool, FALSE);
+    tpool = NULL;
+  }
+  *ptpool = tpool;
+  return result;
+}
+
+void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join)
+{
+  Curl_mutex_acquire(&tpool->lock);
+
+  tpool->aborted = TRUE;
+
+  while(join && Curl_llist_count(&tpool->slots)) {
+    thrdpool_wake_all(tpool);
+    Curl_cond_wait(&tpool->await, &tpool->lock);
+  }
+
+  thrdpool_join_zombies(tpool);
+
+  /* detach all still running threads */
+  if(Curl_llist_count(&tpool->slots)) {
+    struct Curl_llist_node *e;
+    for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) {
+      struct thrdslot *tslot = Curl_node_elem(e);
+      if(tslot->thread != curl_thread_t_null)
+        Curl_thread_destroy(&tslot->thread);
+    }
+    tpool->detached = TRUE;
+  }
+
+  if(!thrdpool_unlink(tpool, TRUE)) {
+    /* tpool not destroyed */
+    Curl_mutex_release(&tpool->lock);
+  }
+}
+
+CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads)
+{
+  CURLcode result;
+
+  Curl_mutex_acquire(&tpool->lock);
+  result = thrdpool_signal(tpool, nthreads);
+  Curl_mutex_release(&tpool->lock);
+  return result;
+}
+
+static bool thrdpool_all_idle(struct curl_thrdpool *tpool)
+{
+  struct Curl_llist_node *e;
+  for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) {
+    struct thrdslot *tslot = Curl_node_elem(e);
+    if(!tslot->idle)
+      return FALSE;
+  }
+  return TRUE;
+}
+
+CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool,
+                                  uint32_t timeout_ms)
+{
+  CURLcode result = CURLE_OK;
+  struct curltime end = { 0 };
+
+  Curl_mutex_acquire(&tpool->lock);
+  DEBUGASSERT(!tpool->aborted);
+  if(tpool->aborted) {
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  while(!thrdpool_all_idle(tpool)) {
+    if(timeout_ms) {
+      timediff_t remain_ms;
+      CURLcode r;
+
+      if(!end.tv_sec && !end.tv_usec) {
+        end = curlx_now();
+        end.tv_sec += (time_t)(timeout_ms / 1000);
+        end.tv_usec += (int)(timeout_ms % 1000) * 1000;
+        if(end.tv_usec >= 1000000) {
+          end.tv_sec++;
+          end.tv_usec -= 1000000;
+        }
+      }
+      remain_ms = curlx_timediff_ms(curlx_now(), end);
+      if(remain_ms <= 0)
+        r = CURLE_OPERATION_TIMEDOUT;
+      else
+        r = Curl_cond_timedwait(&tpool->await, &tpool->lock,
+                                (uint32_t)remain_ms);
+      if(r == CURLE_OPERATION_TIMEDOUT) {
+        result = r;
+        break;
+      }
+    }
+    else {
+      Curl_cond_wait(&tpool->await, &tpool->lock);
+    }
+  }
+
+out:
+  thrdpool_join_zombies(tpool);
+  Curl_mutex_release(&tpool->lock);
+  return result;
+}
+
+#ifdef CURLVERBOSE
+void Curl_thrdpool_trace(struct curl_thrdpool *tpool,
+                         struct Curl_easy *data)
+{
+  struct curl_trc_feat *feat = &Curl_trc_feat_threads;
+  if(Curl_trc_ft_is_verbose(data, feat)) {
+    struct Curl_llist_node *e;
+    struct curltime now = curlx_now();
+
+    Curl_mutex_acquire(&tpool->lock);
+    if(!Curl_llist_count(&tpool->slots)) {
+      Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running",
+                          tpool->name);
+    }
+    for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) {
+      struct thrdslot *tslot = Curl_node_elem(e);
+      timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime);
+      if(!tslot->running) {
+        Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running",
+                            tpool->name, tslot->id);
+      }
+      else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) {
+        Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...",
+                            tpool->name, tslot->id);
+      }
+      else if(tslot->idle) {
+        Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %"
+                            FMT_TIMEDIFF_T "ms",
+                            tpool->name, tslot->id, elapsed_ms);
+      }
+      else {
+        timediff_t remain_ms = tslot->work_timeout_ms ?
+          (tslot->work_timeout_ms - elapsed_ms) : 0;
+        Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %"
+                            FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T
+                            "ms: %s",
+                            tpool->name, tslot->id, elapsed_ms, remain_ms,
+                            tslot->work_description);
+      }
+    }
+    Curl_mutex_release(&tpool->lock);
+  }
+}
+#endif
+
+#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdpool.h b/Utilities/cmcurl/lib/thrdpool.h
new file mode 100644
index 0000000..cc81fb6
--- /dev/null
+++ b/Utilities/cmcurl/lib/thrdpool.h
@@ -0,0 +1,106 @@
+#ifndef HEADER_CURL_THRDPOOL_H
+#define HEADER_CURL_THRDPOOL_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 "curlx/timediff.h"
+
+#ifdef USE_THREADS
+
+struct curl_thrdpool;
+struct Curl_easy;
+
+/* Invoked under thread pool lock to get an "item" to work on. Must
+ * return NULL if there is nothing to do.
+ * Caller might return a descriptive string about the "item", where
+ * available. The string needs to have the same lifetime as the
+ * item itself. */
+typedef void *Curl_thrdpool_take_item_cb(void *user_data,
+                                         const char **pdescription,
+                                         timediff_t *ptimeout_ms);
+
+/* Invoked outside thread pool lock to process the item taken. */
+typedef void Curl_thrdpool_process_item_cb(void *item);
+
+/* Invoked under thread pool lock to return a processed item back
+ * to the producer.
+ * If the thread pool has been destroyed, `user_data` will be NULL
+ * and the callback is responsible to release all `item` resources. */
+typedef void Curl_thrdpool_return_item_cb(void *item, void *user_data);
+
+/* Create a new thread pool.
+ * @param name         name of pool for tracing purposes
+ * @param min_threads  minimum number of threads to have always running
+ * @param max_threads  maximum number of threads running, ever.
+ * @param idle_time_ms maximum time a thread should wait for tasks to
+ *                     process before shutting down (unless the pool is
+ *                     already at minimum thread count), use 0 for
+ *                     infinite wait.
+ * @param fn_take      take the next item to process
+ * @param fn_process   process the item taken
+ * @param fn_return    return the processed item
+ * @param user_data    parameter passed to take/return callbacks
+ */
+CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool,
+                              const char *name,
+                              uint32_t min_threads,
+                              uint32_t max_threads,
+                              uint32_t idle_time_ms,
+                              Curl_thrdpool_take_item_cb *fn_take,
+                              Curl_thrdpool_process_item_cb *fn_process,
+                              Curl_thrdpool_return_item_cb *fn_return,
+                              void *user_data);
+
+/* Destroy the thread pool, release its resources.
+ * With `join` being TRUE, the call will wait for all threads to finish
+ * processing before returning. On FALSE, it will detach all threads
+ * running. Ongoing item processing will continue to run and
+ * `fn_return` will be invoked with NULL user_data before the thread exits.
+ */
+void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join);
+
+/* Signal the pool to wake up `nthreads` idle worker threads, possible
+ * creating new threads up to the max limit. The number should reflect
+ * the items that can actually be taken for processing right away, e.g.
+ * the producers "queue" length of outstanding items.
+ */
+CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads);
+
+CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool,
+                                  uint32_t timeout_ms);
+
+/* Change the properties of a threadpool. */
+CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool,
+                                 uint32_t min_threads,
+                                 uint32_t max_threads,
+                                 uint32_t idle_time_ms);
+
+#ifdef CURLVERBOSE
+void Curl_thrdpool_trace(struct curl_thrdpool *tpool,
+                         struct Curl_easy *data);
+#endif
+
+#endif /* USE_THREADS */
+
+#endif /* HEADER_CURL_THRDPOOL_H */
diff --git a/Utilities/cmcurl/lib/thrdqueue.c b/Utilities/cmcurl/lib/thrdqueue.c
new file mode 100644
index 0000000..1521ccf
--- /dev/null
+++ b/Utilities/cmcurl/lib/thrdqueue.c
@@ -0,0 +1,411 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#ifdef USE_THREADS
+
+#include "llist.h"
+#include "curl_threads.h"
+#include "thrdpool.h"
+#include "thrdqueue.h"
+#include "curlx/timeval.h"
+#ifdef CURLVERBOSE
+#include "curl_trc.h"
+#include "urldata.h"
+#endif
+
+
+struct curl_thrdq {
+  char *name;
+  curl_mutex_t lock;
+  curl_cond_t await;
+  struct Curl_llist sendq;
+  struct Curl_llist recvq;
+  struct curl_thrdpool *tpool;
+  Curl_thrdq_item_free_cb *fn_free;
+  Curl_thrdq_item_process_cb *fn_process;
+  Curl_thrdq_ev_cb *fn_event;
+  void *fn_user_data;
+  uint32_t send_max_len;
+  BIT(aborted);
+};
+
+struct thrdq_item {
+  struct Curl_llist_node node;
+  Curl_thrdq_item_free_cb *fn_free;
+  Curl_thrdq_item_process_cb *fn_process;
+  void *item;
+  struct curltime start;
+  timediff_t timeout_ms;
+  const char *description;
+};
+
+static struct thrdq_item *thrdq_item_create(struct curl_thrdq *tqueue,
+                                            void *item,
+                                            const char *description,
+                                            timediff_t timeout_ms)
+{
+  struct thrdq_item *qitem;
+
+  qitem = curlx_calloc(1, sizeof(*qitem));
+  if(!qitem)
+    return NULL;
+  qitem->item = item;
+  qitem->description = description;
+  qitem->fn_free = tqueue->fn_free;
+  qitem->fn_process = tqueue->fn_process;
+  if(timeout_ms) {
+    qitem->start = curlx_now();
+    qitem->timeout_ms = timeout_ms;
+  }
+  return qitem;
+}
+
+static void thrdq_item_destroy(struct thrdq_item *qitem)
+{
+  if(qitem->item)
+    qitem->fn_free(qitem->item);
+  curlx_free(qitem);
+}
+
+static void thrdq_item_list_dtor(void *user_data, void *elem)
+{
+  (void)user_data;
+  thrdq_item_destroy(elem);
+}
+
+static void *thrdq_tpool_take(void *user_data, const char **pdescription,
+                              timediff_t *ptimeout_ms)
+{
+  struct curl_thrdq *tqueue = user_data;
+  struct thrdq_item *qitem = NULL;
+  struct Curl_llist_node *e;
+  Curl_thrdq_ev_cb *fn_event = NULL;
+  void *fn_user_data = NULL;
+
+  Curl_mutex_acquire(&tqueue->lock);
+  *pdescription = NULL;
+  *ptimeout_ms = 0;
+  if(!tqueue->aborted) {
+    e = Curl_llist_head(&tqueue->sendq);
+    if(e) {
+      struct curltime now = curlx_now();
+      timediff_t timeout_ms;
+      while(e) {
+        qitem = Curl_node_take_elem(e);
+        timeout_ms = (!qitem->timeout_ms) ? 0 :
+          (qitem->timeout_ms - curlx_ptimediff_ms(&now, &qitem->start));
+        if(timeout_ms < 0) {
+          /* timed out while queued, place on receive queue */
+          Curl_llist_append(&tqueue->recvq, qitem, &qitem->node);
+          fn_event = tqueue->fn_event;
+          fn_user_data = tqueue->fn_user_data;
+          qitem = NULL;
+          e = Curl_llist_head(&tqueue->sendq);
+          continue;
+        }
+        else {
+          *pdescription = qitem->description;
+          *ptimeout_ms = timeout_ms;
+          break;
+        }
+      }
+    }
+  }
+  Curl_mutex_release(&tqueue->lock);
+  /* avoiding deadlocks */
+  if(fn_event)
+    fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data);
+  return qitem;
+}
+
+static void thrdq_tpool_return(void *item, void *user_data)
+{
+  struct curl_thrdq *tqueue = user_data;
+  struct thrdq_item *qitem = item;
+  Curl_thrdq_ev_cb *fn_event = NULL;
+  void *fn_user_data = NULL;
+
+  if(!tqueue) {
+    thrdq_item_destroy(item);
+    return;
+  }
+
+  Curl_mutex_acquire(&tqueue->lock);
+  if(tqueue->aborted) {
+    thrdq_item_destroy(qitem);
+  }
+  else {
+    DEBUGASSERT(!Curl_node_llist(&qitem->node));
+    Curl_llist_append(&tqueue->recvq, qitem, &qitem->node);
+    fn_event = tqueue->fn_event;
+    fn_user_data = tqueue->fn_user_data;
+  }
+  Curl_mutex_release(&tqueue->lock);
+  /* avoiding deadlocks */
+  if(fn_event)
+    fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data);
+}
+
+static void thrdq_tpool_process(void *item)
+{
+  struct thrdq_item *qitem = item;
+  qitem->fn_process(qitem->item);
+}
+
+static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join)
+{
+  DEBUGASSERT(tqueue->aborted);
+  if(tqueue->tpool) {
+    if(locked)
+      Curl_mutex_release(&tqueue->lock);
+    Curl_thrdpool_destroy(tqueue->tpool, join);
+    tqueue->tpool = NULL;
+    if(locked)
+      Curl_mutex_acquire(&tqueue->lock);
+  }
+
+  Curl_llist_destroy(&tqueue->sendq, NULL);
+  Curl_llist_destroy(&tqueue->recvq, NULL);
+  curlx_free(tqueue->name);
+  Curl_cond_destroy(&tqueue->await);
+  if(locked)
+    Curl_mutex_release(&tqueue->lock);
+  Curl_mutex_destroy(&tqueue->lock);
+  curlx_free(tqueue);
+}
+
+CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue,
+                           const char *name,
+                           uint32_t max_len,
+                           uint32_t min_threads,
+                           uint32_t max_threads,
+                           uint32_t idle_time_ms,
+                           Curl_thrdq_item_free_cb *fn_free,
+                           Curl_thrdq_item_process_cb *fn_process,
+                           Curl_thrdq_ev_cb *fn_event,
+                           void *user_data)
+{
+  struct curl_thrdq *tqueue;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+
+  tqueue = curlx_calloc(1, sizeof(*tqueue));
+  if(!tqueue)
+    goto out;
+
+  Curl_mutex_init(&tqueue->lock);
+  Curl_cond_init(&tqueue->await);
+  Curl_llist_init(&tqueue->sendq, thrdq_item_list_dtor);
+  Curl_llist_init(&tqueue->recvq, thrdq_item_list_dtor);
+  tqueue->fn_free = fn_free;
+  tqueue->fn_process = fn_process;
+  tqueue->fn_event = fn_event;
+  tqueue->fn_user_data = user_data;
+  tqueue->send_max_len = max_len;
+
+  tqueue->name = curlx_strdup(name);
+  if(!tqueue->name)
+    goto out;
+
+  result = Curl_thrdpool_create(&tqueue->tpool, name,
+                                min_threads, max_threads, idle_time_ms,
+                                thrdq_tpool_take,
+                                thrdq_tpool_process,
+                                thrdq_tpool_return,
+                                tqueue);
+
+out:
+  if(result && tqueue) {
+    tqueue->aborted = TRUE;
+    thrdq_unlink(tqueue, FALSE, TRUE);
+    tqueue = NULL;
+  }
+  *ptqueue = tqueue;
+  return result;
+}
+
+void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join)
+{
+  Curl_mutex_acquire(&tqueue->lock);
+  DEBUGASSERT(!tqueue->aborted);
+  tqueue->aborted = TRUE;
+  thrdq_unlink(tqueue, TRUE, join);
+}
+
+CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item,
+                         const char *description, timediff_t timeout_ms)
+{
+  CURLcode result = CURLE_AGAIN;
+  size_t signals = 0;
+
+  Curl_mutex_acquire(&tqueue->lock);
+  if(tqueue->aborted) {
+    DEBUGASSERT(0);
+    result = CURLE_SEND_ERROR;
+    goto out;
+  }
+  if(timeout_ms < 0) {
+    result = CURLE_OPERATION_TIMEDOUT;
+    goto out;
+  }
+
+  if(!tqueue->send_max_len ||
+     (Curl_llist_count(&tqueue->sendq) < tqueue->send_max_len)) {
+    struct thrdq_item *qitem = thrdq_item_create(tqueue, item, description,
+                                                 timeout_ms);
+    if(!qitem) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
+    item = NULL;
+    Curl_llist_append(&tqueue->sendq, qitem, &qitem->node);
+    signals = Curl_llist_count(&tqueue->sendq);
+    result = CURLE_OK;
+  }
+
+out:
+  Curl_mutex_release(&tqueue->lock);
+  /* Signal thread pool unlocked to avoid deadlocks. Since we added
+   * item to the queue already, it might have been taken for processing
+   * already. Any error in signalling the pool cannot be reported to
+   * the caller since it needs to give up ownership of item. */
+  if(!result && signals)
+    (void)Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals);
+  return result;
+}
+
+CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem)
+{
+  CURLcode result = CURLE_AGAIN;
+  struct Curl_llist_node *e;
+
+  *pitem = NULL;
+  Curl_mutex_acquire(&tqueue->lock);
+  if(tqueue->aborted) {
+    DEBUGASSERT(0);
+    result = CURLE_RECV_ERROR;
+    goto out;
+  }
+
+  e = Curl_llist_head(&tqueue->recvq);
+  if(e) {
+    struct thrdq_item *qitem = Curl_node_take_elem(e);
+    *pitem = qitem->item;
+    qitem->item = NULL;
+    thrdq_item_destroy(qitem);
+    result = CURLE_OK;
+  }
+out:
+  Curl_mutex_release(&tqueue->lock);
+  return result;
+}
+
+static void thrdq_llist_clean_matches(struct Curl_llist *llist,
+                                      Curl_thrdq_item_match_cb *fn_match,
+                                      void *match_data)
+{
+  struct Curl_llist_node *e, *n;
+  struct thrdq_item *qitem;
+
+  for(e = Curl_llist_head(llist); e; e = n) {
+    n = Curl_node_next(e);
+    qitem = Curl_node_elem(e);
+    if(fn_match(qitem->item, match_data))
+      Curl_node_remove(e);
+  }
+}
+
+void Curl_thrdq_clear(struct curl_thrdq *tqueue,
+                      Curl_thrdq_item_match_cb *fn_match,
+                      void *match_data)
+{
+  Curl_mutex_acquire(&tqueue->lock);
+  if(tqueue->aborted) {
+    DEBUGASSERT(0);
+    goto out;
+  }
+  thrdq_llist_clean_matches(&tqueue->sendq, fn_match, match_data);
+  thrdq_llist_clean_matches(&tqueue->recvq, fn_match, match_data);
+out:
+  Curl_mutex_release(&tqueue->lock);
+}
+
+CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue,
+                               uint32_t timeout_ms)
+{
+  return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms);
+}
+
+CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue,
+                              uint32_t max_len,
+                              uint32_t min_threads,
+                              uint32_t max_threads,
+                              uint32_t idle_time_ms)
+{
+  CURLcode result;
+  size_t signals;
+
+  Curl_mutex_acquire(&tqueue->lock);
+  tqueue->send_max_len = max_len;
+  signals = Curl_llist_count(&tqueue->sendq);
+  Curl_mutex_release(&tqueue->lock);
+
+  result = Curl_thrdpool_set_props(tqueue->tpool, min_threads,
+                                   max_threads, idle_time_ms);
+  if(!result && signals)
+    result = Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals);
+  return result;
+}
+
+#ifdef CURLVERBOSE
+void Curl_thrdq_trace(struct curl_thrdq *tqueue,
+                      struct Curl_easy *data)
+{
+  struct curl_trc_feat *feat = &Curl_trc_feat_threads;
+  if(Curl_trc_ft_is_verbose(data, feat)) {
+    struct Curl_llist_node *e;
+    struct thrdq_item *qitem;
+
+    Curl_thrdpool_trace(tqueue->tpool, data);
+    Curl_mutex_acquire(&tqueue->lock);
+    if(!Curl_llist_count(&tqueue->sendq) &&
+       !Curl_llist_count(&tqueue->recvq)) {
+      Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name);
+    }
+    for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) {
+      qitem = Curl_node_elem(e);
+      Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s",
+                          tqueue->name, qitem->description);
+    }
+    for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) {
+      qitem = Curl_node_elem(e);
+      Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s",
+                          tqueue->name, qitem->description);
+    }
+    Curl_mutex_release(&tqueue->lock);
+  }
+}
+#endif
+
+#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdqueue.h b/Utilities/cmcurl/lib/thrdqueue.h
new file mode 100644
index 0000000..d267f5d
--- /dev/null
+++ b/Utilities/cmcurl/lib/thrdqueue.h
@@ -0,0 +1,118 @@
+#ifndef HEADER_CURL_THRDQUEUE_H
+#define HEADER_CURL_THRDQUEUE_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 "curlx/timediff.h"
+
+#ifdef USE_THREADS
+
+struct Curl_easy;
+struct curl_thrdq;
+
+typedef enum {
+  CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */
+} Curl_thrdq_event;
+
+/* Notification callback when "events" happen in the queue. May be
+ * call from any thread, queue is not locked. */
+typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue,
+                              Curl_thrdq_event ev,
+                              void *user_data);
+
+/* Process a queued item. Maybe call from any thread. Queue is
+ * not locked. */
+typedef void Curl_thrdq_item_process_cb(void *item);
+
+/* Free an item. May be called from any thread at any time for an
+ * item that is in the queue (either before or after processing). */
+typedef void Curl_thrdq_item_free_cb(void *item);
+
+/* Create a new queue processing "items" by a thread pool.
+ */
+CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue,
+                           const char *name,
+                           uint32_t max_len, /* 0 for unlimited */
+                           uint32_t min_threads,
+                           uint32_t max_threads,
+                           uint32_t idle_time_ms,
+                           Curl_thrdq_item_free_cb *fn_free,
+                           Curl_thrdq_item_process_cb *fn_process,
+                           Curl_thrdq_ev_cb *fn_event, /* optional */
+                           void *user_data);
+
+/* Destroy the queue, free all queued items unprocessed and destroy
+ * the thread pool used.
+ * @param join TRUE when thread pool shall be joined. FALSE for
+ *             detaching any running threads.
+ */
+void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join);
+
+/* Send "item" onto the queue. The caller needs to clear any reference
+ * to "item" on success, e.g. the queue takes ownership.
+ * `description` is an optional string describing the item for tracing
+ * purposes. It needs to have the same lifetime as `item`.
+ * Returns CURLE_AGAIN when the queue has already been full.
+ *
+ * With`timeout_ms` != 0, items that get stuck that long in the send
+ * queue are removed and added to the receive queue right away.
+ */
+CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item,
+                         const char *description, timediff_t timeout_ms);
+
+/* Receive the oldest, processed item from the queue again, if there is one.
+ * The caller takes ownership of the item received, e.g. the queue
+ * relinquishes all references to item.
+ * Returns CURLE_AGAIN when there is no processed item, setting `pitem`
+ * to NULL.
+ */
+CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem);
+
+/* Return TRUE if the passed "item" matches. */
+typedef bool Curl_thrdq_item_match_cb(void *item, void *match_data);
+
+/* Clear all scheduled/processed items that match from the queue. This
+ * will *not* be able to clear items that are being processed.
+ */
+void Curl_thrdq_clear(struct curl_thrdq *tqueue,
+                      Curl_thrdq_item_match_cb *fn_match,
+                      void *match_data);
+
+CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue,
+                               uint32_t timeout_ms);
+
+CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue,
+                              uint32_t max_len, /* 0 for unlimited */
+                              uint32_t min_threads,
+                              uint32_t max_threads,
+                              uint32_t idle_time_ms);
+
+#ifdef CURLVERBOSE
+void Curl_thrdq_trace(struct curl_thrdq *tqueue,
+                      struct Curl_easy *data);
+#endif
+
+#endif /* USE_THREADS */
+
+#endif /* HEADER_CURL_THRDQUEUE_H */
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index 6dd2f52..fd1a903 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -59,6 +59,7 @@
 #include "hostip.h"
 #include "cfilters.h"
 #include "cw-out.h"
+#include "dnscache.h"
 #include "transfer.h"
 #include "sendf.h"
 #include "curl_trc.h"
@@ -303,15 +304,15 @@
      * 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;
+      CURL_REQ_CLEAR_RECV(data);
     }
     /* if we stopped receiving, leave the loop */
-    if(!(k->keepon & KEEP_RECV))
+    if(!CURL_REQ_WANT_RECV(data))
       break;
 
   } while(maxloops--);
 
-  if(!is_eos && !rate_limited && CURL_WANT_RECV(data) &&
+  if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) &&
      (!rcvd_eagain || data_pending(data, rcvd_eagain))) {
     /* Did not read until EAGAIN/EOS or there is still data pending
      * in buffers. Mark as read-again via simulated SELECT results. */
@@ -319,7 +320,7 @@
     CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty");
   }
 
-  if(((k->keepon & (KEEP_RECV | KEEP_SEND)) == KEEP_SEND) &&
+  if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) &&
      (conn->bits.close || is_multiplex)) {
     /* When we have read the entire thing and the close bit is set, the server
        may now close the connection. If there is now any kind of sending going
@@ -340,9 +341,7 @@
  */
 static CURLcode sendrecv_ul(struct Curl_easy *data)
 {
-  /* We should not get here when the sending is already done. It
-   * probably means that someone set `data-req.keepon |= KEEP_SEND`
-   * when it should not. */
+  /* We should not get here when the sending is already done. */
   DEBUGASSERT(!Curl_req_done_sending(data));
 
   if(!Curl_req_done_sending(data))
@@ -366,7 +365,7 @@
 
   /* We 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) */
-  if(k->keepon & KEEP_RECV) {
+  if(CURL_REQ_WANT_RECV(data)) {
     result = sendrecv_dl(data, k);
     if(result || data->req.done)
       goto out;
@@ -383,7 +382,7 @@
   if(result)
     goto out;
 
-  if(k->keepon) {
+  if(CURL_REQ_WANT_IO(data)) {
     if(Curl_timeleft_ms(data) < 0) {
       if(k->size != -1) {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
@@ -419,7 +418,7 @@
   }
 
   /* If there is nothing more to send/recv, the request is done */
-  if((k->keepon & (KEEP_RECV | KEEP_SEND)) == 0)
+  if(!CURL_REQ_WANT_IO(data))
     data->req.done = TRUE;
 
   result = Curl_pgrsUpdate(data);
@@ -440,6 +439,40 @@
 }
 
 /*
+ * Restore the user credentials to those set in options.
+ */
+CURLcode Curl_reset_userpwd(struct Curl_easy *data)
+{
+  CURLcode result;
+  if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD])
+    data->state.creds_from = CREDS_OPTION;
+  result = Curl_setstropt(&data->state.aptr.user,
+                          data->set.str[STRING_USERNAME]);
+  if(!result)
+    result = Curl_setstropt(&data->state.aptr.passwd,
+                            data->set.str[STRING_PASSWORD]);
+  return result;
+}
+
+/*
+ * Restore the proxy credentials to those set in options.
+ */
+CURLcode Curl_reset_proxypwd(struct Curl_easy *data)
+{
+#ifndef CURL_DISABLE_PROXY
+  CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser,
+                                   data->set.str[STRING_PROXYUSERNAME]);
+  if(!result)
+    result = Curl_setstropt(&data->state.aptr.proxypasswd,
+                            data->set.str[STRING_PROXYPASSWORD]);
+  return result;
+#else
+  (void)data;
+  return CURLE_OK;
+#endif
+}
+
+/*
  * Curl_pretransfer() is called immediately before a transfer starts, and only
  * once for one transfer no matter if it has redirects or do multi-pass
  * authentication etc.
@@ -470,6 +503,8 @@
     uc = curl_url_get(data->set.uh,
                       CURLUPART_URL, &data->set.str[STRING_SET_URL], 0);
     if(uc) {
+      /* clear the pointer to not point to freed memory anymore */
+      Curl_bufref_set(&data->state.url, NULL, 0, NULL);
       failf(data, "No URL set");
       return CURLE_URL_MALFORMAT;
     }
@@ -492,6 +527,7 @@
   data->state.requests = 0;
   data->state.followlocation = 0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
+  data->state.http_ignorecustom = FALSE; /* use custom HTTP method */
   data->state.errorbuf = FALSE; /* no error has occurred */
 #ifndef CURL_DISABLE_HTTP
   Curl_http_neg_init(data, &data->state.http_neg);
@@ -499,8 +535,13 @@
   data->state.authproblem = FALSE;
   data->state.authhost.want = data->set.httpauth;
   data->state.authproxy.want = data->set.proxyauth;
-  Curl_safefree(data->info.wouldredirect);
+  curlx_safefree(data->info.wouldredirect);
   Curl_data_priority_clear_state(data);
+  if(data->set.http_auto_referer)
+    Curl_bufref_free(&data->state.referer);
+  if(data->set.str[STRING_SET_REFERER])
+    Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER],
+                    0, NULL);
 
   if(data->state.httpreq == HTTPREQ_PUT)
     data->state.infilesize = data->set.filesize;
@@ -563,8 +604,8 @@
       if(wc->state < CURLWC_INIT) {
         if(wc->ftpwc)
           wc->dtor(wc->ftpwc);
-        Curl_safefree(wc->pattern);
-        Curl_safefree(wc->path);
+        curlx_safefree(wc->pattern);
+        curlx_safefree(wc->path);
         Curl_wildcard_init(wc); /* init wildcard structures */
       }
     }
@@ -584,23 +625,10 @@
       return CURLE_OUT_OF_MEMORY;
   }
 
-  if(data->set.str[STRING_USERNAME] ||
-     data->set.str[STRING_PASSWORD])
-    data->state.creds_from = CREDS_OPTION;
   if(!result)
-    result = Curl_setstropt(&data->state.aptr.user,
-                            data->set.str[STRING_USERNAME]);
+    result = Curl_reset_userpwd(data);
   if(!result)
-    result = Curl_setstropt(&data->state.aptr.passwd,
-                            data->set.str[STRING_PASSWORD]);
-#ifndef CURL_DISABLE_PROXY
-  if(!result)
-    result = Curl_setstropt(&data->state.aptr.proxyuser,
-                            data->set.str[STRING_PROXYUSERNAME]);
-  if(!result)
-    result = Curl_setstropt(&data->state.aptr.proxypasswd,
-                            data->set.str[STRING_PROXYPASSWORD]);
-#endif
+    result = Curl_reset_proxypwd(data);
 
   data->req.headerbytecount = 0;
   Curl_headers_cleanup(data);
@@ -710,14 +738,11 @@
 
   /* we want header and/or body, if neither then do not do this! */
   if(conn->scheme->run->write_resp_hd || !data->req.no_body) {
-
     if(conn->recv_idx != -1)
-      k->keepon |= KEEP_RECV;
-
+      CURL_REQ_SET_RECV(data);
     if(conn->send_idx != -1)
-      k->keepon |= KEEP_SEND;
+      CURL_REQ_SET_SEND(data);
   }
-
   CURL_TRC_M(data, "xfer_setup: recv_idx=%d, send_idx=%d",
              conn->recv_idx, conn->send_idx);
 }
@@ -799,7 +824,7 @@
                                  const char *hd0, size_t hdlen, bool is_eos)
 {
   if(data->conn->scheme->run->write_resp_hd) {
-    DEBUGASSERT(!hd0[hdlen]); /* null terminated */
+    DEBUGASSERT(!hd0[hdlen]); /* null-terminated */
     /* protocol handlers offering this function take full responsibility
      * for writing all received download data to the client. */
     return data->conn->scheme->run->write_resp_hd(data, hd0, hdlen, is_eos);
@@ -868,8 +893,8 @@
 
 bool Curl_xfer_is_blocked(struct Curl_easy *data)
 {
-  bool want_send = (data->req.keepon & KEEP_SEND);
-  bool want_recv = (data->req.keepon & KEEP_RECV);
+  bool want_send = CURL_REQ_WANT_SEND(data);
+  bool want_recv = CURL_REQ_WANT_RECV(data);
   if(!want_send)
     return want_recv && Curl_xfer_recv_is_paused(data);
   else if(!want_recv)
@@ -908,3 +933,25 @@
   Curl_pgrsRecvPause(data, enable);
   return result;
 }
+
+bool Curl_xfer_is_secure(struct Curl_easy *data)
+{
+  const struct Curl_scheme *scheme = NULL;
+
+  if(data->conn) {
+    scheme = data->conn->scheme;
+    /* if we are connected, but not use SSL, the transfer is not secure.
+     * This covers an insecure http:// proxy that is not tunneling.
+     * We enforce tunneling for such cases, but better be sure here. */
+    if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) &&
+       !Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
+      return FALSE;
+  }
+  else if(data->info.conn_scheme) { /* was connected once */
+    scheme = Curl_get_scheme(data->info.conn_scheme);
+  }
+  else { /* never connected (yet?) */
+    DEBUGASSERT(0); /* not implemented, would need to parse URL */
+  }
+  return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE;
+}
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h
index 05a5f89..b29e70b 100644
--- a/Utilities/cmcurl/lib/transfer.h
+++ b/Utilities/cmcurl/lib/transfer.h
@@ -31,6 +31,8 @@
 
 void Curl_init_CONNECT(struct Curl_easy *data);
 
+CURLcode Curl_reset_userpwd(struct Curl_easy *data);
+CURLcode Curl_reset_proxypwd(struct Curl_easy *data);
 CURLcode Curl_pretransfer(struct Curl_easy *data);
 
 CURLcode Curl_sendrecv(struct Curl_easy *data);
@@ -39,8 +41,8 @@
 
 /**
  * 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
+ * Handle all passed bytes or return an error. By default, this writes
+ * 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
@@ -112,7 +114,7 @@
 /**
  * Send data on the socket/connection filter designated
  * for transfer's outgoing data.
- * Will return CURLE_OK on blocking with (*pnwritten == 0).
+ * Return CURLE_OK on blocking with (*pnwritten == 0).
  */
 CURLcode Curl_xfer_send(struct Curl_easy *data,
                         const void *buf, size_t blen, bool eos,
@@ -121,7 +123,7 @@
 /**
  * Receive data on the socket/connection filter designated
  * for transfer's incoming data.
- * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0).
+ * Return CURLE_AGAIN on blocking with (*pnrcvd == 0).
  */
 CURLcode Curl_xfer_recv(struct Curl_easy *data,
                         char *buf, size_t blen,
@@ -143,4 +145,8 @@
 CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable);
 CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable);
 
+/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the
+ * URL's host. */
+bool Curl_xfer_is_secure(struct Curl_easy *data);
+
 #endif /* HEADER_CURL_TRANSFER_H */
diff --git a/Utilities/cmcurl/lib/uint-bset.c b/Utilities/cmcurl/lib/uint-bset.c
index 5efdbea..5469174 100644
--- a/Utilities/cmcurl/lib/uint-bset.c
+++ b/Utilities/cmcurl/lib/uint-bset.c
@@ -68,7 +68,9 @@
 }
 
 #ifdef UNITTESTS
-UNITTEST uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset)
+/* @unittest 3211 */
+UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset);
+UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset)
 {
   return bset->nslots * 64;
 }
diff --git a/Utilities/cmcurl/lib/uint-bset.h b/Utilities/cmcurl/lib/uint-bset.h
index 7455c42..ce0450c 100644
--- a/Utilities/cmcurl/lib/uint-bset.h
+++ b/Utilities/cmcurl/lib/uint-bset.h
@@ -56,9 +56,6 @@
 /* Destroy the bitset, freeing all resources. */
 void Curl_uint32_bset_destroy(struct uint32_bset *bset);
 
-/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */
-uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset);
-
 /* Get the cardinality of the bitset, e.g. numbers present in the set. */
 uint32_t Curl_uint32_bset_count(struct uint32_bset *bset);
 
diff --git a/Utilities/cmcurl/lib/uint-hash.c b/Utilities/cmcurl/lib/uint-hash.c
index 2377840..c37448f 100644
--- a/Utilities/cmcurl/lib/uint-hash.c
+++ b/Utilities/cmcurl/lib/uint-hash.c
@@ -181,7 +181,9 @@
   return NULL;
 }
 
-static void uint_hash_clear(struct uint_hash *h)
+/* @unittest 1616 */
+UNITTEST void uint_hash_clear(struct uint_hash *h);
+UNITTEST void uint_hash_clear(struct uint_hash *h)
 {
   if(h && h->table) {
     struct uint_hash_entry *he, **he_anchor;
@@ -198,19 +200,12 @@
   }
 }
 
-#ifdef UNITTESTS
-UNITTEST void Curl_uint32_hash_clear(struct uint_hash *h)
-{
-  uint_hash_clear(h);
-}
-#endif
-
 void Curl_uint32_hash_destroy(struct uint_hash *h)
 {
   DEBUGASSERT(h->init == CURL_UINT32_HASHINIT);
   if(h->table) {
     uint_hash_clear(h);
-    Curl_safefree(h->table);
+    curlx_safefree(h->table);
   }
   DEBUGASSERT(h->size == 0);
   h->slots = 0;
diff --git a/Utilities/cmcurl/lib/uint-hash.h b/Utilities/cmcurl/lib/uint-hash.h
index 56d7104..3cbcf8f 100644
--- a/Utilities/cmcurl/lib/uint-hash.h
+++ b/Utilities/cmcurl/lib/uint-hash.h
@@ -44,8 +44,6 @@
                            uint32_t slots,
                            Curl_uint32_hash_dtor *dtor);
 void Curl_uint32_hash_destroy(struct uint_hash *h);
-void Curl_uint32_hash_clear(struct uint_hash *h);
-
 bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value);
 bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id);
 void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id);
diff --git a/Utilities/cmcurl/lib/uint-spbset.c b/Utilities/cmcurl/lib/uint-spbset.c
index 58dcbda..3daa2ee 100644
--- a/Utilities/cmcurl/lib/uint-spbset.c
+++ b/Utilities/cmcurl/lib/uint-spbset.c
@@ -30,9 +30,6 @@
 #define CURL_UINT32_SPBSET_MAGIC  0x70737362
 #endif
 
-/* Clear the bitset, making it empty. */
-UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset);
-
 void Curl_uint32_spbset_init(struct uint32_spbset *bset)
 {
   memset(bset, 0, sizeof(*bset));
@@ -41,10 +38,26 @@
 #endif
 }
 
+/* Clear the bitset, making it empty.
+
+   @unittest 3213
+ */
+UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset);
+UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset)
+{
+  struct uint32_spbset_chunk *next, *chunk;
+
+  for(chunk = bset->head.next; chunk; chunk = next) {
+    next = chunk->next;
+    curlx_free(chunk);
+  }
+  memset(&bset->head, 0, sizeof(bset->head));
+}
+
 void Curl_uint32_spbset_destroy(struct uint32_spbset *bset)
 {
   DEBUGASSERT(bset->init == CURL_UINT32_SPBSET_MAGIC);
-  Curl_uint32_spbset_clear(bset);
+  uint32_spbset_clear(bset);
 }
 
 uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset)
@@ -61,19 +74,8 @@
   return n;
 }
 
-UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset)
-{
-  struct uint32_spbset_chunk *next, *chunk;
-
-  for(chunk = bset->head.next; chunk; chunk = next) {
-    next = chunk->next;
-    curlx_free(chunk);
-  }
-  memset(&bset->head, 0, sizeof(bset->head));
-}
-
-static struct uint32_spbset_chunk *
-uint32_spbset_get_chunk(struct uint32_spbset *bset, uint32_t i, bool grow)
+static struct uint32_spbset_chunk *uint32_spbset_get_chunk(
+  struct uint32_spbset *bset, uint32_t i, bool grow)
 {
   struct uint32_spbset_chunk *chunk, **panchor = NULL;
   uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK);
diff --git a/Utilities/cmcurl/lib/uint-table.c b/Utilities/cmcurl/lib/uint-table.c
index b9c1990..addd2c9 100644
--- a/Utilities/cmcurl/lib/uint-table.c
+++ b/Utilities/cmcurl/lib/uint-table.c
@@ -29,9 +29,6 @@
 #define CURL_UINT32_TBL_MAGIC  0x62757473
 #endif
 
-/* Clear the table, making it empty. */
-UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl);
-
 void Curl_uint32_tbl_init(struct uint32_tbl *tbl,
                           Curl_uint32_tbl_entry_dtor *entry_dtor)
 {
@@ -82,15 +79,12 @@
   return CURLE_OK;
 }
 
-void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl)
-{
-  DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC);
-  Curl_uint32_tbl_clear(tbl);
-  curlx_free(tbl->rows);
-  memset(tbl, 0, sizeof(*tbl));
-}
+/* Clear the table, making it empty.
 
-UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl)
+   @unittest 3212
+ */
+UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl);
+UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl)
 {
   DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC);
   uint32_tbl_clear_rows(tbl, 0, tbl->nrows);
@@ -98,6 +92,14 @@
   tbl->last_key_added = UINT32_MAX;
 }
 
+void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl)
+{
+  DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC);
+  uint32_tbl_clear(tbl);
+  curlx_free(tbl->rows);
+  memset(tbl, 0, sizeof(*tbl));
+}
+
 uint32_t Curl_uint32_tbl_capacity(struct uint32_tbl *tbl)
 {
   return tbl->nrows;
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index ec0457b..5fe6803 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -100,7 +100,7 @@
 #include "curlx/strerr.h"
 #include "curlx/strparse.h"
 
-/* And now for the protocols */
+/* Now for the protocols */
 #include "ftp.h"
 #include "dict.h"
 #include "telnet.h"
@@ -112,7 +112,6 @@
 #include "imap.h"
 #include "url.h"
 #include "connect.h"
-#include "curl_rtmp.h"
 #include "gopher.h"
 #include "mqtt.h"
 #include "rtsp.h"
@@ -133,10 +132,6 @@
 # error READBUFFER_SIZE is too small
 #endif
 
-#if !defined(CURL_DISABLE_PROXY) && defined(USE_UNIX_SOCKETS)
-#define UNIX_SOCKET_PREFIX "localhost"
-#endif
-
 /* Reject URLs exceeding this length */
 #define MAX_URL_LEN 0xffff
 
@@ -165,11 +160,11 @@
   enum dupblob j;
 
   for(i = (enum dupstring)0; i < STRING_LAST; i++) {
-    Curl_safefree(data->set.str[i]);
+    curlx_safefree(data->set.str[i]);
   }
 
   for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
-    Curl_safefree(data->set.blobs[j]);
+    curlx_safefree(data->set.blobs[j]);
   }
 
   Curl_bufref_free(&data->state.referer);
@@ -177,7 +172,7 @@
 
 #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
   Curl_mime_cleanpart(data->set.mimepostp);
-  Curl_safefree(data->set.mimepostp);
+  curlx_safefree(data->set.mimepostp);
 #endif
 
 #ifndef CURL_DISABLE_COOKIES
@@ -190,14 +185,14 @@
 static void up_free(struct Curl_easy *data)
 {
   struct urlpieces *up = &data->state.up;
-  Curl_safefree(up->scheme);
-  Curl_safefree(up->hostname);
-  Curl_safefree(up->port);
-  Curl_safefree(up->user);
-  Curl_safefree(up->password);
-  Curl_safefree(up->options);
-  Curl_safefree(up->path);
-  Curl_safefree(up->query);
+  curlx_safefree(up->scheme);
+  curlx_safefree(up->hostname);
+  curlx_safefree(up->port);
+  curlx_safefree(up->user);
+  curlx_safefree(up->password);
+  curlx_safefree(up->options);
+  curlx_safefree(up->path);
+  curlx_safefree(up->query);
   curl_url_cleanup(data->state.uh);
   data->state.uh = NULL;
 }
@@ -244,9 +239,7 @@
     curlx_free(data->state.range);
 
   /* release any resolve information this transfer kept */
-  Curl_async_destroy(data);
-  Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
-  Curl_resolv_unlink(data, &data->state.dns[1]);
+  Curl_resolv_destroy_all(data);
 
   data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */
   data->magic = 0; /* force a clear AFTER the possibly enforced removal from
@@ -259,7 +252,7 @@
 
   /* Close down all open SSL info and sessions */
   Curl_ssl_close_all(data);
-  Curl_safefree(data->state.first_host);
+  curlx_safefree(data->state.first_host);
   Curl_ssl_free_certinfo(data);
 
   Curl_bufref_free(&data->state.referer);
@@ -280,45 +273,38 @@
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
 #endif
-  Curl_safefree(data->state.most_recent_ftp_entrypath);
-  Curl_safefree(data->info.contenttype);
-  Curl_safefree(data->info.wouldredirect);
+  curlx_safefree(data->state.most_recent_ftp_entrypath);
+  curlx_safefree(data->info.contenttype);
+  curlx_safefree(data->info.wouldredirect);
 
   data_priority_cleanup(data);
 
   /* No longer a dirty share, if it exists */
-  if(data->share) {
-    Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
-    data->share->dirty--;
-    Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
-  }
+  if(Curl_share_easy_unlink(data))
+    DEBUGASSERT(0);
 
   Curl_hash_destroy(&data->meta_hash);
-#ifndef CURL_DISABLE_PROXY
-  Curl_safefree(data->state.aptr.proxyuserpwd);
-#endif
-  Curl_safefree(data->state.aptr.uagent);
-  Curl_safefree(data->state.aptr.userpwd);
-  Curl_safefree(data->state.aptr.accept_encoding);
-  Curl_safefree(data->state.aptr.rangeline);
-  Curl_safefree(data->state.aptr.ref);
-  Curl_safefree(data->state.aptr.host);
+  curlx_safefree(data->state.aptr.uagent);
+  curlx_safefree(data->state.aptr.accept_encoding);
+  curlx_safefree(data->state.aptr.rangeline);
+  curlx_safefree(data->state.aptr.ref);
+  curlx_safefree(data->state.aptr.host);
 #ifndef CURL_DISABLE_COOKIES
-  Curl_safefree(data->state.aptr.cookiehost);
+  curlx_safefree(data->req.cookiehost);
 #endif
 #ifndef CURL_DISABLE_RTSP
-  Curl_safefree(data->state.aptr.rtsp_transport);
+  curlx_safefree(data->state.aptr.rtsp_transport);
 #endif
-  Curl_safefree(data->state.aptr.user);
-  Curl_safefree(data->state.aptr.passwd);
+  curlx_safefree(data->state.aptr.user);
+  curlx_safefree(data->state.aptr.passwd);
 #ifndef CURL_DISABLE_PROXY
-  Curl_safefree(data->state.aptr.proxyuser);
-  Curl_safefree(data->state.aptr.proxypasswd);
+  curlx_safefree(data->state.aptr.proxyuser);
+  curlx_safefree(data->state.aptr.proxypasswd);
 #endif
 
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
   Curl_mime_cleanpart(data->state.formp);
-  Curl_safefree(data->state.formp);
+  curlx_safefree(data->state.formp);
 #endif
 
   /* destruct wildcard structures if it is needed */
@@ -400,7 +386,7 @@
 #endif
 
   set->new_file_perms = 0644;    /* Default permissions */
-  set->allowed_protocols = (curl_prot_t) CURLPROTO_64ALL;
+  set->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL;
   set->redir_protocols = CURLPROTO_REDIR;
 
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@@ -436,6 +422,7 @@
   set->sep_headers = TRUE; /* separated header lists by default */
   set->buffer_size = READBUFFER_SIZE;
   set->upload_buffer_size = UPLOADBUFFER_DEFAULT;
+  set->upload_flags = CURLULFLAG_SEEN;
   set->happy_eyeballs_timeout = CURL_HET_DEFAULT;
   set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
   set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
@@ -458,7 +445,7 @@
 static void easy_meta_freeentry(void *p)
 {
   (void)p;
-  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+  /* Always FALSE. Cannot use a 0 assert here since compilers
    * are not in agreement if they then want a NORETURN attribute or
    * not. *sigh* */
   DEBUGASSERT(p == NULL);
@@ -531,28 +518,28 @@
 #ifndef CURL_DISABLE_PROXY
   Curl_free_idnconverted_hostname(&conn->http_proxy.host);
   Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
-  Curl_safefree(conn->http_proxy.user);
-  Curl_safefree(conn->socks_proxy.user);
-  Curl_safefree(conn->http_proxy.passwd);
-  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 */
+  curlx_safefree(conn->http_proxy.user);
+  curlx_safefree(conn->socks_proxy.user);
+  curlx_safefree(conn->http_proxy.passwd);
+  curlx_safefree(conn->socks_proxy.passwd);
+  curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */
+  curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */
 #endif
-  Curl_safefree(conn->user);
-  Curl_safefree(conn->passwd);
-  Curl_safefree(conn->sasl_authzid);
-  Curl_safefree(conn->options);
-  Curl_safefree(conn->oauth_bearer);
-  Curl_safefree(conn->host.rawalloc); /* hostname buffer */
-  Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
-  Curl_safefree(conn->secondaryhostname);
-  Curl_safefree(conn->localdev);
+  curlx_safefree(conn->user);
+  curlx_safefree(conn->passwd);
+  curlx_safefree(conn->sasl_authzid);
+  curlx_safefree(conn->options);
+  curlx_safefree(conn->oauth_bearer);
+  curlx_safefree(conn->host.rawalloc); /* hostname buffer */
+  curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
+  curlx_safefree(conn->secondaryhostname);
+  curlx_safefree(conn->localdev);
   Curl_ssl_conn_config_cleanup(conn);
 
 #ifdef USE_UNIX_SOCKETS
-  Curl_safefree(conn->unix_domain_socket);
+  curlx_safefree(conn->unix_domain_socket);
 #endif
-  Curl_safefree(conn->destination);
+  curlx_safefree(conn->destination);
   Curl_hash_destroy(&conn->meta_hash);
 
   curlx_free(conn); /* free all the connection oriented data */
@@ -650,19 +637,12 @@
       /* avoid check if already too old */
       dead = TRUE;
     }
-    else if(conn->scheme->run->connection_check) {
+    else if(conn->scheme->run->connection_is_dead) {
       /* The protocol has a special method for checking the state of the
          connection. Use it to check if the connection is dead. */
-      unsigned int state;
-
-      /* briefly attach the connection to this transfer for the purpose of
-         checking it */
+      /* briefly attach the connection for the check */
       Curl_attach_connection(data, conn);
-
-      state = conn->scheme->run->connection_check(data, conn,
-                                                     CONNCHECK_ISDEAD);
-      dead = (state & CONNRESULT_DEAD);
-      /* detach the connection again */
+      dead = conn->scheme->run->connection_is_dead(data, conn);
       Curl_detach_connection(data);
     }
     else {
@@ -705,18 +685,7 @@
 
   /* briefly attach for action */
   Curl_attach_connection(data, conn);
-  if(conn->scheme->run->connection_check) {
-    /* Do a protocol-specific keepalive check on the connection. */
-    unsigned int rc;
-    rc = conn->scheme->run->connection_check(data, conn,
-                                                CONNCHECK_KEEPALIVE);
-    if(rc & CONNRESULT_DEAD)
-      result = CURLE_RECV_ERROR;
-  }
-  else {
-    /* Do the generic action on the FIRSTSOCKET filter chain */
-    result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
-  }
+  result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
   Curl_detach_connection(data);
 
   conn->keepalive = *Curl_pgrs_now(data);
@@ -745,7 +714,7 @@
   BIT(want_proxy_ntlm_http);
   BIT(want_nego_http);
   BIT(want_proxy_nego_http);
-
+  BIT(req_tls); /* require TLS use from a clear-text start */
   BIT(wait_pipe);
   BIT(force_reuse);
   BIT(seen_pending_conn);
@@ -757,7 +726,7 @@
                                      struct url_conn_match *m)
 {
   /* connect-only or to-be-closed connections will not be reused */
-  if(conn->connect_only || conn->bits.close || conn->bits.no_reuse)
+  if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse)
     return FALSE;
 
   /* ip_version must match */
@@ -897,6 +866,9 @@
        (get_protocol_family(conn->scheme) != m->needle->scheme->protocol))
       return FALSE;
   }
+  else if(m->req_tls)
+    /* a clear-text STARTTLS protocol with required TLS */
+    return FALSE;
   return TRUE;
 }
 
@@ -1110,11 +1082,17 @@
   if(m->want_ntlm_http) {
     if(Curl_timestrcmp(m->needle->user, conn->user) ||
        Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
-
       /* we prefer a credential match, but this is at least a connection
-         that can be reused and "upgraded" to NTLM */
-      if(conn->http_ntlm_state == NTLMSTATE_NONE)
+         that can be reused and "upgraded" to NTLM if it does
+         not have any auth ongoing. */
+#ifdef USE_SPNEGO
+      if((conn->http_ntlm_state == NTLMSTATE_NONE)
+         && (conn->http_negotiate_state == GSS_AUTHNONE)) {
+#else
+      if(conn->http_ntlm_state == NTLMSTATE_NONE) {
+#endif
         m->found = conn;
+      }
       return FALSE;
     }
   }
@@ -1285,10 +1263,9 @@
   return TRUE;
 }
 
-static bool url_match_result(bool result, void *userdata)
+static bool url_match_result(void *userdata)
 {
   struct url_conn_match *match = userdata;
-  (void)result;
   if(match->found) {
     /* Attach it now while still under lock, so the connection does
      * no longer appear idle and can be reaped. */
@@ -1324,7 +1301,7 @@
                                 bool *waitpipe)
 {
   struct url_conn_match match;
-  bool result;
+  bool success;
 
   DEBUGASSERT(!data->conn);
   memset(&match, 0, sizeof(match));
@@ -1355,16 +1332,17 @@
     (needle->scheme->protocol & PROTO_FAMILY_HTTP);
 #endif
 #endif
+  match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL;
 
   /* Find a connection in the pool that matches what "data + needle"
    * requires. If a suitable candidate is found, it is attached to "data". */
-  result = Curl_cpool_find(data, needle->destination,
-                           url_match_conn, url_match_result, &match);
+  success = Curl_cpool_find(data, needle->destination,
+                            url_match_conn, url_match_result, &match);
 
   /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
    * is no matching connection then, yet. */
   *waitpipe = (bool)match.wait_pipe;
-  return result;
+  return success;
 }
 
 /*
@@ -1384,7 +1362,7 @@
   conn->send_idx = 0; /* default for sending transfer data */
   conn->connection_id = -1;    /* no ID */
   conn->attached_xfers = 0;
-  conn->remote_port = -1; /* unknown at this point */
+  conn->remote_port = 0; /* unknown at this point */
 
   /* Store creation time to help future close decision making */
   conn->created = *Curl_pgrs_now(data);
@@ -1420,7 +1398,7 @@
   conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
 #endif
   conn->ip_version = data->set.ipver;
-  conn->connect_only = (bool)data->set.connect_only;
+  conn->bits.connect_only = (bool)data->set.connect_only;
   conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
 
   /* Store the local bind parameters that will be used for this connection */
@@ -1451,80 +1429,6 @@
   return NULL;
 }
 
-const struct Curl_scheme *Curl_get_scheme(const char *scheme)
-{
-  return Curl_getn_scheme(scheme, strlen(scheme));
-}
-
-/* Returns a struct scheme pointer if the name is a known scheme. Check the
-   ->run struct field for non-NULL to figure out if an implementation is
-   present. */
-const struct Curl_scheme *Curl_getn_scheme(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
-     */
-  static const struct Curl_scheme * const all_schemes[67] = {
-    &Curl_scheme_file,
-    &Curl_scheme_mqtts, NULL,
-    &Curl_scheme_gophers, NULL,
-    &Curl_scheme_rtmpe,
-    &Curl_scheme_smtp,
-    &Curl_scheme_sftp,
-    &Curl_scheme_smb,
-    &Curl_scheme_smtps,
-    &Curl_scheme_telnet,
-    &Curl_scheme_gopher,
-    &Curl_scheme_tftp, NULL, NULL, NULL,
-    &Curl_scheme_ftps,
-    &Curl_scheme_http,
-    &Curl_scheme_imap,
-    &Curl_scheme_rtmps,
-    &Curl_scheme_rtmpt, NULL, NULL, NULL,
-    &Curl_scheme_ldaps,
-    &Curl_scheme_wss,
-    &Curl_scheme_https, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-    &Curl_scheme_rtsp,
-    &Curl_scheme_smbs,
-    &Curl_scheme_scp, NULL, NULL, NULL,
-    &Curl_scheme_pop3, NULL, NULL,
-    &Curl_scheme_rtmp, NULL, NULL, NULL,
-    &Curl_scheme_rtmpte, NULL, NULL, NULL,
-    &Curl_scheme_dict, NULL, NULL, NULL,
-    &Curl_scheme_mqtt,
-    &Curl_scheme_pop3s,
-    &Curl_scheme_imaps, NULL,
-    &Curl_scheme_ws, NULL,
-    &Curl_scheme_rtmpts,
-    &Curl_scheme_ldap, NULL, NULL,
-    &Curl_scheme_ftp,
-  };
-
-  if(len && (len <= 7)) {
-    const char *s = scheme;
-    size_t l = len;
-    const struct Curl_scheme *h;
-    unsigned int c = 978;
-    while(l) {
-      c <<= 5;
-      c += (unsigned int)Curl_raw_tolower(*s);
-      s++;
-      l--;
-    }
-
-    h = all_schemes[c % 67];
-    if(h && curl_strnequal(scheme, h->name, len) && !h->name[len])
-      return h;
-  }
-  return NULL;
-}
-
 static CURLcode findprotocol(struct Curl_easy *data,
                              struct connectdata *conn,
                              const char *protostr)
@@ -1627,6 +1531,7 @@
   CURLU *uh;
   CURLUcode uc;
   char *hostname;
+  size_t hlen;
   bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
 
   up_free(data); /* cleanup previous leftovers first */
@@ -1685,17 +1590,16 @@
     failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN);
     return CURLE_URL_MALFORMAT;
   }
+
   hostname = data->state.up.hostname;
+  hlen = hostname ? strlen(hostname) : 0;
 
   if(hostname && hostname[0] == '[') {
     /* This looks like an IPv6 address literal. See if there is an address
        scope. */
-    size_t hlen;
-    conn->bits.ipv6_ip = TRUE;
-    /* cut off the brackets! */
+    /* cut off the brackets after copying this! */
     hostname++;
-    hlen = strlen(hostname);
-    hostname[hlen - 1] = 0;
+    hlen -= 2;
 
     zonefrom_url(uh, data, conn);
   }
@@ -1704,6 +1608,7 @@
   conn->host.rawalloc = curlx_strdup(hostname ? hostname : "");
   if(!conn->host.rawalloc)
     return CURLE_OUT_OF_MEMORY;
+  conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */
   conn->host.name = conn->host.rawalloc;
 
   /*************************************************************
@@ -1719,7 +1624,7 @@
     /* This MUST use the IDN decoded name */
     if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) {
       char *url;
-      Curl_safefree(data->state.up.scheme);
+      curlx_safefree(data->state.up.scheme);
       uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
       if(uc)
         return Curl_uc_to_curlcode(uc);
@@ -1880,7 +1785,7 @@
                                            struct connectdata *conn)
 {
   const char *hostname;
-  int port;
+  uint16_t port;
   CURLcode result;
 
   DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close));
@@ -1900,19 +1805,21 @@
   else
 #endif
   {
-    port = conn->remote_port;
-    if(conn->bits.conn_to_host)
-      hostname = conn->conn_to_host.name;
-    else
-      hostname = conn->host.name;
+    port = conn->bits.conn_to_port ?
+      conn->conn_to_port : conn->remote_port;
+    hostname = conn->bits.conn_to_host ?
+      conn->conn_to_host.name : conn->host.name;
   }
 
 #ifdef USE_IPV6
-  conn->destination = curl_maprintf("%u/%d/%s", conn->scope_id, port,
-                                    hostname);
-#else
-  conn->destination = curl_maprintf("%d/%s", port, hostname);
+  /* IPv6 addresses with a scope_id (0 is default == global) have a
+   * printable representation with a '%<scope_id>' suffix. */
+  if(conn->scope_id)
+    conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port,
+                                      conn->scope_id);
+  else
 #endif
+  conn->destination = curl_maprintf("%s:%u", hostname, port);
   if(!conn->destination)
     return CURLE_OUT_OF_MEMORY;
 
@@ -2082,15 +1989,13 @@
     goto error;
   }
 
-#ifdef USE_SSL
-  if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY))
-#endif
-    if(IS_HTTPS_PROXY(proxytype)) {
-      failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
-            "HTTPS-proxy support.", proxy);
-      result = CURLE_NOT_BUILT_IN;
-      goto error;
-    }
+  if(IS_HTTPS_PROXY(proxytype) &&
+     !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
+    failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
+          "HTTPS-proxy support.", proxy);
+    result = CURLE_NOT_BUILT_IN;
+    goto error;
+  }
 
   sockstype =
     proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
@@ -2101,7 +2006,7 @@
   proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy;
   proxyinfo->proxytype = (unsigned char)proxytype;
 
-  /* Is there a username and password given in this proxy url? */
+  /* Is there a username and password given in this proxy URL? */
   uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
   if(uc && (uc != CURLUE_NO_USER)) {
     result = Curl_uc_to_curlcode(uc);
@@ -2120,7 +2025,7 @@
     proxyuser = NULL;
     if(result)
       goto error;
-    Curl_safefree(proxyinfo->passwd);
+    curlx_safefree(proxyinfo->passwd);
     if(!proxypasswd) {
       proxypasswd = curlx_strdup("");
       if(!proxypasswd) {
@@ -2239,7 +2144,7 @@
     if(conn->http_proxy.passwd)
       result = CURLE_OK;
     else
-      Curl_safefree(conn->http_proxy.user);
+      curlx_safefree(conn->http_proxy.user);
   }
   return result;
 }
@@ -2300,15 +2205,15 @@
 
   if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
                         data->set.str[STRING_NOPROXY] : no_proxy)) {
-    Curl_safefree(proxy);
-    Curl_safefree(socksproxy);
+    curlx_safefree(proxy);
+    curlx_safefree(socksproxy);
   }
 #ifndef CURL_DISABLE_HTTP
   else if(!proxy && !socksproxy)
     /* if the host is not in the noproxy list, detect proxy. */
     proxy = detect_proxy(data, conn);
 #endif /* CURL_DISABLE_HTTP */
-  Curl_safefree(no_proxy);
+  curlx_safefree(no_proxy);
 
 #ifdef USE_UNIX_SOCKETS
   /* For the time being do not mix proxy and Unix domain sockets. See #1274 */
@@ -2337,18 +2242,18 @@
    * connection that may exist registered to the same proxy host.
    ***********************************************************************/
   if(proxy || socksproxy) {
-    long ptype = conn->http_proxy.proxytype;
     if(proxy) {
-      result = parse_proxy(data, conn, proxy, ptype);
-      Curl_safefree(proxy); /* parse_proxy copies the proxy string */
+      result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype);
+      curlx_safefree(proxy); /* parse_proxy copies the proxy string */
       if(result)
         goto out;
     }
 
     if(socksproxy) {
-      result = parse_proxy(data, conn, socksproxy, ptype);
+      result = parse_proxy(data, conn, socksproxy,
+                           conn->socks_proxy.proxytype);
       /* parse_proxy copies the socks proxy string */
-      Curl_safefree(socksproxy);
+      curlx_safefree(socksproxy);
       if(result)
         goto out;
     }
@@ -2573,8 +2478,8 @@
 
 #ifndef CURL_DISABLE_NETRC
   if(data->set.use_netrc == CURL_NETRC_REQUIRED) {
-    Curl_safefree(*userp);
-    Curl_safefree(*passwdp);
+    curlx_safefree(*userp);
+    curlx_safefree(*passwdp);
   }
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
@@ -2835,19 +2740,15 @@
     ptr++;
   }
   else {
-    /* check whether the URL's hostname matches */
-    size_t hostname_to_match_len;
-    char *hostname_to_match = curl_maprintf("%s%s%s",
-                                            conn->bits.ipv6_ip ? "[" : "",
-                                            conn->host.name,
-                                            conn->bits.ipv6_ip ? "]" : "");
-    if(!hostname_to_match)
-      return CURLE_OUT_OF_MEMORY;
-    hostname_to_match_len = strlen(hostname_to_match);
-    host_match = curl_strnequal(ptr, hostname_to_match,
-                                hostname_to_match_len);
-    curlx_free(hostname_to_match);
-    ptr += hostname_to_match_len;
+    /* check whether the URL's hostname matches. Use the URL hostname
+     * when it was an IPv6 address. Otherwise use the connection's hostname
+     * that has IDN conversion. */
+    char *hostname_to_match =
+      (data->state.up.hostname && data->state.up.hostname[0] == '[') ?
+      data->state.up.hostname : conn->host.name;
+    size_t hlen = strlen(hostname_to_match);
+    host_match = curl_strnequal(ptr, hostname_to_match, hlen);
+    ptr += hlen;
 
     host_match = host_match && *ptr == ':';
     ptr++;
@@ -2908,13 +2809,13 @@
     else {
       /* no "connect to host" */
       conn->bits.conn_to_host = FALSE;
-      Curl_safefree(host);
+      curlx_safefree(host);
     }
 
     if(port >= 0) {
-      conn->conn_to_port = port;
+      conn->conn_to_port = (uint16_t)port;
       conn->bits.conn_to_port = TRUE;
-      infof(data, "Connecting to port: %d", port);
+      infof(data, "Connecting to port: %u", conn->conn_to_port);
     }
     else {
       /* no "connect to port" */
@@ -3016,7 +2917,7 @@
       conn->conn_to_port = as->dst.port;
       conn->bits.conn_to_port = TRUE;
       conn->bits.altused = TRUE;
-      infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d",
+      infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u",
             Curl_alpnid2str(srcalpnid), host, conn->remote_port,
             Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
       if(srcalpnid != as->dst.alpnid) {
@@ -3044,119 +2945,9 @@
   return result;
 }
 
-#ifdef USE_UNIX_SOCKETS
-static CURLcode resolve_unix(struct Curl_easy *data,
-                             struct connectdata *conn,
-                             const char *unix_path,
-                             struct Curl_dns_entry **pdns)
-{
-  struct Curl_dns_entry *hostaddr;
-  bool longpath = FALSE;
-
-  DEBUGASSERT(unix_path);
-  *pdns = NULL;
-
-  /* Unix domain sockets are local. The host gets ignored, use the specified
-   * domain socket address. Do not cache "DNS entries". There is no DNS
-   * involved and we already have the file system path available. */
-  hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry));
-  if(!hostaddr)
-    return CURLE_OUT_OF_MEMORY;
-
-  hostaddr->addr = Curl_unix2addr(unix_path, &longpath,
-                                  (bool)conn->bits.abstract_unix_socket);
-  if(!hostaddr->addr) {
-    if(longpath)
-      /* Long paths are not supported for now */
-      failf(data, "Unix socket path too long: '%s'", unix_path);
-    curlx_free(hostaddr);
-    return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
-  }
-
-  hostaddr->refcount = 1; /* connection is the only one holding this */
-  *pdns = hostaddr;
-  return CURLE_OK;
-}
-#endif
-
-/*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
-static CURLcode resolve_server(struct Curl_easy *data,
-                               struct connectdata *conn,
-                               bool *async,
-                               struct Curl_dns_entry **pdns)
-{
-  struct hostname *ehost;
-  int eport;
-  timediff_t timeout_ms = Curl_timeleft_ms(data);
-  const char *peertype = "host";
-  CURLcode result;
-
-  *pdns = NULL;
-
-#ifdef USE_UNIX_SOCKETS
-  {
-    const char *unix_path = conn->unix_domain_socket;
-
-#ifndef CURL_DISABLE_PROXY
-    if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
-       !strncmp(UNIX_SOCKET_PREFIX "/",
-                conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
-      unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
-#endif
-
-    if(unix_path) {
-      /* This only works if previous transport is TRNSPRT_TCP. Check it? */
-      conn->transport_wanted = TRNSPRT_UNIX;
-      return resolve_unix(data, conn, unix_path, pdns);
-    }
-  }
-#endif
-
-#ifndef CURL_DISABLE_PROXY
-  if(CONN_IS_PROXIED(conn)) {
-    ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
-      &conn->http_proxy.host;
-    eport = conn->bits.socksproxy ? conn->socks_proxy.port :
-      conn->http_proxy.port;
-    peertype = "proxy";
-  }
-  else
-#endif
-  {
-    ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
-    /* If not connecting via a proxy, extract the port from the URL, if it is
-     * there, thus overriding any defaults that might have been set above. */
-    eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port;
-  }
-
-  result = Curl_resolv_timeout(data, ehost->name,
-                               eport, conn->ip_version,
-                               pdns, timeout_ms);
-  DEBUGASSERT(!result || !*pdns);
-  if(result == CURLE_AGAIN) {
-    *async = TRUE;
-    return CURLE_OK;
-  }
-  else if(result == CURLE_OPERATION_TIMEDOUT) {
-    failf(data, "Failed to resolve %s '%s' with timeout after %"
-          FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
-          curlx_ptimediff_ms(Curl_pgrs_now(data),
-                             &data->progress.t_startsingle));
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-  else if(result) {
-    failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
-    return result;
-  }
-  DEBUGASSERT(*pdns);
-  return CURLE_OK;
-}
-
 static void url_move_hostname(struct hostname *dest, struct hostname *src)
 {
-  Curl_safefree(dest->rawalloc);
+  curlx_safefree(dest->rawalloc);
   Curl_free_idnconverted_hostname(dest);
   *dest = *src;
   memset(src, 0, sizeof(*src));
@@ -3222,7 +3013,7 @@
 static void conn_meta_freeentry(void *p)
 {
   (void)p;
-  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+  /* Always FALSE. Cannot use a 0 assert here since compilers
    * are not in agreement if they then want a NORETURN attribute or
    * not. *sigh* */
   DEBUGASSERT(p == NULL);
@@ -3399,6 +3190,10 @@
     needle->recv[SECONDARYSOCKET] = Curl_cf_recv;
     needle->send[SECONDARYSOCKET] = Curl_cf_send;
     needle->bits.tcp_fastopen = data->set.tcp_fastopen;
+#ifdef USE_UNIX_SOCKETS
+    if(Curl_conn_get_unix_path(needle))
+      needle->transport_wanted = TRNSPRT_UNIX;
+#endif
   }
 
 out:
@@ -3621,41 +3416,12 @@
   return result;
 }
 
-/* Curl_setup_conn() is called after the name resolve initiated in
- * create_conn() is all done.
- *
- * Curl_setup_conn() also handles reused connections
- */
-CURLcode Curl_setup_conn(struct Curl_easy *data,
-                         struct Curl_dns_entry *dns,
-                         bool *protocol_done)
-{
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-
-  DEBUGASSERT(dns);
-  Curl_pgrsTime(data, TIMER_NAMELOOKUP);
-
-  if(!conn->bits.reuse)
-    result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns,
-                             CURL_CF_SSL_DEFAULT);
-  if(!result)
-    result = Curl_headers_init(data);
-
-  /* not sure we need this flag to be passed around any more */
-  *protocol_done = FALSE;
-  return result;
-}
-
-CURLcode Curl_connect(struct Curl_easy *data,
-                      bool *asyncp,
-                      bool *protocol_done)
+CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
 {
   CURLcode result;
   struct connectdata *conn;
 
-  *asyncp = FALSE; /* assume synchronous resolves by default */
-  *protocol_done = FALSE;
+  *pconnected = FALSE;
 
   /* Set the request to virgin state based on transfer settings */
   Curl_req_hard_reset(&data->req, data);
@@ -3664,40 +3430,31 @@
   result = url_find_or_create_conn(data);
   conn = data->conn;
 
-  if(result == CURLE_NO_CONNECTION_AVAILABLE) {
-    DEBUGASSERT(!conn);
-    return result;
+  if(result)
+    goto out;
+
+  DEBUGASSERT(conn);
+  Curl_pgrsTime(data, TIMER_POSTQUEUE);
+  if(conn->bits.reuse) {
+    if(conn->attached_xfers > 1)
+      /* multiplexed */
+      *pconnected = TRUE;
+  }
+  else if(conn->scheme->flags & PROTOPT_NONETWORK) {
+    Curl_pgrsTime(data, TIMER_NAMELOOKUP);
+    *pconnected = TRUE;
+  }
+  else {
+    result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL,
+                             CURL_CF_SSL_DEFAULT);
+    if(!result)
+      result = Curl_headers_init(data);
+    CURL_TRC_M(data, "Curl_conn_setup() -> %d", result);
   }
 
-  if(!result) {
-    DEBUGASSERT(conn);
-    Curl_pgrsTime(data, TIMER_POSTQUEUE);
-    if(conn->bits.reuse) {
-      if(conn->attached_xfers > 1)
-        /* multiplexed */
-        *protocol_done = TRUE;
-    }
-    else if(conn->scheme->flags & PROTOPT_NONETWORK) {
-      *asyncp = FALSE;
-      Curl_pgrsTime(data, TIMER_NAMELOOKUP);
-      *protocol_done = TRUE;
-    }
-    else {
-      /*************************************************************
-       * Resolve the address of the server or proxy
-       *************************************************************/
-      struct Curl_dns_entry *dns;
-      result = resolve_server(data, conn, asyncp, &dns);
-      if(!result) {
-        *asyncp = !dns;
-        if(dns)
-          /* DNS resolution is done: that is either because this is a reused
-             connection, in which case DNS was unnecessary, or because DNS
-             really did finish already (synch resolver/fast async resolve) */
-          result = Curl_setup_conn(data, dns, protocol_done);
-      }
-    }
-  }
+out:
+  if(result == CURLE_NO_CONNECTION_AVAILABLE)
+    DEBUGASSERT(!conn);
 
   if(result && conn) {
     /* We are not allowed to return failure with memory left allocated in the
@@ -3734,6 +3491,7 @@
 
   data->state.done = FALSE; /* *_done() is not called yet */
 
+  data->req.no_body = data->set.opt_no_body;
   if(data->req.no_body)
     /* in HTTP lingo, no body means using the HEAD request... */
     data->state.httpreq = HTTPREQ_HEAD;
@@ -3865,11 +3623,6 @@
   return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
 }
 
-CURLcode Curl_1st_err(CURLcode r1, CURLcode r2)
-{
-  return r1 ? r1 : r2;
-}
-
 CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2)
 {
   if(r1 && (r1 != CURLE_AGAIN))
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h
index c4cd762..66baf70 100644
--- a/Utilities/cmcurl/lib/url.h
+++ b/Utilities/cmcurl/lib/url.h
@@ -26,7 +26,7 @@
 #include "curl_setup.h"
 
 /*
- * Prototypes for library-wide functions provided by url.c
+ * Prototypes for library-wide functions
  */
 
 CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn);
@@ -36,12 +36,7 @@
 void Curl_freeset(struct Curl_easy *data);
 CURLcode Curl_uc_to_curlcode(CURLUcode uc);
 CURLcode Curl_close(struct Curl_easy **datap); /* opposite of Curl_open() */
-CURLcode Curl_connect(struct Curl_easy *data,
-                      bool *asyncp,
-                      bool *protocol_done);
-CURLcode Curl_setup_conn(struct Curl_easy *data,
-                         struct Curl_dns_entry *dns,
-                         bool *protocol_done);
+CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected);
 void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn);
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userp, char **passwdp,
@@ -69,13 +64,6 @@
 void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
 void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
 
-/* Get protocol handler for a URI scheme
- * @param scheme URI scheme, case-insensitive
- * @return NULL of handler not found
- */
-const struct Curl_scheme *Curl_get_scheme(const char *scheme);
-const struct Curl_scheme *Curl_getn_scheme(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
                                              specified */
@@ -93,15 +81,8 @@
                           struct connectdata *conn);
 
 /**
- * Always eval all arguments, return the first result != CURLE_OK.
- * A non-short-circuit evaluation.
- */
-CURLcode Curl_1st_err(CURLcode r1, CURLcode r2);
-
-/**
  * Always eval all arguments, return the first
- * result != (CURLE_OK|CURLE_AGAIN) or `r1`.
- * A non-short-circuit evaluation.
+ * result != (CURLE_OK | CURLE_AGAIN) or `r1`.
  */
 CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2);
 
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h
index 29d4fe5..4d8f2c1 100644
--- a/Utilities/cmcurl/lib/urlapi-int.h
+++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -24,6 +24,34 @@
  *
  ***************************************************************************/
 #include "curl_setup.h"
+#include <curl/urlapi.h>
+
+/* Internal representation of CURLU. Point to URL-encoded strings. */
+struct Curl_URL {
+  char *scheme;
+  char *user;
+  char *password;
+  char *options; /* IMAP only? */
+  char *host;
+  char *zoneid; /* for numerical IPv6 addresses */
+  char *port;
+  char *path;
+  char *query;
+  char *fragment;
+  unsigned short portnum; /* the numerical version (if 'port' is set) */
+  BIT(query_present);    /* to support blank */
+  BIT(fragment_present); /* to support blank */
+  BIT(guessed_scheme);   /* when a URL without scheme is parsed */
+};
+
+#define HOST_ERROR   (-1) /* out of memory */
+#define HOST_NAME    1
+#define HOST_IPV4    2
+#define HOST_IPV6    3
+
+#define QUERY_NO      2
+#define QUERY_NOT_YET 3 /* allow to change to query */
+#define QUERY_YES     4
 
 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
                             bool guess_scheme);
@@ -32,12 +60,9 @@
 
 CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace);
 
-#ifdef UNITTESTS
-UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
-                                   bool has_scheme);
-#endif
-
-#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE
+#define U_CURLU_URLDECODE  (unsigned int)CURLU_URLDECODE
 #define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS
 
+bool Curl_url_same_origin(CURLU *base, CURLU *href);
+
 #endif /* HEADER_CURL_URLAPI_INT_H */
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index a4b82f3..103e0a6 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -63,24 +63,6 @@
 #define AF_INET6 (AF_INET + 1)
 #endif
 
-/* Internal representation of CURLU. Point to URL-encoded strings. */
-struct Curl_URL {
-  char *scheme;
-  char *user;
-  char *password;
-  char *options; /* IMAP only? */
-  char *host;
-  char *zoneid; /* for numerical IPv6 addresses */
-  char *port;
-  char *path;
-  char *query;
-  char *fragment;
-  unsigned short portnum; /* the numerical version (if 'port' is set) */
-  BIT(query_present);    /* to support blank */
-  BIT(fragment_present); /* to support blank */
-  BIT(guessed_scheme);   /* when a URL without scheme is parsed */
-};
-
 #define DEFAULT_SCHEME "https"
 
 static void free_urlhandle(struct Curl_URL *u)
@@ -124,19 +106,34 @@
 /* urlencode_str() writes data into an output dynbuf and URL-encodes the
  * spaces in the source URL accordingly.
  *
+ * This function re-encodes the string, meaning that it leaves already encoded
+ * bytes as-is and works by encoding only what *has* to be encoded - unless it
+ * has to uppercase the hex to normalize.
+ *
+ * Illegal percent-encoding sequences are left as-is.
+ *
  * URL encoding should be skipped for hostnames, otherwise IDN resolution
  * will fail.
+ *
+ * 'query' tells if it is a query part or not, or if it is allowed to
+ * "transition" into a query part with a question mark.
+ *
+ * @unittest 1675
  */
-static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
-                               size_t len, bool relative,
-                               bool query)
+UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url,
+                                 size_t len, bool relative,
+                                 unsigned int query);
+UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url,
+                                 size_t len, bool relative,
+                                 unsigned int query)
 {
   /* we must add this with whitespace-replacing */
-  bool left = !query;
   const unsigned char *iptr;
   const unsigned char *host_sep = (const unsigned char *)url;
   CURLcode result = CURLE_OK;
 
+  DEBUGASSERT((query >= QUERY_NO) && (query <= QUERY_YES));
+
   if(!relative) {
     size_t n;
     host_sep = (const unsigned char *)find_host_sep(url);
@@ -149,7 +146,7 @@
 
   for(iptr = host_sep; len && !result; iptr++, len--) {
     if(*iptr == ' ') {
-      if(left)
+      if(query != QUERY_YES)
         result = curlx_dyn_addn(o, "%20", 3);
       else
         result = curlx_dyn_addn(o, "+", 1);
@@ -159,10 +156,22 @@
       Curl_hexbyte(&out[1], *iptr);
       result = curlx_dyn_addn(o, out, 3);
     }
+    else if(*iptr == '%' && (len >= 3) &&
+            ISXDIGIT(iptr[1]) && ISXDIGIT(iptr[2]) &&
+            (ISLOWER(iptr[1]) || ISLOWER(iptr[2]))) {
+      /* uppercase it */
+      unsigned char hex = (unsigned char)((curlx_hexval(iptr[1]) << 4) |
+                                          curlx_hexval(iptr[2]));
+      unsigned char out[3] = { '%' };
+      Curl_hexbyte(&out[1], hex);
+      result = curlx_dyn_addn(o, out, 3);
+      iptr += 2;
+      len -= 2;
+    }
     else {
       result = curlx_dyn_addn(o, iptr, 1);
-      if(*iptr == '?')
-        left = FALSE;
+      if(*iptr == '?' && (query == QUERY_NOT_YET))
+        query = QUERY_YES;
     }
   }
 
@@ -251,8 +260,8 @@
                                       unsigned int flags,
                                       size_t *offset) /* to the hostname */
 {
-  CURLUcode result = CURLUE_OK;
-  CURLcode ccode;
+  CURLUcode ures = CURLUE_OK;
+  CURLcode result;
   char *userp = NULL;
   char *passwdp = NULL;
   char *optionsp = NULL;
@@ -285,21 +294,21 @@
 
   /* 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! */
-  ccode = Curl_parse_login_details(login, ptr - login - 1,
-                                   &userp, &passwdp,
-                                   (h && (h->flags & PROTOPT_URLOPTIONS)) ?
-                                   &optionsp : NULL);
-  if(ccode) {
+  result = Curl_parse_login_details(login, ptr - login - 1,
+                                    &userp, &passwdp,
+                                    (h && (h->flags & PROTOPT_URLOPTIONS)) ?
+                                    &optionsp : NULL);
+  if(result) {
     /* the only possible error from Curl_parse_login_details is out of
        memory: */
-    result = CURLUE_OUT_OF_MEMORY;
+    ures = CURLUE_OUT_OF_MEMORY;
     goto out;
   }
 
   if(userp) {
     if(flags & CURLU_DISALLOW_USER) {
       /* Option DISALLOW_USER is set and URL contains username. */
-      result = CURLUE_USER_NOT_ALLOWED;
+      ures = CURLUE_USER_NOT_ALLOWED;
       goto out;
     }
     curlx_free(u->user);
@@ -329,11 +338,14 @@
   u->password = NULL;
   u->options = NULL;
 
-  return result;
+  return ures;
 }
 
-UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
-                                   bool has_scheme)
+/* @unittest 1653 */
+UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host,
+                              bool has_scheme);
+UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host,
+                              bool has_scheme)
 {
   const char *portptr;
   const char *hostname = curlx_dyn_ptr(host);
@@ -386,9 +398,15 @@
   return CURLUE_OK;
 }
 
-/* this assumes 'hostname' now starts with [ */
-static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
-                            size_t hlen) /* length of hostname */
+/* This function assumes 'hostname' now starts with [. It trims 'hostname' in
+ * place and it sets u->zoneid if present.
+ *
+ * @unittest 1675
+ */
+UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
+                              size_t hlen);
+UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
+                              size_t hlen) /* length of hostname */
 {
   size_t len;
   DEBUGASSERT(*hostname == '[');
@@ -472,15 +490,12 @@
  * integers.
  *
  * Returns the host type.
+ *
+ * @unittest 1675
  */
 
-#define HOST_ERROR   (-1) /* out of memory */
-
-#define HOST_NAME    1
-#define HOST_IPV4    2
-#define HOST_IPV6    3
-
-static int ipv4_normalize(struct dynbuf *host)
+UNITTEST int ipv4_normalize(struct dynbuf *host);
+UNITTEST int ipv4_normalize(struct dynbuf *host)
 {
   bool done = FALSE;
   int n = 0;
@@ -624,7 +639,7 @@
     goto out;
   }
 
-  uc = Curl_parse_port(u, host, has_scheme);
+  uc = parse_port(u, host, has_scheme);
   if(uc)
     goto out;
 
@@ -657,21 +672,21 @@
 /* used for HTTP/2 server push */
 CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
 {
-  CURLUcode result;
+  CURLUcode ures;
   struct dynbuf host;
 
   DEBUGASSERT(authority);
   curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
 
-  result = parse_authority(u, authority, strlen(authority),
-                           CURLU_DISALLOW_USER, &host, !!u->scheme);
-  if(result)
+  ures = parse_authority(u, authority, strlen(authority),
+                         CURLU_DISALLOW_USER, &host, !!u->scheme);
+  if(ures)
     curlx_dyn_free(&host);
   else {
     curlx_free(u->host);
     u->host = curlx_dyn_ptr(&host);
   }
-  return result;
+  return ures;
 }
 
 /*
@@ -700,7 +715,6 @@
 
 /*
  * dedotdotify()
- * @unittest: 1395
  *
  * This function gets a null-terminated path with dot and dotdot sequences
  * passed in and strips them off according to the rules in RFC 3986 section
@@ -711,6 +725,8 @@
  * RETURNS
  *
  * Zero for success and 'out' set to an allocated dedotdotified string.
+ *
+ * @unittest 1395
  */
 UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
 UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
@@ -718,38 +734,36 @@
   struct dynbuf out;
   CURLcode result = CURLE_OK;
 
+  /* variables for leading dot checks */
+  const char *dinput = input;
+  size_t dlen = clen;
+
   *outp = NULL;
-  /* the path always starts with a slash, and a slash has not dot */
+  /* a single byte path cannot be cleaned up */
   if(clen < 2)
     return 0;
 
   curlx_dyn_init(&out, clen + 1);
 
-  /*  A. If the input buffer begins with a prefix of "../" or "./", then
-      remove that prefix from the input buffer; otherwise, */
-  if(is_dot(&input, &clen)) {
-    const char *p = input;
-    size_t blen = clen;
-
-    if(!clen)
-      /* . [end] */
-      goto end;
-    else if(ISSLASH(*p)) {
+  /* if the input buffer begins with a prefix of "../" or "./", then remove
+     that prefix from the input buffer; otherwise, */
+  if(is_dot(&dinput, &dlen)) {
+    if(ISSLASH(*dinput)) {
       /* one dot followed by a slash */
-      input = p + 1;
-      clen--;
+      input = dinput + 1;
+      clen = dlen - 1;
     }
 
-    /*  D. if the input buffer consists only of "." or "..", then remove
-        that from the input buffer; otherwise, */
-    else if(is_dot(&p, &blen)) {
-      if(!blen)
+    /* if the input buffer consists only of "." or "..", then remove
+       that from the input buffer; otherwise, */
+    else if(is_dot(&dinput, &dlen)) {
+      if(!dlen)
         /* .. [end] */
         goto end;
-      else if(ISSLASH(*p)) {
+      else if(ISSLASH(*dinput)) {
         /* ../ */
-        input = p + 1;
-        clen = blen - 1;
+        input = dinput + 1;
+        clen = dlen - 1;
       }
     }
   }
@@ -758,9 +772,9 @@
     if(ISSLASH(*input)) {
       const char *p = &input[1];
       size_t blen = clen - 1;
-      /*  B. if the input buffer begins with a prefix of "/./" or "/.", where
-          "."  is a complete path segment, then replace that prefix with "/" in
-          the input buffer; otherwise, */
+      /* if the input buffer begins with a prefix of "/./" or "/.", where "."
+         is a complete path segment, then replace that prefix with "/" in the
+         input buffer; otherwise, */
       if(is_dot(&p, &blen)) {
         if(!blen) { /* /. */
           result = curlx_dyn_addn(&out, "/", 1);
@@ -772,10 +786,10 @@
           continue;
         }
 
-        /*  C. if the input buffer begins with a prefix of "/../" or "/..",
-            where ".." is a complete path segment, then replace that prefix
-            with "/" in the input buffer and remove the last segment and its
-            preceding "/" (if any) from the output buffer; otherwise, */
+        /* if the input buffer begins with a prefix of "/../" or "/..", where
+           ".." is a complete path segment, then replace that prefix with "/"
+           in the input buffer and remove the last segment and its preceding
+           "/" (if any) from the output buffer; otherwise, */
         else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
           /* remove the last segment from the output buffer */
           size_t len = curlx_dyn_len(&out);
@@ -798,10 +812,10 @@
       }
     }
 
-    /*  E. move the first path segment in the input buffer to the end of
-        the output buffer, including the initial "/" character (if any) and
-        any subsequent characters up to, but not including, the next "/"
-        character or the end of the input buffer. */
+    /* move the first path segment in the input buffer to the end of the
+       output buffer, including the initial "/" character (if any) and any
+       subsequent characters up to, but not including, the next "/" character
+       or the end of the input buffer. */
 
     result = curlx_dyn_addn(&out, input, 1);
     input++;
@@ -820,13 +834,19 @@
   return result ? 1 : 0; /* success */
 }
 
-static CURLUcode parse_file(const char *url, size_t urllen, CURLU *u,
-                            struct dynbuf *host, const char **pathp,
-                            size_t *pathlenp)
+/*
+ * @unittest 1675
+ */
+UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u,
+                              const char **pathp, size_t *pathlenp);
+UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u,
+                              const char **pathp, size_t *pathlenp)
 {
   const char *path;
   size_t pathlen;
-  bool uncpath = FALSE;
+
+  *pathp = NULL;
+  *pathlenp = 0;
   if(urllen <= 6)
     /* file:/ is not enough to actually be a complete file: URL */
     return CURLUE_BAD_FILE_URL;
@@ -835,10 +855,6 @@
   path = &url[5];
   pathlen = urllen - 5;
 
-  u->scheme = curlx_strdup("file");
-  if(!u->scheme)
-    return CURLUE_OUT_OF_MEMORY;
-
   /* Extra handling URLs with an authority component (i.e. that start with
    * "file://")
    *
@@ -858,9 +874,6 @@
      *
      *  o the hostname is a FQDN that resolves to this machine, or
      *
-     *  o it is an UNC String transformed to an URI (Windows only, RFC 8089
-     *    Appendix E.3).
-     *
      * For brevity, we only consider URLs with empty, "localhost", or
      * "127.0.0.1" hostnames as local, otherwise as an UNC String.
      *
@@ -875,42 +888,16 @@
          checkprefix("127.0.0.1/", ptr)) {
         ptr += 9; /* now points to the slash after the host */
       }
-      else {
-#ifdef _WIN32
-        size_t len;
-
-        /* the hostname, NetBIOS computer name, can not contain disallowed
-           chars, and the delimiting slash character must be appended to the
-           hostname */
-        path = strpbrk(ptr, "/\\:*?\"<>|");
-        if(!path || *path != '/')
-          return CURLUE_BAD_FILE_URL;
-
-        len = path - ptr;
-        if(len) {
-          CURLcode code = curlx_dyn_addn(host, ptr, len);
-          if(code)
-            return cc2cu(code);
-          uncpath = TRUE;
-        }
-
-        ptr -= 2; /* now points to the // before the host in UNC */
-#else
+      else
         /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
            none */
         return CURLUE_BAD_FILE_URL;
-#endif
-      }
     }
 
     path = ptr;
     pathlen = urllen - (ptr - url);
   }
 
-  if(!uncpath)
-    /* no host for file: URLs by default */
-    curlx_dyn_reset(host);
-
 #if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__)
   /* Do not allow Windows drive letters when not in Windows.
    * This catches both "file:/c:" and "file:c:" */
@@ -927,6 +914,10 @@
     pathlen--;
   }
 #endif
+  u->scheme = curlx_strdup("file");
+  if(!u->scheme)
+    return CURLUE_OUT_OF_MEMORY;
+
   *pathp = path;
   *pathlenp = pathlen;
   return CURLUE_OK;
@@ -1012,16 +1003,16 @@
 static CURLUcode handle_fragment(CURLU *u, const char *fragment,
                                  size_t fraglen, unsigned int flags)
 {
-  CURLUcode result;
+  CURLUcode ures;
   u->fragment_present = TRUE;
   if(fraglen > 1) {
     /* skip the leading '#' in the copy but include the terminating null */
     if(flags & CURLU_URLENCODE) {
       struct dynbuf enc;
       curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-      result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE);
-      if(result)
-        return result;
+      ures = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, QUERY_NO);
+      if(ures)
+        return ures;
       u->fragment = curlx_dyn_ptr(&enc);
     }
     else {
@@ -1040,12 +1031,12 @@
   if(qlen > 1) {
     if(flags & CURLU_URLENCODE) {
       struct dynbuf enc;
-      CURLUcode result;
+      CURLUcode ures;
       curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
       /* skip the leading question mark */
-      result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE);
-      if(result)
-        return result;
+      ures = urlencode_str(&enc, query + 1, qlen - 1, TRUE, QUERY_YES);
+      if(ures)
+        return ures;
       u->query = curlx_dyn_ptr(&enc);
     }
     else {
@@ -1064,24 +1055,22 @@
 }
 
 static CURLUcode handle_path(CURLU *u, const char *path,
-                             size_t pathlen, unsigned int flags)
+                             size_t pathlen, unsigned int flags,
+                             bool is_file)
 {
-  CURLUcode result;
+  CURLUcode ures;
   if(pathlen && (flags & CURLU_URLENCODE)) {
     struct dynbuf enc;
     curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-    result = urlencode_str(&enc, path, pathlen, TRUE, FALSE);
-    if(result)
-      return result;
+    ures = urlencode_str(&enc, path, pathlen, TRUE, QUERY_NO);
+    if(ures)
+      return ures;
     pathlen = curlx_dyn_len(&enc);
     path = u->path = curlx_dyn_ptr(&enc);
   }
 
-  if(pathlen <= 1) {
-    /* there is no path left or the slash, unset */
-    path = NULL;
-  }
-  else {
+  if(pathlen >= (size_t)(1 + !is_file)) {
+    /* paths for file:// scheme can be one byte, others need to be two */
     if(!u->path) {
       u->path = curlx_memdup0(path, pathlen);
       if(!u->path)
@@ -1114,15 +1103,16 @@
   char schemebuf[MAX_SCHEME_LEN + 1];
   size_t schemelen = 0;
   size_t urllen;
-  CURLUcode result = CURLUE_OK;
+  CURLUcode ures = CURLUE_OK;
   struct dynbuf host;
+  bool is_file = FALSE;
 
   DEBUGASSERT(url);
 
   curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
 
-  result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE));
-  if(result)
+  ures = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE));
+  if(ures)
     goto fail;
 
   schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf),
@@ -1130,13 +1120,15 @@
                                             CURLU_DEFAULT_SCHEME));
 
   /* handle the file: scheme */
-  if(schemelen && !strcmp(schemebuf, "file"))
-    result = parse_file(url, urllen, u, &host, &path, &pathlen);
+  if(schemelen && !strcmp(schemebuf, "file")) {
+    is_file = TRUE;
+    ures = parse_file(url, urllen, u, &path, &pathlen);
+  }
   else {
     const char *hostp = NULL;
     size_t hostlen;
-    result = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp);
-    if(result)
+    ures = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp);
+    if(ures)
       goto fail;
 
     /* find the end of the hostname + port number */
@@ -1146,49 +1138,49 @@
     /* this pathlen also contains the query and the fragment */
     pathlen = urllen - (path - url);
     if(hostlen) {
-      result = parse_authority(u, hostp, hostlen, flags, &host,
-                               u->scheme != NULL);
-      if(!result && (flags & CURLU_GUESS_SCHEME) && !u->scheme)
-        result = guess_scheme(u, &host);
+      ures = parse_authority(u, hostp, hostlen, flags, &host,
+                             u->scheme != NULL);
+      if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme)
+        ures = guess_scheme(u, &host);
     }
     else if(flags & CURLU_NO_AUTHORITY) {
       /* allowed to be empty. */
       if(curlx_dyn_add(&host, ""))
-        result = CURLUE_OUT_OF_MEMORY;
+        ures = CURLUE_OUT_OF_MEMORY;
     }
     else
-      result = CURLUE_NO_HOST;
+      ures = CURLUE_NO_HOST;
   }
-  if(!result) {
+  if(!ures) {
     /* The path might at this point contain a fragment and/or a query to
        handle */
     const char *fragment = strchr(path, '#');
     if(fragment) {
       size_t fraglen = pathlen - (fragment - path);
-      result = handle_fragment(u, fragment, fraglen, flags);
+      ures = handle_fragment(u, fragment, fraglen, flags);
       /* after this, pathlen still contains the query */
       pathlen -= fraglen;
     }
   }
-  if(!result) {
+  if(!ures) {
     const char *query = memchr(path, '?', pathlen);
     if(query) {
       size_t qlen = pathlen - (query - path);
-      result = handle_query(u, query, qlen, flags);
+      ures = handle_query(u, query, qlen, flags);
       pathlen -= qlen;
     }
   }
-  if(!result)
+  if(!ures)
     /* the fragment and query parts are trimmed off from the path */
-    result = handle_path(u, path, pathlen, flags);
-  if(!result) {
+    ures = handle_path(u, path, pathlen, flags, is_file);
+  if(!ures) {
     u->host = curlx_dyn_ptr(&host);
     return CURLUE_OK;
   }
 fail:
   curlx_dyn_free(&host);
   free_urlhandle(u);
-  return result;
+  return ures;
 }
 
 /*
@@ -1197,15 +1189,15 @@
 static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
                                       unsigned int flags)
 {
-  CURLUcode result;
+  CURLUcode ures;
   CURLU tmpurl;
   memset(&tmpurl, 0, sizeof(tmpurl));
-  result = parseurl(url, &tmpurl, flags);
-  if(!result) {
+  ures = parseurl(url, &tmpurl, flags);
+  if(!ures) {
     free_urlhandle(u);
     *u = tmpurl;
   }
-  return result;
+  return ures;
 }
 
 /*
@@ -1272,7 +1264,8 @@
   curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
 
   if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
-     !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
+     !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed,
+                    QUERY_NOT_YET)) {
     uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
                               flags & ~U_CURLU_PATH_AS_IS);
   }
@@ -1380,11 +1373,12 @@
   if(urldecode) {
     char *decoded;
     size_t dlen;
-    /* this unconditional rejection of control bytes is documented
-       API behavior */
-    CURLcode res = Curl_urldecode(part, partlen, &decoded, &dlen, REJECT_CTRL);
+    /* this unconditional rejection of control bytes is documented API
+       behavior */
+    CURLcode result = Curl_urldecode(part, partlen, &decoded, &dlen,
+                                     REJECT_CTRL);
     curlx_free(part);
-    if(res)
+    if(result)
       return CURLUE_URLDECODE;
     part = decoded;
     partlen = dlen;
@@ -1392,7 +1386,8 @@
   if(urlencode) {
     struct dynbuf enc;
     curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-    uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY);
+    uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY ?
+                       QUERY_YES : QUERY_NO);
     curlx_free(part);
     if(uc)
       return uc;
@@ -1425,29 +1420,25 @@
 static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags)
 {
   char *url;
-  const char *scheme;
-  char *options = u->options;
-  char *port = u->port;
   char *allochost = NULL;
-  bool show_fragment =
-    u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY);
-  bool show_query = (u->query && u->query[0]) ||
-    (u->query_present && flags & CURLU_GET_EMPTY);
-  bool punycode = (flags & CURLU_PUNYCODE) ? 1 : 0;
-  bool depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0;
-  bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0;
+  const char *fragmentsep =
+    (u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY)) ?
+    "#" : "";
+  const char *querysep = ((u->query && u->query[0]) ||
+                          (u->query_present && flags & CURLU_GET_EMPTY)) ?
+    "?" : "";
   char portbuf[7];
   if(u->scheme && curl_strequal("file", u->scheme)) {
     url = curl_maprintf("file://%s%s%s%s%s",
-                        u->path,
-                        show_query ? "?" : "",
-                        u->query ? u->query : "",
-                        show_fragment ? "#" : "",
-                        u->fragment ? u->fragment : "");
+                        u->path, querysep, u->query ? u->query : "",
+                        fragmentsep, u->fragment ? u->fragment : "");
   }
   else if(!u->host)
     return CURLUE_NO_HOST;
   else {
+    const char *scheme;
+    char *options = u->options;
+    char *port = u->port;
     const struct Curl_scheme *h = NULL;
     char schemebuf[MAX_SCHEME_LEN + 5];
     if(u->scheme)
@@ -1489,19 +1480,19 @@
         allochost = curlx_dyn_ptr(&enc);
       }
     }
-    else if(urlencode) {
+    else if(flags & CURLU_URLENCODE) {
       allochost = curl_easy_escape(NULL, u->host, 0);
       if(!allochost)
         return CURLUE_OUT_OF_MEMORY;
     }
-    else if(punycode) {
+    else if(flags & CURLU_PUNYCODE) {
       if(!Curl_is_ASCII_name(u->host)) {
         CURLUcode ret = host_decode(u->host, &allochost);
         if(ret)
           return ret;
       }
     }
-    else if(depunyfy) {
+    else if(flags & CURLU_PUNY2IDN) {
       if(Curl_is_ASCII_name(u->host)) {
         CURLUcode ret = host_encode(u->host, &allochost);
         if(ret)
@@ -1526,9 +1517,9 @@
                         port ? ":" : "",
                         port ? port : "",
                         u->path ? u->path : "/",
-                        show_query ? "?" : "",
+                        querysep,
                         u->query ? u->query : "",
-                        show_fragment ? "#" : "",
+                        fragmentsep,
                         u->fragment ? u->fragment : "");
     curlx_free(allochost);
   }
@@ -1649,6 +1640,7 @@
     const char *s = scheme;
     if(ISALPHA(*s)) {
       /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+      s++;
       while(--plen) {
         if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
           s++; /* fine */
@@ -1737,37 +1729,37 @@
     memset(u, 0, sizeof(struct Curl_URL));
     break;
   case CURLUPART_SCHEME:
-    Curl_safefree(u->scheme);
+    curlx_safefree(u->scheme);
     u->guessed_scheme = FALSE;
     break;
   case CURLUPART_USER:
-    Curl_safefree(u->user);
+    curlx_safefree(u->user);
     break;
   case CURLUPART_PASSWORD:
-    Curl_safefree(u->password);
+    curlx_safefree(u->password);
     break;
   case CURLUPART_OPTIONS:
-    Curl_safefree(u->options);
+    curlx_safefree(u->options);
     break;
   case CURLUPART_HOST:
-    Curl_safefree(u->host);
+    curlx_safefree(u->host);
     break;
   case CURLUPART_ZONEID:
-    Curl_safefree(u->zoneid);
+    curlx_safefree(u->zoneid);
     break;
   case CURLUPART_PORT:
     u->portnum = 0;
-    Curl_safefree(u->port);
+    curlx_safefree(u->port);
     break;
   case CURLUPART_PATH:
-    Curl_safefree(u->path);
+    curlx_safefree(u->path);
     break;
   case CURLUPART_QUERY:
-    Curl_safefree(u->query);
+    curlx_safefree(u->query);
     u->query_present = FALSE;
     break;
   case CURLUPART_FRAGMENT:
-    Curl_safefree(u->fragment);
+    curlx_safefree(u->fragment);
     u->fragment_present = FALSE;
     break;
   default:
@@ -1845,7 +1837,7 @@
     break;
   case CURLUPART_HOST:
     storep = &u->host;
-    Curl_safefree(u->zoneid);
+    curlx_safefree(u->zoneid);
     break;
   case CURLUPART_ZONEID:
     storep = &u->zoneid;
@@ -1996,3 +1988,36 @@
   }
   return CURLUE_OK;
 }
+
+bool Curl_url_same_origin(CURLU *base, CURLU *href)
+{
+  const struct Curl_scheme *s = NULL;
+
+  /* base must be an absolute URL */
+  if(!base->scheme || !base->host)
+    return FALSE;
+  if(href->scheme && !curl_strequal(base->scheme, href->scheme))
+    return FALSE;
+  if(href->host) {
+    if(!curl_strequal(base->host, href->host))
+      return FALSE;
+    if(!curl_strequal(base->port, href->port)) {
+      /* This may still match if only one has an explicit port
+       * and it is the default for the scheme. */
+      if(base->port && href->port)
+        return FALSE;
+
+      s = Curl_get_scheme(base->scheme);
+      if(!s) /* Cannot match default port for unknown scheme */
+        return FALSE;
+
+      /* The port which is set must be the default one */
+      if((base->port && (base->portnum != s->defport)) ||
+         (href->port && (href->portnum != s->defport)))
+        return FALSE;
+    }
+  }
+  else if(href->port) /* no host in href, then there must be no port */
+    return FALSE;
+  return TRUE;
+}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index 5ae1480..f35bfee 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -26,70 +26,7 @@
 /* This file is for lib internal stuff */
 #include "curl_setup.h"
 
-#define PORT_FTP    21
-#define PORT_FTPS   990
-#define PORT_TELNET 23
-#define PORT_HTTP   80
-#define PORT_HTTPS  443
-#define PORT_DICT   2628
-#define PORT_LDAP   389
-#define PORT_LDAPS  636
-#define PORT_TFTP   69
-#define PORT_SSH    22
-#define PORT_IMAP   143
-#define PORT_IMAPS  993
-#define PORT_POP3   110
-#define PORT_POP3S  995
-#define PORT_SMB    445
-#define PORT_SMBS   445
-#define PORT_SMTP   25
-#define PORT_SMTPS  465 /* sometimes called SSMTP */
-#define PORT_RTSP   554
-#define PORT_RTMP   1935
-#define PORT_RTMPT  PORT_HTTP
-#define PORT_RTMPS  PORT_HTTPS
-#define PORT_GOPHER 70
-#define PORT_MQTT   1883
-#define PORT_MQTTS  8883
-
-#ifdef USE_ECH
-/* CURLECH_ bits for the tls_ech option */
-#define CURLECH_DISABLE    (1 << 0)
-#define CURLECH_GREASE     (1 << 1)
-#define CURLECH_ENABLE     (1 << 2)
-#define CURLECH_HARD       (1 << 3)
-#define CURLECH_CLA_CFG    (1 << 4)
-#endif
-
-#ifndef CURL_DISABLE_WEBSOCKETS
-/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
- * the rest are internal information. If we use higher bits we only do this on
- * platforms that have a >= 64-bit type and then we use such a type for the
- * protocol fields in the protocol handler.
- */
-#define CURLPROTO_WS     (1L << 30)
-#define CURLPROTO_WSS    ((curl_prot_t)1 << 31)
-#else
-#define CURLPROTO_WS     0L
-#define CURLPROTO_WSS    0L
-#endif
-
-#define CURLPROTO_MQTTS  (1LL << 32)
-
-#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff)
-
-/* the default protocols accepting a redirect to */
-#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \
-                         CURLPROTO_FTPS)
-
-typedef curl_off_t curl_prot_t;
-
-/* This mask is for all the old protocols that are provided and defined in the
-   public header and shall exclude protocols added since which are not exposed
-   in the API */
-#define CURLPROTO_MASK   0x3ffffff
-
-#define CURL_DEFAULT_USER "anonymous"
+#define CURL_DEFAULT_USER     "anonymous"
 #define CURL_DEFAULT_PASSWORD "ftp@example.com"
 
 #if !defined(_WIN32) && !defined(MSDOS)
@@ -98,31 +35,11 @@
 #define CURL_PREFER_LF_LINEENDS
 #endif
 
-/* Convenience defines for checking protocols or their SSL based version. Each
-   protocol handler should only ever have a single CURLPROTO_ in its protocol
-   field. */
-#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \
-                           CURLPROTO_WSS)
-#define PROTO_FAMILY_FTP  (CURLPROTO_FTP | CURLPROTO_FTPS)
-#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S)
-#define PROTO_FAMILY_SMB  (CURLPROTO_SMB | CURLPROTO_SMBS)
-#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP | CURLPROTO_SMTPS)
-#define PROTO_FAMILY_SSH  (CURLPROTO_SCP | CURLPROTO_SFTP)
-
-#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \
-  !defined(CURL_DISABLE_POP3)
-/* these protocols support CURLOPT_DIRLISTONLY */
-#define CURL_LIST_ONLY_PROTOCOL 1
-#endif
-
 #define DEFAULT_CONNCACHE_SIZE 5
 
 /* length of longest IPv6 address string including the trailing null */
 #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
 
-/* Default FTP/IMAP etc response timeout in milliseconds */
-#define RESP_TIMEOUT (60 * 1000)
-
 /* Max string input length is a precaution against abuse and to detect junk
    input easier and better. */
 #define CURL_MAX_INPUT_LENGTH 8000000
@@ -167,6 +84,7 @@
                              size_t *pnread);          /* how much received */
 
 #include "mime.h"
+#include "protocol.h"
 #include "ftp.h"
 #include "http.h"
 #include "smb.h"
@@ -322,16 +240,11 @@
   GSS_AUTHSUCC
 } curlnegotiate;
 
-#ifdef CURL_DISABLE_PROXY
-#define CONN_IS_PROXIED(x) 0
-#else
-#define CONN_IS_PROXIED(x) (x)->bits.proxy
-#endif
-
 /*
  * Boolean values that concerns this connection.
  */
 struct ConnectBits {
+  BIT(connect_only);
 #ifndef CURL_DISABLE_PROXY
   BIT(httpproxy);  /* if set, this transfer is done through an HTTP proxy */
   BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
@@ -350,8 +263,6 @@
                         that overrides the host in the URL */
   BIT(conn_to_port); /* if set, this connection has a "connect to port"
                         that overrides the port in the URL (remote port) */
-  BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6
-                   IP address */
   BIT(ipv6);    /* we communicate with a site using an IPv6 address */
   BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is
                    supposed to be called, after ->curl_do() */
@@ -378,9 +289,6 @@
   BIT(multiplex); /* connection is multiplexed */
   BIT(tcp_fastopen); /* use TCP Fast Open */
   BIT(tls_enable_alpn); /* TLS ALPN extension? */
-#ifndef CURL_DISABLE_DOH
-  BIT(doh);
-#endif
 #ifdef USE_UNIX_SOCKETS
   BIT(abstract_unix_socket);
 #endif
@@ -393,6 +301,7 @@
   BIT(shutdown_handler); /* connection shutdown: handler shut down */
   BIT(shutdown_filters); /* connection shutdown: filters shut down */
   BIT(in_cpool);     /* connection is kept in a connection pool */
+  BIT(dns_resolved); /* DNS records for connection were resolved */
 };
 
 struct hostname {
@@ -402,165 +311,9 @@
   const char *dispname; /* name to display, as 'name' might be encoded */
 };
 
-/*
- * Flags on the keepon member of the Curl_transfer_keeper
- */
-
-#define KEEP_NONE       0
-#define KEEP_RECV       (1 << 0) /* there is or may be data to read */
-#define KEEP_SEND       (1 << 1) /* there is or may be data to write */
-
-/* transfer wants to send */
-#define CURL_WANT_SEND(data) ((data)->req.keepon & KEEP_SEND)
-/* transfer wants to receive */
-#define CURL_WANT_RECV(data) ((data)->req.keepon & KEEP_RECV)
-
 #define FIRSTSOCKET     0
 #define SECONDARYSOCKET 1
 
-/*
- * Specific protocol handler.
- */
-
-struct Curl_protocol {
-  /* Complement to setup_connection_internals(). This is done before the
-     transfer "owns" the connection. */
-  CURLcode (*setup_connection)(struct Curl_easy *data,
-                               struct connectdata *conn);
-
-  /* These two functions MUST be set to be protocol dependent */
-  CURLcode (*do_it)(struct Curl_easy *data, bool *done);
-  CURLcode (*done)(struct Curl_easy *, CURLcode, bool);
-
-  /* If the curl_do() function is better made in two halves, this
-   * curl_do_more() function will be called afterwards, if set. For example
-   * for doing the FTP stuff after the PASV/PORT command.
-   */
-  CURLcode (*do_more)(struct Curl_easy *, int *);
-
-  /* This function *MAY* be set to a protocol-dependent function that is run
-   * after the connect() and everything is done, as a step in the connection.
-   * The 'done' pointer points to a bool that should be set to TRUE if the
-   * function completes before return. If it does not complete, the caller
-   * should call the ->connecting() function until it is.
-   */
-  CURLcode (*connect_it)(struct Curl_easy *data, bool *done);
-
-  /* See above. */
-  CURLcode (*connecting)(struct Curl_easy *data, bool *done);
-  CURLcode (*doing)(struct Curl_easy *data, bool *done);
-
-  /* Called from the multi interface during the PROTOCONNECT phase, and it
-     should then return a proper fd set */
-  CURLcode (*proto_pollset)(struct Curl_easy *data,
-                            struct easy_pollset *ps);
-
-  /* Called from the multi interface during the DOING phase, and it should
-     then return a proper fd set */
-  CURLcode (*doing_pollset)(struct Curl_easy *data,
-                            struct easy_pollset *ps);
-
-  /* Called from the multi interface during the DO_MORE phase, and it should
-     then return a proper fd set */
-  CURLcode (*domore_pollset)(struct Curl_easy *data,
-                            struct easy_pollset *ps);
-
-  /* Called from the multi interface during the DO_DONE, PERFORM and
-     WAITPERFORM phases, and it should then return a proper fd set. Not setting
-     this will make libcurl use the generic default one. */
-  CURLcode (*perform_pollset)(struct Curl_easy *data,
-                              struct easy_pollset *ps);
-
-  /* This function *MAY* be set to a protocol-dependent function that is run
-   * by the curl_disconnect(), as a step in the disconnection. If the handler
-   * is called because the connection has been considered dead,
-   * dead_connection is set to TRUE. The connection is (again) associated with
-   * the transfer here.
-   */
-  CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
-                         bool dead_connection);
-
-  /* If used, this function gets called from transfer.c to
-     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);
-
-  /* If used, this function gets called from transfer.c to
-     allow the protocol to do extra handling in writing a single response
-     header line to the client. */
-  CURLcode (*write_resp_hd)(struct Curl_easy *data,
-                            const char *hd, size_t hdlen, bool is_eos);
-
-  /* This function can perform various checks on the connection. See
-     CONNCHECK_* for more information about the checks that can be performed,
-     and CONNRESULT_* for the results that can be returned. */
-  uint32_t (*connection_check)(struct Curl_easy *data,
-                               struct connectdata *conn,
-                               uint32_t checks_to_perform);
-
-  /* attach() attaches this transfer to this connection */
-  void (*attach)(struct Curl_easy *data, struct connectdata *conn);
-
-  /* return CURLE_OK if a redirect to `newurl` should be followed,
-     CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change
-     the way the follow request is performed. */
-  CURLcode (*follow)(struct Curl_easy *data, const char *newurl,
-                     followtype type);
-};
-
-struct Curl_scheme {
-  const char *name;       /* URL scheme name in lowercase */
-  const struct Curl_protocol *run; /* implementation */
-  curl_prot_t protocol;   /* See CURLPROTO_* - this needs to be the single
-                             specific protocol bit */
-  curl_prot_t family;     /* single bit for protocol family; the non-TLS name
-                             of the protocol this is */
-  uint32_t flags;         /* Extra particular characteristics, see PROTOPT_* */
-  uint16_t defport;       /* Default port. */
-};
-
-#define PROTOPT_NONE 0             /* nothing extra */
-#define PROTOPT_SSL (1 << 0)       /* uses SSL */
-#define PROTOPT_DUAL (1 << 1)      /* this protocol uses two connections */
-#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */
-/* some protocols will have to call the underlying functions without regard to
-   what exact state the socket signals. IE even if the socket says "readable",
-   the send function might need to be called while uploading, or vice versa.
-*/
-#define PROTOPT_DIRLOCK (1 << 3)
-#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */
-#define PROTOPT_NEEDSPWD (1 << 5)  /* needs a password, and if none is set it
-                                      gets a default */
-#define PROTOPT_NOURLQUERY (1 << 6)  /* protocol cannot handle
-                                        URL query strings (?foo=bar) ! */
-#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per
-                                            request instead of per
-                                            connection */
-#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */
-/* (1 << 9) was PROTOPT_STREAM, now free */
-#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo
-                                        field of the URL */
-#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a
-                                           HTTP proxy as HTTP proxies may know
-                                           this protocol and act as
-                                           a gateway */
-#define PROTOPT_WILDCARD (1 << 12)  /* protocol supports wildcard matching */
-#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in
-                                         username and password */
-#define PROTOPT_NOTCPPROXY (1 << 14)  /* this protocol cannot proxy over TCP */
-#define PROTOPT_SSL_REUSE (1 << 15)   /* this protocol may reuse an existing
-                                         SSL connection in the same family
-                                         without having PROTOPT_SSL. */
-#define PROTOPT_CONN_REUSE (1 << 16)  /* this protocol can reuse connections */
-
-#define CONNCHECK_NONE 0                 /* No checks */
-#define CONNCHECK_ISDEAD (1 << 0)        /* Check if the connection is dead. */
-#define CONNCHECK_KEEPALIVE (1 << 1)     /* Perform any keepalive function. */
-
-#define CONNRESULT_NONE 0                /* No extra information. */
-#define CONNRESULT_DEAD (1 << 0)         /* The connection is dead. */
-
 #define TRNSPRT_NONE 0
 #define TRNSPRT_TCP  3
 #define TRNSPRT_UDP  4
@@ -630,7 +383,7 @@
   char *sasl_authzid;     /* authorization identity string, allocated */
   char *oauth_bearer; /* OAUTH2 bearer, allocated */
   struct curltime created; /* creation time */
-  struct curltime lastused; /* when returned to the connection poolas idle */
+  struct curltime lastused; /* when returned to the connection pool as idle */
 
   /* A connection can have one or two sockets and connection filters.
    * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */
@@ -695,33 +448,31 @@
      that subsequent bound-requested connections are not accidentally reusing
      wrong connections. */
   char *localdev;
-  uint16_t localportrange;
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
   int socks5_gssapi_enctype;
 #endif
-  /* The field below gets set in connect.c:connecthost() */
-  int remote_port; /* the remote port, not the proxy port! */
-  int conn_to_port; /* the remote port to connect to. valid only if
-                       bits.conn_to_port is set */
-
   uint32_t attached_xfers; /* # of attached easy handles */
 
 #ifdef USE_IPV6
   uint32_t scope_id;  /* Scope id for IPv6 */
 #endif
+  /* The field below gets set in connect.c:connecthost() */
+  uint16_t remote_port; /* the remote port, not the proxy port! */
+  uint16_t conn_to_port; /* the remote port to connect to. valid only if
+                            bits.conn_to_port is set */
+  uint16_t localportrange;
   uint16_t localport;
   uint16_t secondary_port; /* secondary socket remote port to connect to
                                     (ftp) */
-  uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not
-   necessarily the transport the connection ends using due to Alt-Svc
-   and happy eyeballing. Use `Curl_conn_get_transport() for actual value
-   once the connection is set up. */
+  uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily
+   the transport the connection ends using due to Alt-Svc and happy
+   eyeballing. Use Curl_conn_get_transport() for actual value once the
+   connection is set up. */
   uint8_t ip_version; /* copied from the Curl_easy at creation time */
   /* HTTP version last responded with by the server or negotiated via ALPN.
    * 0 at start, then one of 09, 10, 11, etc. */
   uint8_t httpversion_seen;
   uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */
-  BIT(connect_only);
 };
 
 #ifndef CURL_DISABLE_PROXY
@@ -792,6 +543,7 @@
                       force redraw at next call */
   struct pgrs_dir ul;
   struct pgrs_dir dl;
+  curl_off_t deliver; /* amount of data delivered to application */
 
   curl_off_t current_speed; /* uses the currently fastest transfer */
   curl_off_t earlydata_sent;
@@ -817,7 +569,7 @@
 
   curl_off_t speed_amount[CURL_SPEED_RECORDS];
   struct curltime speed_time[CURL_SPEED_RECORDS];
-  uint8_t speeder_c;
+  uint32_t speeder_c;
   BIT(hide);
   BIT(ul_size_known);
   BIT(dl_size_known);
@@ -899,13 +651,6 @@
   EXPIRE_LAST /* not an actual timer, used as a marker only */
 } expire_id;
 
-typedef enum {
-  TRAILERS_NONE,
-  TRAILERS_INITIALIZED,
-  TRAILERS_SENDING,
-  TRAILERS_DONE
-} trailers_state;
-
 /*
  * One instance for each timeout an easy handle can set.
  */
@@ -940,8 +685,10 @@
   curl_off_t recent_conn_id; /* The most recent connection used, might no
                               * longer exist */
   struct dynbuf headerb; /* buffer to store headers in */
+#ifndef CURL_DISABLE_HSTS
   struct curl_slist *hstslist; /* list of HSTS files set by
                                   curl_easy_setopt(HSTS) calls */
+#endif
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
                                 bytes / second */
 
@@ -965,9 +712,8 @@
   struct auth authhost;  /* auth details for host */
   struct auth authproxy; /* auth details for proxy */
 
-  struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */
 #ifdef USE_CURL_ASYNC
-  struct Curl_async async;  /* asynchronous name resolver data */
+  struct Curl_resolv_async *async;  /* asynchronous name resolver data */
 #endif
 
 #ifdef USE_OPENSSL
@@ -1027,8 +773,6 @@
   struct Curl_llist httphdrs; /* received headers */
   struct curl_header headerout[2]; /* for external purposes */
   struct Curl_header_store *prevhead; /* the latest added header */
-  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
@@ -1048,13 +792,9 @@
   struct dynamically_allocated_data {
     char *uagent;
     char *accept_encoding;
-    char *userpwd;
     char *rangeline;
     char *ref;
     char *host;
-#ifndef CURL_DISABLE_COOKIES
-    char *cookiehost;
-#endif
 #ifndef CURL_DISABLE_RTSP
     char *rtsp_transport;
 #endif
@@ -1063,7 +803,6 @@
     char *user;
     char *passwd;
 #ifndef CURL_DISABLE_PROXY
-    char *proxyuserpwd;
     char *proxyuser;
     char *proxypasswd;
 #endif
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index 9609390..5ecfd60 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -255,13 +255,13 @@
  * Parameters:
  *
  * chlgref [in]     - The challenge message.
- * nonce   [in/out] - The buffer where the nonce will be stored.
+ * nonce   [in/out] - The buffer where the nonce is stored.
  * nlen    [in]     - The length of the nonce buffer.
- * realm   [in/out] - The buffer where the realm will be stored.
+ * realm   [in/out] - The buffer where the realm is stored.
  * rlen    [in]     - The length of the realm buffer.
- * alg     [in/out] - The buffer where the algorithm will be stored.
+ * alg     [in/out] - The buffer where the algorithm is stored.
  * alen    [in]     - The length of the algorithm buffer.
- * qop     [in/out] - The buffer where the qop-options will be stored.
+ * qop     [in/out] - The buffer where the qop-options is stored.
  * qlen    [in]     - The length of the qop buffer.
  *
  * Returns CURLE_OK on success.
@@ -356,6 +356,7 @@
   char *spn         = NULL;
   char *qrealm;
   char *qnonce;
+  char *quserp;
 
   /* Decode the challenge message */
   CURLcode result = auth_decode_digest_md5_message(chlg,
@@ -384,7 +385,7 @@
   if(result)
     return result;
 
-  /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
+  /* Good so far, now calculate A1 and H(A1) according to RFC 2831 */
   ctxt = Curl_MD5_init(&Curl_DIGEST_MD5);
   if(!ctxt)
     return CURLE_OUT_OF_MEMORY;
@@ -469,20 +470,22 @@
   for(i = 0; i < MD5_DIGEST_LEN; i++)
     curl_msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
 
-  /* escape double quotes and backslashes in the realm and nonce as
+  /* escape double quotes and backslashes in the username, realm and nonce as
      necessary */
   qrealm = auth_digest_string_quoted(realm);
   qnonce = auth_digest_string_quoted(nonce);
-  if(qrealm && qnonce)
+  quserp = auth_digest_string_quoted(userp);
+  if(qrealm && qnonce && quserp)
     /* Generate the response */
     response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
                              "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\","
                              "response=%s,qop=%s",
-                             userp, qrealm, qnonce,
+                             quserp, qrealm, qnonce,
                              cnonce, nonceCount, spn, resp_hash_hex, qop);
 
   curlx_free(qrealm);
   curlx_free(qnonce);
+  curlx_free(quserp);
   curlx_free(spn);
   if(!response)
     return CURLE_OUT_OF_MEMORY;
@@ -669,7 +672,7 @@
  * uripath [in]     - The path of the HTTP uri.
  * digest  [in/out] - The digest data struct being used and modified.
  * outptr  [in/out] - The address where a pointer to newly allocated memory
- *                    holding the result will be stored upon completion.
+ *                    holding the result is stored upon completion.
  * outlen  [out]    - The length of the output message.
  *
  * Returns CURLE_OK on success.
@@ -853,8 +856,8 @@
      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
 
      Digest parameters are all quoted strings. Username which is provided by
-     the user will need double quotes and backslashes within it escaped.
-     realm, nonce, and opaque will need backslashes as well as they were
+     the user needs double quotes and backslashes within it escaped.
+     realm, nonce, and opaque needs backslashes as well as they were
      de-escaped when copied from request header. cnonce is generated with
      web-safe characters. uri is already percent encoded. nc is 8 hex
      characters. algorithm and qop with standard values only contain web-safe
@@ -977,7 +980,7 @@
  * uripath [in]     - The path of the HTTP uri.
  * digest  [in/out] - The digest data struct being used and modified.
  * outptr  [in/out] - The address where a pointer to newly allocated memory
- *                    holding the result will be stored upon completion.
+ *                    holding the result is stored upon completion.
  * outlen  [out]    - The length of the output message.
  *
  * Returns CURLE_OK on success.
@@ -1028,12 +1031,12 @@
  */
 void Curl_auth_digest_cleanup(struct digestdata *digest)
 {
-  Curl_safefree(digest->nonce);
-  Curl_safefree(digest->cnonce);
-  Curl_safefree(digest->realm);
-  Curl_safefree(digest->opaque);
-  Curl_safefree(digest->qop);
-  Curl_safefree(digest->algorithm);
+  curlx_safefree(digest->nonce);
+  curlx_safefree(digest->cnonce);
+  curlx_safefree(digest->realm);
+  curlx_safefree(digest->opaque);
+  curlx_safefree(digest->qop);
+  curlx_safefree(digest->algorithm);
 
   digest->nc = 0;
   digest->algo = ALGO_MD5; /* default algorithm */
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index f29e569..f0b6780 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -375,7 +375,7 @@
  * uripath [in]     - The path of the HTTP uri.
  * digest  [in/out] - The digest data struct being used and modified.
  * outptr  [in/out] - The address where a pointer to newly allocated memory
- *                    holding the result will be stored upon completion.
+ *                    holding the result is stored upon completion.
  * outlen  [out]    - The length of the output message.
  *
  * Returns CURLE_OK on success.
@@ -427,10 +427,10 @@
      (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) {
     if(digest->http_context) {
       Curl_pSecFn->DeleteSecurityContext(digest->http_context);
-      Curl_safefree(digest->http_context);
+      curlx_safefree(digest->http_context);
     }
-    Curl_safefree(digest->user);
-    Curl_safefree(digest->passwd);
+    curlx_safefree(digest->user);
+    curlx_safefree(digest->passwd);
   }
 
   if(digest->http_context) {
@@ -460,7 +460,7 @@
     else { /* delete the context so a new one can be made */
       infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status);
       Curl_pSecFn->DeleteSecurityContext(digest->http_context);
-      Curl_safefree(digest->http_context);
+      curlx_safefree(digest->http_context);
     }
   }
 
@@ -474,8 +474,8 @@
     TCHAR *spn;
 
     /* free the copy of user/passwd used to make the previous identity */
-    Curl_safefree(digest->user);
-    Curl_safefree(digest->passwd);
+    curlx_safefree(digest->user);
+    curlx_safefree(digest->passwd);
 
     if(userp && *userp) {
       /* Populate our identity structure */
@@ -515,7 +515,7 @@
       if(!digest->passwd) {
         curlx_free(output_token);
         Curl_sspi_free_identity(p_identity);
-        Curl_safefree(digest->user);
+        curlx_safefree(digest->user);
         return CURLE_OUT_OF_MEMORY;
       }
     }
@@ -595,7 +595,7 @@
       Curl_sspi_free_identity(p_identity);
       curlx_free(output_token);
 
-      Curl_safefree(digest->http_context);
+      curlx_safefree(digest->http_context);
 
       if(status == SEC_E_INSUFFICIENT_MEMORY)
         return CURLE_OUT_OF_MEMORY;
@@ -637,7 +637,7 @@
 void Curl_auth_digest_cleanup(struct digestdata *digest)
 {
   /* Free the input token */
-  Curl_safefree(digest->input_token);
+  curlx_safefree(digest->input_token);
 
   /* Reset any variables */
   digest->input_token_len = 0;
@@ -645,12 +645,12 @@
   /* Delete security context */
   if(digest->http_context) {
     Curl_pSecFn->DeleteSecurityContext(digest->http_context);
-    Curl_safefree(digest->http_context);
+    curlx_safefree(digest->http_context);
   }
 
   /* Free the copy of user/passwd used to make the identity for http_context */
-  Curl_safefree(digest->user);
-  Curl_safefree(digest->passwd);
+  curlx_safefree(digest->user);
+  curlx_safefree(digest->passwd);
 }
 
 #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index d6af18f..64c735b 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -33,7 +33,7 @@
 #include "curl_gssapi.h"
 #include "curl_trc.h"
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -320,7 +320,7 @@
   }
 }
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
 
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index c08b6a4..e7491be 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -444,8 +444,8 @@
   krb5->p_identity = NULL;
 
   /* Free the SPN and output token */
-  Curl_safefree(krb5->spn);
-  Curl_safefree(krb5->output_token);
+  curlx_safefree(krb5->spn);
+  curlx_safefree(krb5->output_token);
 
   /* Reset any variables */
   krb5->token_max = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index b50cfdd..1e485ed 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -152,7 +152,7 @@
 /* Indicates that 128-bit encryption is supported. */
 
 #define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1 << 30)
-/* Indicates that the client will provide an encrypted master key in
+/* Indicates that the client provides an encrypted master key in
    the "Session Key" field of the Type 3 message. */
 
 #define NTLMFLAG_NEGOTIATE_56                    (1 << 31)
@@ -424,7 +424,7 @@
                                              const char *userp,
                                              const char *passwdp,
                                              const char *service,
-                                             const char *hostname,
+                                             const char *host,
                                              struct ntlmdata *ntlm,
                                              struct bufref *out)
 {
@@ -445,7 +445,7 @@
   size_t size;
 
   char *ntlmbuf;
-  const char *host = "";              /* empty */
+  const char *hostname = "";          /* empty */
   const char *domain = "";            /* empty */
   size_t hostlen = 0;
   size_t domlen = 0;
@@ -456,7 +456,7 @@
   (void)userp;
   (void)passwdp;
   (void)service;
-  (void)hostname;
+  (void)host;
 
   /* Clean up any former leftovers and initialise to defaults */
   Curl_auth_cleanup_ntlm(ntlm);
@@ -490,7 +490,7 @@
                           SHORTPAIR(hostlen),
                           SHORTPAIR(hostoff),
                           0, 0,
-                          host,  /* this is empty */
+                          hostname, /* this is empty */
                           domain /* this is empty */);
 
   if(!ntlmbuf)
@@ -850,7 +850,7 @@
 void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
 {
   /* Free the target info */
-  Curl_safefree(ntlm->target_info);
+  curlx_safefree(ntlm->target_info);
 
   /* Reset any variables */
   ntlm->target_info_len = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index 12cf32b..4c41eb2 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -191,21 +191,21 @@
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
-                                             const struct bufref *type2,
+                                             const struct bufref *type2ref,
                                              struct ntlmdata *ntlm)
 {
   /* Ensure we have a valid type-2 message */
-  if(!Curl_bufref_len(type2)) {
+  if(!Curl_bufref_len(type2ref)) {
     infof(data, "NTLM handshake failure (empty type-2 message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
   /* Store the challenge for later use */
-  ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2),
-                                    Curl_bufref_len(type2));
+  ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2ref),
+                                    Curl_bufref_len(type2ref));
   if(!ntlm->input_token)
     return CURLE_OUT_OF_MEMORY;
-  ntlm->input_token_len = Curl_bufref_len(type2);
+  ntlm->input_token_len = Curl_bufref_len(type2ref);
 
   return CURLE_OK;
 }
@@ -255,7 +255,7 @@
   /* ssl context comes from schannel.
    * When extended protection is used in IIS server,
    * we have to pass a second SecBuffer to the SecBufferDesc
-   * otherwise IIS will not pass the authentication (401 response).
+   * otherwise IIS does not pass the authentication (401 response).
    * Minimum supported version is Windows 7.
    * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811
    */
@@ -341,13 +341,13 @@
   ntlm->p_identity = NULL;
 
   /* Free the input and output tokens */
-  Curl_safefree(ntlm->input_token);
-  Curl_safefree(ntlm->output_token);
+  curlx_safefree(ntlm->input_token);
+  curlx_safefree(ntlm->output_token);
 
   /* Reset any variables */
   ntlm->token_max = 0;
 
-  Curl_safefree(ntlm->spn);
+  curlx_safefree(ntlm->spn);
 }
 
 #endif /* USE_WINDOWS_SSPI && USE_NTLM */
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
index af8498b..38bb4c1 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -32,7 +32,7 @@
 #include "curl_gssapi.h"
 #include "curl_trc.h"
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
@@ -171,7 +171,7 @@
                                            NULL);
 
   /* Free the decoded challenge as it is not required anymore */
-  Curl_safefree(input_token.value);
+  curlx_safefree(input_token.value);
 
   nego->status = major_status;
   if(GSS_ERROR(major_status)) {
@@ -211,7 +211,7 @@
  * data        [in]     - The session handle.
  * nego        [in/out] - The Negotiate data struct being used and modified.
  * outptr      [in/out] - The address where a pointer to newly allocated memory
- *                        holding the result will be stored upon completion.
+ *                        holding the result is stored upon completion.
  * outlen      [out]    - The length of the output message.
  *
  * Returns CURLE_OK on success.
@@ -288,7 +288,7 @@
   nego->havemultiplerequests = FALSE;
 }
 
-#if defined(__GNUC__) && defined(__APPLE__)
+#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
 #pragma GCC diagnostic pop
 #endif
 
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
index 1f73123..1baf593 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -192,7 +192,7 @@
     /* ssl context comes from Schannel.
      * When extended protection is used in IIS server,
      * we have to pass a second SecBuffer to the SecBufferDesc
-     * otherwise IIS will not pass the authentication (401 response).
+     * otherwise IIS does not pass the authentication (401 response).
      * Minimum supported version is Windows 7.
      * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811
      */
@@ -278,7 +278,7 @@
  * data        [in]     - The session handle.
  * nego        [in/out] - The Negotiate data struct being used and modified.
  * outptr      [in/out] - The address where a pointer to newly allocated memory
- *                        holding the result will be stored upon completion.
+ *                        holding the result is stored upon completion.
  * outlen      [out]    - The length of the output message.
  *
  * Returns CURLE_OK on success.
@@ -329,8 +329,8 @@
   nego->p_identity = NULL;
 
   /* Free the SPN and output token */
-  Curl_safefree(nego->spn);
-  Curl_safefree(nego->output_token);
+  curlx_safefree(nego->spn);
+  curlx_safefree(nego->output_token);
 
   /* Reset any variables */
   nego->status = 0;
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index 3e66c89..279da60 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -117,6 +117,7 @@
 /* This is used to clean up the digest specific data */
 void Curl_auth_digest_cleanup(struct digestdata *digest);
 #else
+#define Curl_auth_digest_cleanup(x)
 #define Curl_auth_is_digest_supported()       FALSE
 #endif /* !CURL_DISABLE_DIGEST_AUTH */
 
@@ -199,7 +200,7 @@
                                              const char *userp,
                                              const char *passwdp,
                                              const char *service,
-                                             const char *hostname,
+                                             const char *host,
                                              struct ntlmdata *ntlm,
                                              struct bufref *out);
 
@@ -263,7 +264,7 @@
                                               const char *passwdp,
                                               const char *service,
                                               const char *host,
-                                              const bool mutual,
+                                              const bool mutual_auth,
                                               const struct bufref *chlg,
                                               struct kerberos5data *krb5,
                                               struct bufref *out);
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index ff9aed0..a3e69ac 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -46,23 +46,18 @@
 #include <libpsl.h>
 #endif
 
-#ifdef USE_LIBRTMP
-#include <librtmp/rtmp.h>
-#include "curl_rtmp.h"
-#endif
-
 #ifdef HAVE_LIBZ
 #include <cm3p/zlib.h>
 #endif
 
 #ifdef HAVE_BROTLI
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 /* Ignore -Wvla warnings in brotli headers */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wvla"
 #endif
 #include <brotli/decode.h>
-#if defined(__GNUC__) || defined(__clang__)
+#ifdef CURL_HAVE_DIAG
 #pragma GCC diagnostic pop
 #endif
 #endif
@@ -178,9 +173,6 @@
 #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
   char h3_version[30];
 #endif
-#ifdef USE_LIBRTMP
-  char rtmp_version[30];
-#endif
 #ifdef USE_GSASL
   char gsasl_buf[30];
 #endif
@@ -244,10 +236,6 @@
   Curl_quic_ver(h3_version, sizeof(h3_version));
   src[i++] = h3_version;
 #endif
-#ifdef USE_LIBRTMP
-  Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
-  src[i++] = rtmp_version;
-#endif
 #ifdef USE_GSASL
   curl_msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s",
                  gsasl_check_version(NULL));
@@ -255,11 +243,9 @@
 #endif
 #ifdef HAVE_GSSAPI
 #ifdef HAVE_GSSGNU
-  curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s",
-                 GSS_VERSION);
+  curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", GSS_VERSION);
 #elif defined(CURL_KRB5_VERSION)
-  curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s",
-                 CURL_KRB5_VERSION);
+  curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", CURL_KRB5_VERSION);
 #else
   curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5");
 #endif
@@ -350,14 +336,6 @@
 #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
   "pop3s",
 #endif
-#ifdef USE_LIBRTMP
-  "rtmp",
-  "rtmpe",
-  "rtmps",
-  "rtmpt",
-  "rtmpte",
-  "rtmpts",
-#endif
 #ifndef CURL_DISABLE_RTSP
   "rtsp",
 #endif
@@ -365,7 +343,7 @@
   "scp",
   "sftp",
 #endif
-#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
+#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE)
   "smb",
 #  ifdef USE_SSL
   "smbs",
@@ -451,8 +429,8 @@
 #ifndef CURL_DISABLE_ALTSVC
   FEATURE("alt-svc",     NULL,                CURL_VERSION_ALTSVC),
 #endif
-#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR)
-  FEATURE("asyn-rr", NULL,             0),
+#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) && defined(USE_HTTPSRR)
+  FEATURE("asyn-rr",     NULL,                0),
 #endif
 #ifdef CURLRES_ASYNCH
   FEATURE("AsynchDNS",   NULL,                CURL_VERSION_ASYNCHDNS),
@@ -694,13 +672,5 @@
   feature_names[n] = NULL;  /* Terminate array. */
   version_info.features = features;
 
-#ifdef USE_LIBRTMP
-  {
-    static char rtmp_version[30];
-    Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
-    version_info.rtmp_version = rtmp_version;
-  }
-#endif
-
   return &version_info;
 }
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
index 48905d3..8cf3886 100644
--- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -52,6 +52,7 @@
 #include "rand.h"
 #include "multiif.h"
 #include "cfilters.h"
+#include "cf-dns.h"
 #include "cf-socket.h"
 #include "connect.h"
 #include "progress.h"
@@ -64,7 +65,6 @@
 #include "vquic/vquic.h"
 #include "vquic/vquic_int.h"
 #include "vquic/vquic-tls.h"
-#include "vtls/keylog.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_scache.h"
 #include "vquic/curl_ngtcp2.h"
@@ -183,7 +183,7 @@
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   const ngtcp2_transport_params *rp;
   /* Peer should have sent us its transport parameters. If it
-   * announces a positive `max_idle_timeout` it will close the
+   * announces a positive `max_idle_timeout` it closes the
    * connection when it does not hear from us for that time.
    *
    * Some servers use this as a keep-alive timer at a rather low
@@ -789,6 +789,7 @@
   }
 }
 
+/* for ngtcp2 <v1.22.0 */
 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
                                     uint8_t *token, size_t cidlen,
                                     void *user_data)
@@ -809,6 +810,27 @@
   return 0;
 }
 
+#ifdef NGTCP2_CALLBACKS_V3  /* ngtcp2 v1.22.0+ */
+static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid,
+  struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data)
+{
+  CURLcode result;
+  (void)tconn;
+  (void)user_data;
+
+  result = Curl_rand(NULL, cid->data, cidlen);
+  if(result)
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  cid->datalen = cidlen;
+
+  result = Curl_rand(NULL, token->data, sizeof(token->data));
+  if(result)
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+
+  return 0;
+}
+#endif
+
 static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
                           void *user_data)
 {
@@ -852,7 +874,7 @@
   cb_extend_max_local_streams_bidi,
   NULL, /* extend_max_local_streams_uni */
   cb_rand,
-  cb_get_new_connection_id,
+  cb_get_new_connection_id, /* for ngtcp2 <v1.22.0 */
   NULL, /* remove_connection_id */
   ngtcp2_crypto_update_key_cb, /* update_key */
   NULL, /* path_validation */
@@ -878,6 +900,12 @@
 #ifdef NGTCP2_CALLBACKS_V2  /* ngtcp2 v1.14.0+ */
   NULL, /* begin_path_validation */
 #endif
+#ifdef NGTCP2_CALLBACKS_V3  /* ngtcp2 v1.22.0+ */
+  NULL, /* recv_stateless_reset2 */
+  cb_get_new_connection_id2, /* get_new_connection_id2 */
+  NULL, /* dcid_status2 */
+  ngtcp2_crypto_get_path_challenge_data2_cb, /* get_path_challenge_data2 */
+#endif
 };
 
 #if defined(_MSC_VER) && defined(_DLL)
@@ -1113,7 +1141,7 @@
   if(stream->rx_offset_max < stream->rx_offset)
     stream->rx_offset_max = stream->rx_offset;
 
-  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRId64,
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64,
               stream->id, blen, stream->rx_offset_max - stream->rx_offset);
   cf_ngtcp2_upd_rx_win(cf, data, stream);
   return 0;
@@ -1461,8 +1489,8 @@
   result = CURLE_AGAIN;
 
 out:
-  result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx));
-  result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx));
+  result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx));
+  result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx));
 denied:
   CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu",
               stream ? stream->id : -1, blen, result, *pnread);
@@ -1559,7 +1587,7 @@
   }
 
   CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
-              "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
+              "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")",
               stream->id, (int)nvecs,
               *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "",
               nwritten, Curl_bufq_len(&stream->sendbuf),
@@ -1788,7 +1816,7 @@
   result = cf_progress_egress(cf, data, &pktx);
 
 out:
-  result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx));
+  result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx));
 denied:
   CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu",
               stream ? stream->id : -1, len, result, *pnwritten);
@@ -2018,7 +2046,7 @@
    * This is called PMTUD (Path Maximum Transmission Unit Discovery).
    * Since a PMTUD might be rejected right on send, we do not want it
    * be followed by other packets of lesser size. Because those would
-   * also fail then. So, if we detect a PMTUD while buffering, we flush.
+   * also fail then. If we detect a PMTUD while buffering, we flush.
    */
   max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn);
   path_max_payload_size =
@@ -2043,7 +2071,7 @@
       ++pktcnt;
       if(pktcnt == 1) {
         /* first packet in buffer. This is either of a known, "good"
-         * payload size or it is a PMTUD. We will see. */
+         * payload size or it is a PMTUD. We shall see. */
         gsolen = nread;
       }
       else if(nread > gsolen ||
@@ -2216,8 +2244,8 @@
       (uint8_t *)buffer, sizeof(buffer),
       &ctx->last_error, pktx.ts);
     CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
-                PRIu64 ") -> %d", ctx->last_error.type,
-                ctx->last_error.error_code, (int)nwritten);
+                PRIu64 ") -> %zd", ctx->last_error.type,
+                ctx->last_error.error_code, (ssize_t)nwritten);
     /* there are cases listed in ngtcp2 documentation where this call
      * may fail. Since we are doing a connection shutdown as graceful
      * as we can, such an error is ignored here. */
@@ -2257,7 +2285,7 @@
 
   if(Curl_bufq_is_empty(&ctx->q.sendbuf)) {
     /* Sent everything off. ngtcp2 seems to have no support for graceful
-     * shutdowns. So, we are done. */
+     * shutdowns. We are done. */
     CURL_TRC_CF(data, cf, "shutdown completely sent off, done");
     *done = TRUE;
     result = CURLE_OK;
@@ -2384,7 +2412,7 @@
     DEBUGASSERT(data);
     if(!data)
       return 0;
-    CURL_TRC_CF(data, cf, "SSL message: %s %s [%d]",
+    CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]",
                 incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype);
     switch(htype) {
     case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
@@ -2567,6 +2595,18 @@
   return result;
 }
 
+static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL
+  return Curl_ossl_need_httpsrr(data);
+#elif defined(USE_WOLFSSL)
+  return Curl_wssl_need_httpsrr(data);
+#else
+  (void)data;
+  return FALSE;
+#endif
+}
+
 /*
  * Might be called twice for happy eyeballs.
  */
@@ -2681,8 +2721,14 @@
   }
 
   *done = FALSE;
-  pktx_init(&pktx, cf, data);
 
+  if(cf_ngtcp2_need_httpsrr(data) &&
+     !Curl_conn_dns_resolved_https(data, cf->sockindex)) {
+    CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
+    return CURLE_OK;
+  }
+
+  pktx_init(&pktx, cf, data);
   CF_DATA_SAVE(save, cf, data);
 
   if(!ctx->qconn) {
@@ -2859,7 +2905,7 @@
     goto out;
 
   /* We do not announce a max idle timeout, but when the peer does
-   * it will close the connection when it expires. */
+   * it closes the connection when it expires. */
   rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
   if(rp && rp->max_idle_timeout) {
     timediff_t idletime_ms =
@@ -2913,7 +2959,7 @@
 CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
                                struct Curl_easy *data,
                                struct connectdata *conn,
-                               const struct Curl_addrinfo *ai)
+                               struct Curl_sockaddr_ex *addr)
 {
   struct cf_ngtcp2_ctx *ctx = NULL;
   struct Curl_cfilter *cf = NULL;
@@ -2931,7 +2977,7 @@
     goto out;
   cf->conn = conn;
 
-  result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC);
+  result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
   if(result)
     goto out;
   cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
index 1a87faf..185272a 100644
--- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
@@ -53,7 +53,7 @@
 CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
                                struct Curl_easy *data,
                                struct connectdata *conn,
-                               const struct Curl_addrinfo *ai);
+                               struct Curl_sockaddr_ex *addr);
 #endif
 
 #endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
index d8e6b30..73f664a 100644
--- a/Utilities/cmcurl/lib/vquic/curl_quiche.c
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -32,6 +32,7 @@
 #include "uint-hash.h"
 #include "urldata.h"
 #include "cfilters.h"
+#include "cf-dns.h"
 #include "cf-socket.h"
 #include "curl_trc.h"
 #include "rand.h"
@@ -48,7 +49,6 @@
 #include "url.h"
 #include "bufref.h"
 #include "vtls/openssl.h"
-#include "vtls/keylog.h"
 #include "vtls/vtls.h"
 
 /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */
@@ -481,7 +481,7 @@
     return;
 
   /* Even when the transfer has already errored, we need to receive
-   * the data from quiche, as quiche will otherwise get stuck and
+   * the data from quiche, as quiche otherwise gets stuck and
    * raise events to receive over and over again. */
   cb_ctx.cf = cf;
   cb_ctx.data = data;
@@ -779,7 +779,7 @@
       else
         failf(data, "connection closed by server");
       /* Connection timed out, expire all transfers belonging to it
-       * as will not get any more POLL events here. */
+       * as it does not get any more POLL events here. */
       cf_quiche_expire_conn_closed(cf, data);
       return CURLE_SEND_ERROR;
     }
@@ -918,7 +918,7 @@
     result = CURLE_AGAIN;
 
 out:
-  result = Curl_1st_err(result, cf_flush_egress(cf, data));
+  result = Curl_1st_fatal(result, cf_flush_egress(cf, data));
   if(*pnread > 0)
     ctx->data_recvd += *pnread;
   CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%"
@@ -939,8 +939,8 @@
   rv = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
                            (uint8_t *)CURL_UNCONST(buf), len, eos);
   if(rv == QUICHE_H3_ERR_DONE || (rv == 0 && len > 0)) {
-    /* Blocked on flow control and should HOLD sending. But when do we open
-     * again? */
+    /* Blocked on flow control and should HOLD sending.
+       When do we open again? */
     if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
       CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) "
                   "-> window exhausted", stream->id, len);
@@ -1051,7 +1051,7 @@
       goto out;
     }
     else {
-      CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRIu64,
+      CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64,
                   Curl_bufref_ptr(&data->state.url), rv);
     }
     result = CURLE_SEND_ERROR;
@@ -1144,7 +1144,7 @@
   }
 
 out:
-  result = Curl_1st_err(result, cf_flush_egress(cf, data));
+  result = Curl_1st_fatal(result, cf_flush_egress(cf, data));
 
   CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu",
               stream ? stream->id : (uint64_t)~0, len,
@@ -1152,8 +1152,8 @@
   return result;
 }
 
-static bool stream_is_writeable(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
+static bool stream_is_writable(struct Curl_cfilter *cf,
+                               struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
@@ -1181,7 +1181,7 @@
     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->opened &&
-                (stream->quic_flow_blocked || !stream_is_writeable(cf, data));
+                (stream->quic_flow_blocked || !stream_is_writable(cf, data));
     want_recv = (want_recv || c_exhaust || s_exhaust);
     want_send = (!s_exhaust && want_send) ||
                  !Curl_bufq_is_empty(&ctx->q.sendbuf);
@@ -1379,6 +1379,12 @@
   }
 
   *done = FALSE;
+  if(Curl_ossl_need_httpsrr(data) &&
+     !Curl_conn_dns_resolved_https(data, cf->sockindex)) {
+    CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
+    return CURLE_OK;
+  }
+
   vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data));
 
   if(!ctx->qconn) {
@@ -1634,7 +1640,7 @@
 CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
                                struct Curl_easy *data,
                                struct connectdata *conn,
-                               const struct Curl_addrinfo *ai)
+                               struct Curl_sockaddr_ex *addr)
 {
   struct cf_quiche_ctx *ctx = NULL;
   struct Curl_cfilter *cf = NULL;
@@ -1652,7 +1658,7 @@
     goto out;
   cf->conn = conn;
 
-  result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC);
+  result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
   if(result)
     goto out;
   cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h
index fb5a563..c2c88dd 100644
--- a/Utilities/cmcurl/lib/vquic/curl_quiche.h
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h
@@ -38,7 +38,7 @@
 CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
                                struct Curl_easy *data,
                                struct connectdata *conn,
-                               const struct Curl_addrinfo *ai);
+                               struct Curl_sockaddr_ex *addr);
 
 #endif
 
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c
index e135ac5..d81f2a9 100644
--- a/Utilities/cmcurl/lib/vquic/vquic-tls.c
+++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -45,7 +45,6 @@
 
 #include "urldata.h"
 #include "cfilters.h"
-#include "vtls/keylog.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_scache.h"
 #include "vquic/vquic-tls.h"
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h
index 33adec2..c461c15 100644
--- a/Utilities/cmcurl/lib/vquic/vquic-tls.h
+++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h
@@ -72,7 +72,7 @@
  * @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 peer             the peer to be connected to
  * @param alpns            the ALPN specifications to negotiate, may be NULL
  * @param cb_setup         optional callback for early TLS config
  * @param cb_user_data     user_data param for callback
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index 1eaabb9..0040ad6 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -702,20 +702,20 @@
 CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
                              struct connectdata *conn,
-                             const struct Curl_addrinfo *ai,
+                             struct Curl_sockaddr_ex *addr,
                              uint8_t transport)
 {
   (void)transport;
   DEBUGASSERT(transport == TRNSPRT_QUIC);
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
-  return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
+  return Curl_cf_ngtcp2_create(pcf, data, conn, addr);
 #elif defined(USE_QUICHE)
-  return Curl_cf_quiche_create(pcf, data, conn, ai);
+  return Curl_cf_quiche_create(pcf, data, conn, addr);
 #else
   *pcf = NULL;
   (void)data;
   (void)conn;
-  (void)ai;
+  (void)addr;
   return CURLE_NOT_BUILT_IN;
 #endif
 }
@@ -725,7 +725,7 @@
                              unsigned char transport)
 {
   if(transport == TRNSPRT_UNIX) {
-    /* cannot do QUIC over a Unix domain socket */
+    failf(data, "HTTP/3 cannot be used over UNIX domain sockets");
     return CURLE_QUIC_CONNECT_ERROR;
   }
   if(!(conn->scheme->flags & PROTOPT_SSL)) {
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h
index 528e184..1f0a1ab 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.h
+++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -42,7 +42,7 @@
 CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
                              struct connectdata *conn,
-                             const struct Curl_addrinfo *ai,
+                             struct Curl_sockaddr_ex *addr,
                              uint8_t transport);
 
 extern struct Curl_cftype Curl_cft_http3;
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index 3a2a52e..44094e4 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -147,7 +147,7 @@
     rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen);
     if(rc != SSH_OK || hlen != 16) {
       failf(data,
-            "Denied establishing ssh session: md5 fingerprint not available");
+            "Denied establishing ssh session: MD5 fingerprint not available");
       goto cleanup;
     }
 
@@ -158,7 +158,7 @@
 
     if(!curl_strequal(md5buffer, pubkey_md5)) {
       failf(data,
-            "Denied establishing ssh session: mismatch md5 fingerprint. "
+            "Denied establishing ssh session: mismatch MD5 fingerprint. "
             "Remote %s is not equal to %s", md5buffer, pubkey_md5);
       rc = SSH_ERROR;
       goto cleanup;
@@ -357,6 +357,8 @@
                                       struct ssh_conn *sshc,
                                       struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
   Curl_pgrsSetDownloadSize(data, -1);
   if(data->req.no_body) {
     myssh_to(data, sshc, SSH_STOP);
@@ -384,6 +386,8 @@
 {
   CURLcode result = CURLE_OK;
 
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
   curlx_dyn_reset(&sshc->readdir_buf);
   if(sshc->readdir_attrs)
     sftp_attributes_free(sshc->readdir_attrs);
@@ -480,7 +484,7 @@
     sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
   }
 
-  Curl_safefree(sshc->readdir_linkPath);
+  curlx_safefree(sshc->readdir_linkPath);
 
   if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", sshc->readdir_filename)) {
     /* Not using:
@@ -519,8 +523,10 @@
   ssh_string_free_char(sshc->readdir_tmp);
   sshc->readdir_tmp = NULL;
 
-  if(result)
+  if(result) {
     myssh_to(data, sshc, SSH_STOP);
+    sshc->actualcode = result;
+  }
   else
     myssh_to(data, sshc, SSH_SFTP_READDIR);
   return SSH_NO_ERROR;
@@ -544,8 +550,8 @@
   if(cmd)
     failf(data, "%s command failed: %s", cmd,
           ssh_get_error(sshc->ssh_session));
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   myssh_to(data, sshc, SSH_SFTP_CLOSE);
   sshc->nextstate = SSH_NO_STATE;
   sshc->actualcode = CURLE_QUOTE_ERROR;
@@ -564,6 +570,8 @@
   else if(statvfs) {
 #ifdef _MSC_VER
 #define CURL_LIBSSH_VFS_SIZE_MASK "I64u"
+#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 6)
+#define CURL_LIBSSH_VFS_SIZE_MASK "llu"
 #else
 #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64
 #endif
@@ -696,8 +704,8 @@
     int dir = ssh_get_poll_flags(sshc->ssh_session);
     /* translate the libssh define bits into our own bit defines */
     sshc->waitfor =
-      ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) |
-      ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0);
+      ((dir & SSH_READ_PENDING) ? REQ_IO_RECV : 0) |
+      ((dir & SSH_WRITE_PENDING) ? REQ_IO_SEND : 0);
   }
   else
     sshc->waitfor = 0;
@@ -955,6 +963,9 @@
   int flags;
   int rc = 0;
 
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
   if(data->state.resume_from) {
     sftp_attributes attrs;
 
@@ -1084,7 +1095,7 @@
   /* upload data */
   Curl_xfer_setup_send(data, FIRSTSOCKET);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->recv_idx = FIRSTSOCKET;
 
   /* since we do not really wait for anything at this point, we want the
@@ -1101,6 +1112,8 @@
                                        struct ssh_conn *sshc,
                                        struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
   /* Work on getting the specified file */
   if(sshc->sftp_file)
     sftp_close(sshc->sftp_file);
@@ -1205,7 +1218,7 @@
   }
   Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->send_idx = 0;
 
   sshc->sftp_recv_state = 0;
@@ -1218,11 +1231,13 @@
                                struct ssh_conn *sshc,
                                struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
   if(sshc->sftp_file) {
     sftp_close(sshc->sftp_file);
     sshc->sftp_file = NULL;
   }
-  Curl_safefree(sshp->path);
+  curlx_safefree(sshp->path);
 
   CURL_TRC_SSH(data, "SFTP DONE done");
 
@@ -1306,8 +1321,8 @@
 
   /* This is the last step in the SFTP connect phase. Do note that while
      we get the homedir here, we get the "workingpath" in the DO action
-     since the homedir will remain the same between request but the
-     working path will not. */
+     since the homedir remains the same between request but the
+     working path does not. */
   CURL_TRC_SSH(data, "CONNECT phase done");
   myssh_to(data, sshc, SSH_STOP);
   return SSH_NO_ERROR;
@@ -1317,7 +1332,12 @@
                                     struct ssh_conn *sshc,
                                     struct SSHPROTO *sshp)
 {
-  CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+  CURLcode result;
+
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
   if(result) {
     sshc->actualcode = result;
     myssh_to(data, sshc, SSH_STOP);
@@ -1346,12 +1366,12 @@
   return SSH_NO_ERROR;
 }
 
-static int return_quote_error(struct Curl_easy *data,
-                              struct ssh_conn *sshc)
+static int quote_error(struct Curl_easy *data,
+                       struct ssh_conn *sshc)
 {
   failf(data, "Suspicious data after the command line");
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   myssh_to(data, sshc, SSH_SFTP_CLOSE);
   sshc->nextstate = SSH_NO_STATE;
   sshc->actualcode = CURLE_QUOTE_ERROR;
@@ -1371,9 +1391,12 @@
   const char *cmd = sshc->quote_item->data;
   sshc->acceptfail = FALSE;
 
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
   /* if a command starts with an asterisk, which a legal SFTP command never
-     can, the command will be allowed to fail without it causing any
-     aborts or cancels etc. It will cause libcurl to act as if the command
+     can, the command is allowed to fail without it causing any
+     aborts or cancels etc. It causes libcurl to act as if the command
      is successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
@@ -1460,14 +1483,14 @@
       else
         failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
               "Bad second parameter");
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       myssh_to(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return SSH_NO_ERROR;
     }
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     sshc->quote_attrs = NULL;
     myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT);
     return SSH_NO_ERROR;
@@ -1483,20 +1506,20 @@
         failf(data, "Out of memory");
       else
         failf(data, "Syntax error in ln/symlink: Bad second parameter");
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       myssh_to(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return SSH_NO_ERROR;
     }
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
     return SSH_NO_ERROR;
   }
   else if(!strncmp(cmd, "mkdir ", 6)) {
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     /* create directory */
     myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR);
     return SSH_NO_ERROR;
@@ -1511,42 +1534,42 @@
         failf(data, "Out of memory");
       else
         failf(data, "Syntax error in rename: Bad second parameter");
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       myssh_to(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return SSH_NO_ERROR;
     }
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME);
     return SSH_NO_ERROR;
   }
   else if(!strncmp(cmd, "rmdir ", 6)) {
     /* delete directory */
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR);
     return SSH_NO_ERROR;
   }
   else if(!strncmp(cmd, "rm ", 3)) {
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK);
     return SSH_NO_ERROR;
   }
 #ifdef HAS_STATVFS_SUPPORT
   else if(!strncmp(cmd, "statvfs ", 8)) {
     if(*cp)
-      return return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS);
     return SSH_NO_ERROR;
   }
 #endif
 
   failf(data, "Unknown SFTP command");
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   myssh_to(data, sshc, SSH_SFTP_CLOSE);
   sshc->nextstate = SSH_NO_STATE;
   sshc->actualcode = CURLE_QUOTE_ERROR;
@@ -1556,8 +1579,8 @@
 static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data,
                                     struct ssh_conn *sshc)
 {
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
 
   sshc->quote_item = sshc->quote_item->next;
 
@@ -1583,8 +1606,8 @@
   sshc->acceptfail = FALSE;
 
   /* if a command starts with an asterisk, which a legal SFTP command never
-     can, the command will be allowed to fail without it causing any
-     aborts or cancels etc. It will cause libcurl to act as if the command
+     can, the command is allowed to fail without it causing any
+     aborts or cancels etc. It causes libcurl to act as if the command
      is successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
@@ -1659,9 +1682,6 @@
       myssh_quote_error(data, sshc, NULL);
       return SSH_NO_ERROR;
     }
-    if(date > UINT_MAX)
-      /* because the liubssh API cannot deal with a larger value */
-      date = UINT_MAX;
     if(!strncmp(cmd, "atime", 5))
       sshc->quote_attrs->atime = (uint32_t)date;
     else /* mtime */
@@ -1736,7 +1756,7 @@
   data->req.maxdownload = bytecount;
   Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->send_idx = 0;
 
   myssh_to(data, sshc, SSH_STOP);
@@ -1830,30 +1850,348 @@
       sshc->pubkey = NULL;
     }
 
-    Curl_safefree(sshc->rsa_pub);
-    Curl_safefree(sshc->rsa);
-    Curl_safefree(sshc->quote_path1);
-    Curl_safefree(sshc->quote_path2);
+    curlx_safefree(sshc->rsa_pub);
+    curlx_safefree(sshc->rsa);
+    curlx_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path2);
     curlx_dyn_free(&sshc->readdir_buf);
-    Curl_safefree(sshc->readdir_linkPath);
+    curlx_safefree(sshc->readdir_linkPath);
     SSH_STRING_FREE_CHAR(sshc->homedir);
     sshc->initialised = FALSE;
   }
 }
 
+static int myssh_in_SFTP_QUOTE_SETSTAT(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  int rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
+                        sshc->quote_attrs);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "setstat");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_QUOTE_SYMLINK(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  int rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
+                        sshc->quote_path1);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "symlink");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_QUOTE_MKDIR(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  int rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
+                      (mode_t)data->set.new_directory_perms);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "mkdir");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_QUOTE_RENAME(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
+{
+  int rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
+                       sshc->quote_path2);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "rename");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_QUOTE_RMDIR(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  int rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "rmdir");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_QUOTE_UNLINK(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
+{
+  int rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
+  if(rc == SSH_AGAIN)
+    return rc;
+  if(rc && !sshc->acceptfail) {
+    myssh_quote_error(data, sshc, "rm");
+    return rc;
+  }
+  myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_GETINFO(struct Curl_easy *data,
+                                 struct ssh_conn *sshc)
+{
+  if(data->set.get_filetime)
+    myssh_to(data, sshc, SSH_SFTP_FILETIME);
+  else
+    myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_FILETIME(struct Curl_easy *data,
+                                  struct ssh_conn *sshc,
+                                  struct SSHPROTO *sshp)
+{
+  sftp_attributes attrs;
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  attrs = sftp_stat(sshc->sftp_session, sshp->path);
+  if(attrs) {
+    data->info.filetime = attrs->mtime;
+    sftp_attributes_free(attrs);
+  }
+
+  myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_TRANS_INIT(struct Curl_easy *data,
+                                    struct ssh_conn *sshc,
+                                    struct SSHPROTO *sshp)
+{
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+  if(data->state.upload)
+    myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  else {
+    size_t path_len = strlen(sshp->path);
+
+    if(path_len && sshp->path[path_len - 1] == '/')
+      myssh_to(data, sshc, SSH_SFTP_READDIR_INIT);
+    else
+      myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
+  }
+
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_CREATE_DIRS_INIT(struct Curl_easy *data,
+                                          struct ssh_conn *sshc,
+                                          struct SSHPROTO *sshp)
+{
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  if(strlen(sshp->path) > 1) {
+    sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+    myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
+  }
+  else {
+    myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  }
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_CREATE_DIRS(struct Curl_easy *data,
+                                     struct ssh_conn *sshc,
+                                     struct SSHPROTO *sshp)
+{
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  sshc->slash_pos = strchr(sshc->slash_pos, '/');
+  if(sshc->slash_pos) {
+    *sshc->slash_pos = 0;
+
+    infof(data, "Creating directory '%s'", sshp->path);
+    myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
+    return SSH_NO_ERROR;
+  }
+  myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SFTP_CREATE_DIRS_MKDIR(struct Curl_easy *data,
+                                           struct ssh_conn *sshc,
+                                           struct SSHPROTO *sshp)
+{
+  int rc;
+  int err;
+  /* 'mode' - parameter is preliminary - default to 0644 */
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  rc = sftp_mkdir(sshc->sftp_session, sshp->path,
+                  (mode_t)data->set.new_directory_perms);
+  if(rc == SSH_AGAIN)
+    return rc;
+  *sshc->slash_pos = '/';
+  ++sshc->slash_pos;
+  if(rc < 0) {
+    /*
+     * Abort if failure was not that the directory already
+     * exists or the permission was denied (creation might
+     * succeed further down the path) - retry on unspecific
+     * FAILURE also
+     */
+    err = sftp_get_error(sshc->sftp_session);
+    if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
+       (err != SSH_FX_FAILURE) &&
+       (err != SSH_FX_PERMISSION_DENIED)) {
+      return myssh_to_SFTP_CLOSE(data, sshc);
+    }
+    rc = 0; /* clear rc and continue */
+  }
+  myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
+  return rc;
+}
+
+static int myssh_in_SCP_UPLOAD_INIT(struct Curl_easy *data,
+                                    struct ssh_conn *sshc,
+                                    struct SSHPROTO *sshp)
+{
+  int rc;
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  rc = ssh_scp_init(sshc->scp_session);
+  if(rc != SSH_OK) {
+    const char *err_msg = ssh_get_error(sshc->ssh_session);
+    failf(data, "%s", err_msg);
+    return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
+  }
+
+  rc = ssh_scp_push_file64(sshc->scp_session, sshp->path,
+                           (uint64_t)data->state.infilesize,
+                           (int)data->set.new_file_perms);
+
+  if(rc != SSH_OK) {
+    const char *err_msg = ssh_get_error(sshc->ssh_session);
+    failf(data, "%s", err_msg);
+    return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
+  }
+
+  /* upload data */
+  Curl_xfer_setup_send(data, FIRSTSOCKET);
+
+  /* not set by Curl_xfer_setup to preserve io_flags */
+  data->conn->recv_idx = FIRSTSOCKET;
+
+  myssh_to(data, sshc, SSH_STOP);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SCP_DOWNLOAD_INIT(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
+{
+  int rc = ssh_scp_init(sshc->scp_session);
+  if(rc != SSH_OK) {
+    const char *err_msg = ssh_get_error(sshc->ssh_session);
+    failf(data, "%s", err_msg);
+    return myssh_to_ERROR(data, sshc,
+                          CURLE_COULDNT_CONNECT);
+  }
+  myssh_to(data, sshc, SSH_SCP_DOWNLOAD);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SCP_DONE(struct Curl_easy *data,
+                             struct ssh_conn *sshc)
+{
+  if(data->state.upload)
+    myssh_to(data, sshc, SSH_SCP_SEND_EOF);
+  else
+    myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SCP_SEND_EOF(struct Curl_easy *data,
+                                 struct ssh_conn *sshc)
+{
+  if(sshc->scp_session) {
+    int rc = ssh_scp_close(sshc->scp_session);
+    if(rc == SSH_AGAIN) {
+      /* Currently the ssh_scp_close handles waiting for
+       * EOF in blocking way.
+       */
+      return SSH_AGAIN;
+    }
+    if(rc != SSH_OK) {
+      infof(data,
+            "Failed to close libssh scp channel: %s",
+            ssh_get_error(sshc->ssh_session));
+    }
+  }
+
+  myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+  return SSH_NO_ERROR;
+}
+
+static int myssh_in_SCP_CHANNEL_FREE(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  if(sshc->scp_session) {
+    ssh_scp_free(sshc->scp_session);
+    sshc->scp_session = NULL;
+  }
+  CURL_TRC_SSH(data, "SCP DONE phase complete");
+
+  ssh_set_blocking(sshc->ssh_session, 0);
+
+  myssh_to(data, sshc, SSH_SESSION_DISCONNECT);
+  return SSH_NO_ERROR;
+}
+
+static CURLcode myssh_in_SESSION_FREE(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
+{
+  CURLcode result;
+  sshc_cleanup(sshc);
+  /* the code we are about to return */
+  result = sshc->actualcode;
+  memset(sshc, 0, sizeof(struct ssh_conn));
+  connclose(data->conn, "SSH session free");
+  sshc->state = SSH_SESSION_FREE;   /* current */
+  sshc->nextstate = SSH_NO_STATE;
+  myssh_to(data, sshc, SSH_STOP);
+  return result;
+}
+
 /*
- * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * myssh_statemachine() runs the SSH state machine as far as it can without
  * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh function returns SSH_AGAIN
+ * to is set to TRUE if the libssh function returns SSH_AGAIN
  * meaning it wants to be called again when the socket is ready
  */
-static CURLcode myssh_statemach_act(struct Curl_easy *data,
-                                    struct ssh_conn *sshc,
-                                    struct SSHPROTO *sshp,
-                                    bool *block)
+static CURLcode myssh_statemachine(struct Curl_easy *data,
+                                   struct ssh_conn *sshc,
+                                   struct SSHPROTO *sshp,
+                                   bool *block)
 {
   CURLcode result = CURLE_OK;
-  int rc = SSH_NO_ERROR, err;
+  int rc = SSH_NO_ERROR;
 
   *block = FALSE;                   /* we are not blocking by default */
   do {
@@ -1909,14 +2247,13 @@
       rc = myssh_in_SFTP_REALPATH(data, sshc);
       break;
     case SSH_SFTP_QUOTE_INIT:
-      rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) :
-        CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp);
       break;
     case SSH_SFTP_POSTQUOTE_INIT:
       rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc);
       break;
     case SSH_SFTP_QUOTE:
-      rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_QUOTE(data, sshc, sshp);
       break;
     case SSH_SFTP_NEXT_QUOTE:
       rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc);
@@ -1925,186 +2262,52 @@
       rc = myssh_in_SFTP_QUOTE_STAT(data, sshc);
       break;
     case SSH_SFTP_QUOTE_SETSTAT:
-      rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
-                        sshc->quote_attrs);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "setstat");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_SETSTAT(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_SYMLINK:
-      rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
-                        sshc->quote_path1);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "symlink");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_SYMLINK(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_MKDIR:
-      rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
-                      (mode_t)data->set.new_directory_perms);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "mkdir");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_MKDIR(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_RENAME:
-      rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
-                       sshc->quote_path2);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "rename");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_RENAME(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_RMDIR:
-      rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "rmdir");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_RMDIR(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_UNLINK:
-      rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
-      if(rc == SSH_AGAIN)
-        break;
-      if(rc && !sshc->acceptfail) {
-        myssh_quote_error(data, sshc, "rm");
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
+      rc = myssh_in_SFTP_QUOTE_UNLINK(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_STATVFS:
       rc = myssh_in_SFTP_QUOTE_STATVFS(data, sshc);
       break;
-
     case SSH_SFTP_GETINFO:
-      if(data->set.get_filetime) {
-        myssh_to(data, sshc, SSH_SFTP_FILETIME);
-      }
-      else {
-        myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
-      }
+      rc = myssh_in_SFTP_GETINFO(data, sshc);
       break;
-
-    case SSH_SFTP_FILETIME: {
-      sftp_attributes attrs;
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      attrs = sftp_stat(sshc->sftp_session, sshp->path);
-      if(attrs) {
-        data->info.filetime = attrs->mtime;
-        sftp_attributes_free(attrs);
-      }
-
-      myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+    case SSH_SFTP_FILETIME:
+      rc = myssh_in_SFTP_FILETIME(data, sshc, sshp);
       break;
-    }
-
     case SSH_SFTP_TRANS_INIT:
-      if(data->state.upload)
-        myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
-      else if(sshp) {
-        if(sshp->path[strlen(sshp->path) - 1] == '/')
-          myssh_to(data, sshc, SSH_SFTP_READDIR_INIT);
-        else
-          myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
-      }
-      else
-        result = CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_TRANS_INIT(data, sshc, sshp);
       break;
-
     case SSH_SFTP_UPLOAD_INIT:
-      rc = sshp ? myssh_in_UPLOAD_INIT(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+      rc = myssh_in_UPLOAD_INIT(data, sshc, sshp);
       break;
-
     case SSH_SFTP_CREATE_DIRS_INIT:
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      else if(strlen(sshp->path) > 1) {
-        sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
-        myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
-      }
-      else {
-        myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
-      }
+      rc = myssh_in_SFTP_CREATE_DIRS_INIT(data, sshc, sshp);
       break;
-
     case SSH_SFTP_CREATE_DIRS:
-      sshc->slash_pos = strchr(sshc->slash_pos, '/');
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      else if(sshc->slash_pos) {
-        *sshc->slash_pos = 0;
-
-        infof(data, "Creating directory '%s'", sshp->path);
-        myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+      rc = myssh_in_SFTP_CREATE_DIRS(data, sshc, sshp);
       break;
-
     case SSH_SFTP_CREATE_DIRS_MKDIR:
-      /* 'mode' - parameter is preliminary - default to 0644 */
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      rc = sftp_mkdir(sshc->sftp_session, sshp->path,
-                      (mode_t)data->set.new_directory_perms);
-      *sshc->slash_pos = '/';
-      ++sshc->slash_pos;
-      if(rc < 0) {
-        /*
-         * Abort if failure was not that the directory already exists or
-         * the permission was denied (creation might succeed further down
-         * the path) - retry on unspecific FAILURE also
-         */
-        err = sftp_get_error(sshc->sftp_session);
-        if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
-           (err != SSH_FX_FAILURE) &&
-           (err != SSH_FX_PERMISSION_DENIED)) {
-          rc = myssh_to_SFTP_CLOSE(data, sshc);
-          break;
-        }
-        rc = 0; /* clear rc and continue */
-      }
-      myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
+      rc = myssh_in_SFTP_CREATE_DIRS_MKDIR(data, sshc, sshp);
       break;
-
     case SSH_SFTP_READDIR_INIT:
-      rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) :
-        CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_READDIR_INIT(data, sshc, sshp);
       break;
     case SSH_SFTP_READDIR:
-      rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_READDIR(data, sshc, sshp);
       break;
     case SSH_SFTP_READDIR_LINK:
       rc = myssh_in_SFTP_READDIR_LINK(data, sshc);
@@ -2116,14 +2319,13 @@
       rc = myssh_in_SFTP_READDIR_DONE(data, sshc);
       break;
     case SSH_SFTP_DOWNLOAD_INIT:
-      rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) :
-        CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp);
       break;
     case SSH_SFTP_DOWNLOAD_STAT:
       rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc);
       break;
     case SSH_SFTP_CLOSE:
-      rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : CURLE_FAILED_INIT;
+      rc = myssh_in_SFTP_CLOSE(data, sshc, sshp);
       break;
     case SSH_SFTP_SHUTDOWN:
       rc = myssh_in_SFTP_SHUTDOWN(data, sshc);
@@ -2131,107 +2333,32 @@
     case SSH_SCP_TRANS_INIT:
       rc = myssh_in_TRANS_INIT(data, sshc, sshp);
       break;
-
     case SSH_SCP_UPLOAD_INIT:
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      rc = ssh_scp_init(sshc->scp_session);
-      if(rc != SSH_OK) {
-        const char *err_msg = ssh_get_error(sshc->ssh_session);
-        failf(data, "%s", err_msg);
-        rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
-        break;
-      }
-
-      rc = ssh_scp_push_file64(sshc->scp_session, sshp->path,
-                               (uint64_t)data->state.infilesize,
-                               (int)data->set.new_file_perms);
-
-      if(rc != SSH_OK) {
-        const char *err_msg = ssh_get_error(sshc->ssh_session);
-        failf(data, "%s", err_msg);
-        rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
-        break;
-      }
-
-      /* upload data */
-      Curl_xfer_setup_send(data, FIRSTSOCKET);
-
-      /* not set by Curl_xfer_setup to preserve keepon bits */
-      data->conn->recv_idx = FIRSTSOCKET;
-
-      myssh_to(data, sshc, SSH_STOP);
-
+      rc = myssh_in_SCP_UPLOAD_INIT(data, sshc, sshp);
       break;
-
     case SSH_SCP_DOWNLOAD_INIT:
-      rc = ssh_scp_init(sshc->scp_session);
-      if(rc != SSH_OK) {
-        const char *err_msg = ssh_get_error(sshc->ssh_session);
-        failf(data, "%s", err_msg);
-        rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT);
+      rc = myssh_in_SCP_DOWNLOAD_INIT(data, sshc);
+      if(rc)
         break;
-      }
-      myssh_to(data, sshc, SSH_SCP_DOWNLOAD);
       FALLTHROUGH();
-
     case SSH_SCP_DOWNLOAD:
       rc = myssh_SSH_SCP_DOWNLOAD(data, sshc);
       break;
-
     case SSH_SCP_DONE:
-      if(data->state.upload)
-        myssh_to(data, sshc, SSH_SCP_SEND_EOF);
-      else
-        myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+      rc = myssh_in_SCP_DONE(data, sshc);
       break;
-
     case SSH_SCP_SEND_EOF:
-      if(sshc->scp_session) {
-        rc = ssh_scp_close(sshc->scp_session);
-        if(rc == SSH_AGAIN) {
-          /* Currently the ssh_scp_close handles waiting for EOF in
-           * blocking way.
-           */
-          break;
-        }
-        if(rc != SSH_OK) {
-          infof(data, "Failed to close libssh scp channel: %s",
-                ssh_get_error(sshc->ssh_session));
-        }
-      }
-
-      myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+      rc = myssh_in_SCP_SEND_EOF(data, sshc);
       break;
-
     case SSH_SCP_CHANNEL_FREE:
-      if(sshc->scp_session) {
-        ssh_scp_free(sshc->scp_session);
-        sshc->scp_session = NULL;
-      }
-      CURL_TRC_SSH(data, "SCP DONE phase complete");
-
-      ssh_set_blocking(sshc->ssh_session, 0);
-
-      myssh_to(data, sshc, SSH_SESSION_DISCONNECT);
+      myssh_in_SCP_CHANNEL_FREE(data, sshc);
       FALLTHROUGH();
-
     case SSH_SESSION_DISCONNECT:
       myssh_SESSION_DISCONNECT(data, sshc);
       FALLTHROUGH();
     case SSH_SESSION_FREE:
-      sshc_cleanup(sshc);
-      /* the code we are about to return */
-      result = sshc->actualcode;
-      memset(sshc, 0, sizeof(struct ssh_conn));
-      connclose(data->conn, "SSH session free");
-      sshc->state = SSH_SESSION_FREE;   /* current */
-      sshc->nextstate = SSH_NO_STATE;
-      myssh_to(data, sshc, SSH_STOP);
+      result = myssh_in_SESSION_FREE(data, sshc);
       break;
-
     case SSH_QUIT:
     default:
       /* internal error */
@@ -2268,12 +2395,12 @@
   if(!sshc || (sock == CURL_SOCKET_BAD))
     return CURLE_FAILED_INIT;
 
-  waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
+  waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags;
   if(waitfor) {
     int flags = 0;
-    if(waitfor & KEEP_RECV)
+    if(waitfor & REQ_IO_RECV)
       flags |= CURL_POLL_IN;
-    if(waitfor & KEEP_SEND)
+    if(waitfor & REQ_IO_SEND)
       flags |= CURL_POLL_OUT;
     DEBUGASSERT(flags);
     CURL_TRC_SSH(data, "pollset, flags=%x", flags);
@@ -2298,7 +2425,7 @@
 
   if(!sshc || !sshp)
     return CURLE_FAILED_INIT;
-  result = myssh_statemach_act(data, sshc, sshp, &block);
+  result = myssh_statemachine(data, sshc, sshp, &block);
   *done = (sshc->state == SSH_STOP);
   myssh_block2waitfor(conn, sshc, block);
 
@@ -2317,7 +2444,7 @@
     bool block;
     timediff_t left_ms = 1000;
 
-    result = myssh_statemach_act(data, sshc, sshp, &block);
+    result = myssh_statemachine(data, sshc, sshp, &block);
     if(result)
       break;
 
@@ -2348,7 +2475,7 @@
   struct SSHPROTO *sshp = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(sshp->path);
+  curlx_safefree(sshp->path);
   curlx_free(sshp);
 }
 
@@ -2422,13 +2549,9 @@
     return CURLE_FAILED_INIT;
   }
 
-  if(conn->bits.ipv6_ip) {
-    char ipv6[MAX_IPADR_LEN];
-    curl_msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name);
-    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6);
-  }
-  else
-    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+  rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST,
+                       (data->state.up.hostname[0] == '[') ?
+                       data->state.up.hostname : conn->host.name);
 
   if(rc != SSH_OK) {
     failf(data, "Could not set remote host");
@@ -2600,7 +2723,7 @@
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
 
-  data->req.keepon = 0;   /* clear all bits */
+  CURL_REQ_CLEAR_IO(data);
   return result;
 }
 
@@ -2637,7 +2760,7 @@
 
 #if 0
   /* The following code is misleading, mostly added as wishful thinking
-   * that libssh at some point will implement non-blocking ssh_scp_write/read.
+   * that libssh at some point would implement non-blocking ssh_scp_write/read.
    * Currently rc can only be number of bytes read or SSH_ERROR. */
   myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
 
@@ -2671,7 +2794,7 @@
     return CURLE_SSH;
 #if 0
   /* The following code is misleading, mostly added as wishful thinking
-   * that libssh at some point will implement non-blocking ssh_scp_write/read.
+   * that libssh at some point would implement non-blocking ssh_scp_write/read.
    * Currently rc can only be SSH_OK or SSH_ERROR. */
 
   myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
@@ -2985,7 +3108,7 @@
   scp_disconnect,               /* disconnect */
   ZERO_NULL,                    /* write_resp */
   ZERO_NULL,                    /* write_resp_hd */
-  ZERO_NULL,                    /* connection_check */
+  ZERO_NULL,                    /* connection_is_dead */
   ZERO_NULL,                    /* attach connection */
   ZERO_NULL,                    /* follow */
 };
@@ -3008,7 +3131,7 @@
   sftp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* follow */
 };
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index 63f5735..4e2a722 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -148,7 +148,9 @@
 #endif /* CURL_LIBSSH2_DEBUG */
   if(num_prompts == 1) {
     struct connectdata *conn = data->conn;
-    responses[0].text = curlx_strdup(conn->passwd);
+    /* this function must allocate memory that can be freed by libssh2, which
+       uses the LIBSSH2_FREE_FUNC callback */
+    responses[0].text = Curl_cstrdup(conn->passwd);
     responses[0].length =
       responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd));
   }
@@ -405,7 +407,7 @@
       rc = CURLKHSTAT_REJECT;
 
     switch(rc) {
-    default: /* unknown return codes will equal reject */
+    default: /* unknown return codes is the same as reject */
     case CURLKHSTAT_REJECT:
       myssh_to(data, sshc, SSH_SESSION_FREE);
       FALLTHROUGH();
@@ -476,7 +478,7 @@
                                        LIBSSH2_HOSTKEY_HASH_SHA256);
     if(!fingerprint) {
       failf(data,
-            "Denied establishing ssh session: sha256 fingerprint "
+            "Denied establishing ssh session: SHA256 fingerprint "
             "not available");
       myssh_to(data, sshc, SSH_SESSION_FREE);
       return CURLE_PEER_FAILED_VERIFICATION;
@@ -491,7 +493,7 @@
     }
 
     if(!fingerprint_b64) {
-      failf(data, "sha256 fingerprint could not be encoded");
+      failf(data, "SHA256 fingerprint could not be encoded");
       myssh_to(data, sshc, SSH_SESSION_FREE);
       return CURLE_PEER_FAILED_VERIFICATION;
     }
@@ -509,13 +511,13 @@
       b64_pos++;
     }
 
-    /* Before we authenticate we check the hostkey's sha256 fingerprint
+    /* Before we authenticate we check the hostkey's SHA256 fingerprint
      * against a known fingerprint, if available.
      */
     if((pub_pos != b64_pos) ||
        strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) {
       failf(data,
-            "Denied establishing ssh session: mismatch sha256 fingerprint. "
+            "Denied establishing ssh session: mismatch SHA256 fingerprint. "
             "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
       curlx_free(fingerprint_b64);
       myssh_to(data, sshc, SSH_SESSION_FREE);
@@ -545,17 +547,17 @@
       infof(data, "SSH MD5 fingerprint: %s", md5buffer);
     }
 
-    /* This does NOT verify the length of 'pubkey_md5' separately, which will
-       make the comparison below fail unless it is exactly 32 characters */
+    /* This does NOT verify the length of 'pubkey_md5' separately, which
+       makes the comparison below fail unless it is exactly 32 characters */
     if(!fingerprint || !curl_strequal(md5buffer, pubkey_md5)) {
       if(fingerprint) {
         failf(data,
-              "Denied establishing ssh session: mismatch md5 fingerprint. "
+              "Denied establishing ssh session: mismatch MD5 fingerprint. "
               "Remote %s is not equal to %s", md5buffer, pubkey_md5);
       }
       else {
         failf(data,
-              "Denied establishing ssh session: md5 fingerprint "
+              "Denied establishing ssh session: MD5 fingerprint "
               "not available");
       }
       myssh_to(data, sshc, SSH_SESSION_FREE);
@@ -600,7 +602,7 @@
 }
 
 /*
- * ssh_force_knownhost_key_type() will check the known hosts file and try to
+ * ssh_force_knownhost_key_type() checks the known hosts file and try to
  * force a specific public key type from the server if an entry is found.
  */
 static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
@@ -624,7 +626,7 @@
     struct connectdata *conn = data->conn;
     /* lets try to find our host in the known hosts file */
     while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
-      /* For non-standard ports, the name will be enclosed in */
+      /* For non-standard ports, the name is enclosed in */
       /* square brackets, followed by a colon and the port */
       if(store) {
         if(store->name) {
@@ -715,12 +717,12 @@
   return result;
 }
 
-static CURLcode return_quote_error(struct Curl_easy *data,
-                                   struct ssh_conn *sshc)
+static CURLcode quote_error(struct Curl_easy *data,
+                            struct ssh_conn *sshc)
 {
   failf(data, "Suspicious data after the command line");
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   return CURLE_QUOTE_ERROR;
 }
 
@@ -741,8 +743,8 @@
   sshc->acceptfail = FALSE;
 
   /* if a command starts with an asterisk, which a legal SFTP command never
-     can, the command will be allowed to fail without it causing any
-     aborts or cancels etc. It will cause libcurl to act as if the command
+     can, the command is allowed to fail without it causing any
+     aborts or cancels etc. It causes libcurl to act as if the command
      is successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
@@ -808,11 +810,11 @@
     if(result) {
       if(result != CURLE_OUT_OF_MEMORY)
         failf(data, "Syntax error in %s: Bad second parameter", cmd);
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       return result;
     }
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
 
     memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
     myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT);
@@ -827,17 +829,17 @@
     if(result) {
       if(result != CURLE_OUT_OF_MEMORY)
         failf(data, "Syntax error in ln/symlink: Bad second parameter");
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       return result;
     }
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
     return result;
   }
   else if(!strncmp(cmd, "mkdir ", 6)) {
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     /* create directory */
     myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR);
     return result;
@@ -850,37 +852,37 @@
     if(result) {
       if(result != CURLE_OUT_OF_MEMORY)
         failf(data, "Syntax error in rename: Bad second parameter");
-      Curl_safefree(sshc->quote_path1);
+      curlx_safefree(sshc->quote_path1);
       return result;
     }
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME);
     return result;
   }
   else if(!strncmp(cmd, "rmdir ", 6)) {
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     /* delete directory */
     myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR);
     return result;
   }
   else if(!strncmp(cmd, "rm ", 3)) {
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK);
     return result;
   }
   else if(!strncmp(cmd, "statvfs ", 8)) {
     if(*cp)
-      return_quote_error(data, sshc);
+      return quote_error(data, sshc);
     myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS);
     return result;
   }
 
   failf(data, "Unknown SFTP command");
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   return CURLE_QUOTE_ERROR;
 }
 
@@ -894,7 +896,7 @@
   /*
    * NOTE!!!  libssh2 requires that the destination path is a full path
    *          that includes the destination file and name OR ends in a "/"
-   *          If this is not done the destination file will be named the
+   *          If this is not done the destination file is named the
    *          same name as the last directory in the path.
    */
 
@@ -1054,7 +1056,7 @@
   /* upload data */
   Curl_xfer_setup_send(data, FIRSTSOCKET);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->recv_idx = FIRSTSOCKET;
 
   /* since we do not really wait for anything at this point, we want the
@@ -1103,7 +1105,7 @@
           if(!sshc->rsa)
             out_of_memory = TRUE;
           else if(curlx_stat(sshc->rsa, &sbuf)) {
-            Curl_safefree(sshc->rsa);
+            curlx_safefree(sshc->rsa);
           }
         }
         curlx_free(home);
@@ -1138,8 +1140,8 @@
     }
 
     if(out_of_memory || !sshc->rsa) {
-      Curl_safefree(sshc->rsa);
-      Curl_safefree(sshc->rsa_pub);
+      curlx_safefree(sshc->rsa);
+      curlx_safefree(sshc->rsa_pub);
       myssh_to(data, sshc, SSH_SESSION_FREE);
       return CURLE_OUT_OF_MEMORY;
     }
@@ -1157,7 +1159,7 @@
   else {
     myssh_to(data, sshc, SSH_AUTH_PASS_INIT);
   }
-  return 0;
+  return CURLE_OK;
 }
 
 static CURLcode sftp_quote_stat(struct Curl_easy *data,
@@ -1169,8 +1171,8 @@
   sshc->acceptfail = FALSE;
 
   /* if a command starts with an asterisk, which a legal SFTP command never
-     can, the command will be allowed to fail without it causing any aborts or
-     cancels etc. It will cause libcurl to act as if the command is
+     can, the command is allowed to fail without it causing any aborts or
+     cancels etc. It causes libcurl to act as if the command is
      successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
@@ -1266,8 +1268,8 @@
   myssh_to(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
   return CURLE_OK;
 fail:
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
   return CURLE_QUOTE_ERROR;
 }
 
@@ -1296,7 +1298,7 @@
     data->req.size = -1;
     data->req.maxdownload = -1;
     Curl_pgrsSetDownloadSize(data, -1);
-    attrs.filesize = 0; /* might be uninitialized but will be read below */
+    attrs.filesize = 0; /* might be uninitialized but is read below */
   }
   else {
     curl_off_t size = attrs.filesize;
@@ -1358,7 +1360,7 @@
   }
   Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->send_idx = 0;
 
   myssh_to(data, sshc, SSH_STOP);
@@ -1489,9 +1491,9 @@
    * must never change it later. Thus, always specify the correct username
    * here, even though the libssh2 docs kind of indicate that it should be
    * possible to get a 'generic' list (not user-specific) of authentication
-   * methods, presumably with a blank username. That will not work in my
+   * methods, presumably with a blank username. That does not work in my
    * experience.
-   * So always specify it here.
+   * Therefore always specify it here.
    */
   struct connectdata *conn = data->conn;
   sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
@@ -1535,8 +1537,8 @@
   if(rc == LIBSSH2_ERROR_EAGAIN)
     return CURLE_AGAIN;
 
-  Curl_safefree(sshc->rsa_pub);
-  Curl_safefree(sshc->rsa);
+  curlx_safefree(sshc->rsa_pub);
+  curlx_safefree(sshc->rsa);
 
   if(rc == 0) {
     sshc->authed = TRUE;
@@ -1801,10 +1803,15 @@
   /*
    * Get the "home" directory
    */
-  int rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
-                                   ".", curlx_uztoui(strlen(".")),
-                                   sshp->readdir_filename, CURL_PATH_MAX,
-                                   LIBSSH2_SFTP_REALPATH);
+  int rc;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  rc = libssh2_sftp_symlink_ex(sshc->sftp_session, ".",
+                               curlx_uztoui(strlen(".")),
+                               sshp->readdir_filename, CURL_PATH_MAX,
+                               LIBSSH2_SFTP_REALPATH);
   if(rc == LIBSSH2_ERROR_EAGAIN)
     return CURLE_AGAIN;
 
@@ -1835,8 +1842,7 @@
 
   /* This is the last step in the SFTP connect phase. Do note that while we
      get the homedir here, we get the "workingpath" in the DO action since the
-     homedir will remain the same between request but the working path will
-     not. */
+     homedir remains the same between request but the working path does not. */
   CURL_TRC_SSH(data, "CONNECT phase done");
   return CURLE_OK;
 }
@@ -1845,7 +1851,12 @@
                                           struct ssh_conn *sshc,
                                           struct SSHPROTO *sshp)
 {
-  CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+  CURLcode result;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
   if(result) {
     myssh_to(data, sshc, SSH_STOP);
     return result;
@@ -1881,7 +1892,12 @@
                                      struct SSHPROTO *sshp)
 {
   /* Send quote commands */
-  CURLcode result = sftp_quote(data, sshc, sshp);
+  CURLcode result;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  result = sftp_quote(data, sshc, sshp);
   if(result) {
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
@@ -1892,8 +1908,8 @@
 static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data,
                                           struct ssh_conn *sshc)
 {
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
 
   sshc->quote_item = sshc->quote_item->next;
 
@@ -1917,7 +1933,12 @@
                                           struct SSHPROTO *sshp,
                                           bool *blockp)
 {
-  CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp);
+  CURLcode result;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  result = sftp_quote_stat(data, sshc, sshp, blockp);
   if(result) {
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
@@ -1929,11 +1950,15 @@
                                              struct ssh_conn *sshc,
                                              struct SSHPROTO *sshp)
 {
-  int rc =
-    libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
-                         curlx_uztoui(strlen(sshc->quote_path2)),
-                         LIBSSH2_SFTP_SETSTAT,
-                         &sshp->quote_attrs);
+  int rc;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+                            curlx_uztoui(strlen(sshc->quote_path2)),
+                            LIBSSH2_SFTP_SETSTAT,
+                            &sshp->quote_attrs);
   if(rc == LIBSSH2_ERROR_EAGAIN)
     return CURLE_AGAIN;
 
@@ -1941,8 +1966,8 @@
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     failf(data, "Attempt to set SFTP stats for \"%s\" failed: %s",
           sshc->quote_path2, sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
-    Curl_safefree(sshc->quote_path2);
+    curlx_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path2);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -1968,8 +1993,8 @@
     failf(data, "symlink \"%s\" to \"%s\" failed: %s",
           sshc->quote_path1, sshc->quote_path2,
           sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
-    Curl_safefree(sshc->quote_path2);
+    curlx_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path2);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -1991,7 +2016,7 @@
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     failf(data, "mkdir \"%s\" failed: %s",
           sshc->quote_path1, sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path1);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -2020,8 +2045,8 @@
     failf(data, "rename \"%s\" to \"%s\" failed: %s",
           sshc->quote_path1, sshc->quote_path2,
           sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
-    Curl_safefree(sshc->quote_path2);
+    curlx_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path2);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -2042,7 +2067,7 @@
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     failf(data, "rmdir \"%s\" failed: %s",
           sshc->quote_path1, sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path1);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -2063,7 +2088,7 @@
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     failf(data, "rm \"%s\" failed: %s",
           sshc->quote_path1, sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path1);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -2087,7 +2112,7 @@
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     failf(data, "statvfs \"%s\" failed: %s",
           sshc->quote_path1, sftp_libssh2_strerror(sftperr));
-    Curl_safefree(sshc->quote_path1);
+    curlx_safefree(sshc->quote_path1);
     myssh_to(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     return CURLE_QUOTE_ERROR;
@@ -2140,9 +2165,14 @@
                                                  struct SSHPROTO *sshp)
 {
   /* 'mode' - parameter is preliminary - default to 0644 */
-  int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
-                                 curlx_uztoui(strlen(sshp->path)),
-                                 (long)data->set.new_directory_perms);
+  int rc;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
+                             curlx_uztoui(strlen(sshp->path)),
+                             (long)data->set.new_directory_perms);
   if(rc == LIBSSH2_ERROR_EAGAIN)
     return CURLE_AGAIN;
 
@@ -2170,6 +2200,9 @@
                                             struct ssh_conn *sshc,
                                             struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
   Curl_pgrsSetDownloadSize(data, -1);
   if(data->req.no_body) {
     myssh_to(data, sshc, SSH_STOP);
@@ -2204,13 +2237,16 @@
                                             struct SSHPROTO *sshp)
 {
   CURLcode result;
-  int rc =
-    libssh2_sftp_symlink_ex(sshc->sftp_session,
-                            curlx_dyn_ptr(&sshp->readdir_link),
-                            (unsigned int)
-                            curlx_dyn_len(&sshp->readdir_link),
-                            sshp->readdir_filename,
-                            CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
+  int rc;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
+                               curlx_dyn_ptr(&sshp->readdir_link),
+                               (unsigned int)
+                               curlx_dyn_len(&sshp->readdir_link),
+                               sshp->readdir_filename,
+                               CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
   if(rc == LIBSSH2_ERROR_EAGAIN)
     return CURLE_AGAIN;
 
@@ -2233,9 +2269,12 @@
                                             struct SSHPROTO *sshp)
 {
   curl_off_t bytecount;
+  libssh2_struct_stat sb;
 
+  if(!sshp)
+    return CURLE_FAILED_INIT;
   /*
-   * We must check the remote file; if it is a directory no values will
+   * We must check the remote file; if it is a directory no values are
    * be set in sb
    */
 
@@ -2244,7 +2283,6 @@
    */
 
   /* get a fresh new channel from the ssh layer */
-  libssh2_struct_stat sb;
   memset(&sb, 0, sizeof(libssh2_struct_stat));
   sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, sshp->path, &sb);
   if(!sshc->ssh_channel) {
@@ -2266,7 +2304,7 @@
   data->req.maxdownload = (curl_off_t)sb.st_size;
   Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->send_idx = 0;
 
   myssh_to(data, sshc, SSH_STOP);
@@ -2278,6 +2316,8 @@
                                      struct SSHPROTO *sshp)
 {
   int rc = 0;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
   if(sshc->sftp_handle) {
     rc = libssh2_sftp_close(sshc->sftp_handle);
     if(rc == LIBSSH2_ERROR_EAGAIN)
@@ -2291,7 +2331,7 @@
     sshc->sftp_handle = NULL;
   }
 
-  Curl_safefree(sshp->path);
+  curlx_safefree(sshp->path);
 
   CURL_TRC_SSH(data, "SFTP DONE done");
 
@@ -2339,7 +2379,7 @@
     sshc->sftp_session = NULL;
   }
 
-  Curl_safefree(sshc->homedir);
+  curlx_safefree(sshc->homedir);
 
   myssh_to(data, sshc, SSH_SESSION_DISCONNECT);
   return CURLE_OK;
@@ -2349,6 +2389,8 @@
                                              struct ssh_conn *sshc,
                                              struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return CURLE_FAILED_INIT;
   /*
    * Work on getting the specified file
    */
@@ -2377,10 +2419,12 @@
                                           struct ssh_conn *sshc,
                                           struct SSHPROTO *sshp)
 {
+  if(!sshp)
+    return CURLE_FAILED_INIT;
   /*
    * libssh2 requires that the destination path is a full path that
    * includes the destination file and name OR ends in a "/" . If this is
-   * not done the destination file will be named the same name as the last
+   * not done the destination file is named the same name as the last
    * directory in the path.
    */
   sshc->ssh_channel =
@@ -2412,7 +2456,7 @@
   Curl_pgrsSetUploadSize(data, data->state.infilesize);
   Curl_xfer_setup_send(data, FIRSTSOCKET);
 
-  /* not set by Curl_xfer_setup to preserve keepon bits */
+  /* not set by Curl_xfer_setup to preserve io_flags */
   data->conn->recv_idx = FIRSTSOCKET;
 
   myssh_to(data, sshc, SSH_STOP);
@@ -2452,7 +2496,7 @@
     }
   }
 
-  Curl_safefree(sshc->homedir);
+  curlx_safefree(sshc->homedir);
 
   myssh_to(data, sshc, SSH_SESSION_FREE);
   return CURLE_OK;
@@ -2548,19 +2592,320 @@
   DEBUGASSERT(sshc->kh == NULL);
   DEBUGASSERT(sshc->ssh_agent == NULL);
 
-  Curl_safefree(sshc->rsa_pub);
-  Curl_safefree(sshc->rsa);
-  Curl_safefree(sshc->quote_path1);
-  Curl_safefree(sshc->quote_path2);
-  Curl_safefree(sshc->homedir);
+  curlx_safefree(sshc->rsa_pub);
+  curlx_safefree(sshc->rsa);
+  curlx_safefree(sshc->quote_path1);
+  curlx_safefree(sshc->quote_path2);
+  curlx_safefree(sshc->homedir);
 
   return CURLE_OK;
 }
 
+static CURLcode ssh_state_sftp_getinfo(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  if(data->set.get_filetime)
+    myssh_to(data, sshc, SSH_SFTP_FILETIME);
+  else
+    myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_filetime(struct Curl_easy *data,
+                                        struct ssh_conn *sshc,
+                                        struct SSHPROTO *sshp)
+{
+  LIBSSH2_SFTP_ATTRIBUTES attrs;
+  int rc;
+
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+                            curlx_uztoui(strlen(sshp->path)),
+                            LIBSSH2_SFTP_STAT, &attrs);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc == 0)
+    data->info.filetime = (time_t)attrs.mtime;
+
+  myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_trans_init(struct Curl_easy *data,
+                                          struct ssh_conn *sshc,
+                                          struct SSHPROTO *sshp)
+{
+  if(data->state.upload)
+    myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  else if(sshp) {
+    size_t plen = strlen(sshp->path);
+    if(plen && (sshp->path[plen - 1] == '/'))
+      myssh_to(data, sshc, SSH_SFTP_READDIR_INIT);
+    else
+      myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
+  }
+  else
+    return CURLE_FAILED_INIT;
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_upload_init(struct Curl_easy *data,
+                                           struct ssh_conn *sshc,
+                                           struct SSHPROTO *sshp,
+                                           bool *block)
+{
+  CURLcode result;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  result = sftp_upload_init(data, sshc, sshp, block);
+  if(result) {
+    myssh_to(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+  }
+  return result;
+}
+
+static CURLcode ssh_state_sftp_create_dirs_init(struct Curl_easy *data,
+                                                struct ssh_conn *sshc,
+                                                struct SSHPROTO *sshp)
+{
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  if(strlen(sshp->path) > 1) {
+    sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+    myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
+  }
+  else
+    myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data,
+                                           struct ssh_conn *sshc,
+                                           struct SSHPROTO *sshp)
+{
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  sshc->slash_pos = strchr(sshc->slash_pos, '/');
+  if(sshc->slash_pos) {
+    *sshc->slash_pos = 0;
+    infof(data, "Creating directory '%s'", sshp->path);
+    myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
+    return CURLE_OK;
+  }
+  myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir(struct Curl_easy *data,
+                                       struct ssh_conn *sshc,
+                                       struct SSHPROTO *sshp,
+                                       bool *block)
+{
+  CURLcode result;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  result = sftp_readdir(data, sshc, sshp, block);
+  if(result)
+    myssh_to(data, sshc, SSH_SFTP_CLOSE);
+  return result;
+}
+
+static CURLcode ssh_state_sftp_readdir_bottom(struct Curl_easy *data,
+                                              struct ssh_conn *sshc,
+                                              struct SSHPROTO *sshp)
+{
+  CURLcode result;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+
+  result = curlx_dyn_addn(&sshp->readdir, "\n", 1);
+  if(!result)
+    result = Curl_client_write(data, CLIENTWRITE_BODY,
+                               curlx_dyn_ptr(&sshp->readdir),
+                               curlx_dyn_len(&sshp->readdir));
+  if(result) {
+    curlx_dyn_free(&sshp->readdir);
+    myssh_to(data, sshc, SSH_STOP);
+  }
+  else {
+    curlx_dyn_reset(&sshp->readdir);
+    myssh_to(data, sshc, SSH_SFTP_READDIR);
+  }
+  return result;
+}
+
+static CURLcode ssh_state_sftp_readdir_done(struct Curl_easy *data,
+                                            struct ssh_conn *sshc)
+{
+  if(libssh2_sftp_closedir(sshc->sftp_handle) ==
+     LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  sshc->sftp_handle = NULL;
+
+  /* no data to transfer */
+  Curl_xfer_setup_nop(data);
+  myssh_to(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_download_stat(struct Curl_easy *data,
+                                             struct ssh_conn *sshc,
+                                             struct SSHPROTO *sshp,
+                                             bool *block)
+{
+  CURLcode result;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  result = sftp_download_stat(data, sshc, sshp, block);
+  if(result) {
+    myssh_to(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+  }
+  return result;
+}
+
+static CURLcode ssh_state_scp_trans_init(struct Curl_easy *data,
+                                         struct ssh_conn *sshc,
+                                         struct SSHPROTO *sshp)
+{
+  CURLcode result;
+  if(!sshp)
+    return CURLE_FAILED_INIT;
+  result = Curl_getworkingpath(data, sshc->homedir,
+                               &sshp->path);
+  if(result) {
+    myssh_to(data, sshc, SSH_STOP);
+    return result;
+  }
+
+  if(data->state.upload) {
+    if(data->state.infilesize < 0) {
+      failf(data, "SCP requires a known file size for upload");
+      myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+      return CURLE_UPLOAD_FAILED;
+    }
+    myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT);
+  }
+  else
+    myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_done(struct Curl_easy *data,
+                                   struct ssh_conn *sshc)
+{
+  if(data->state.upload)
+    myssh_to(data, sshc, SSH_SCP_SEND_EOF);
+  else
+    myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  if(sshc->ssh_channel) {
+    int rc = libssh2_channel_send_eof(sshc->ssh_channel);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+    if(rc) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data,
+            "Failed to send libssh2 channel EOF: %d %s",
+            rc, err_msg);
+    }
+  }
+  myssh_to(data, sshc, SSH_SCP_WAIT_EOF);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  if(sshc->ssh_channel) {
+    int rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+    if(rc) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data, "Failed to get channel EOF: %d %s",
+            rc, err_msg);
+    }
+  }
+  myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data,
+                                         struct ssh_conn *sshc)
+{
+  if(sshc->ssh_channel) {
+    int rc =
+      libssh2_channel_wait_closed(sshc->ssh_channel);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+    if(rc) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data, "Channel failed to close: %d %s",
+            rc, err_msg);
+    }
+  }
+  myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_channel_free(
+  struct Curl_easy *data,
+  struct ssh_conn *sshc)
+{
+  if(sshc->ssh_channel) {
+    int rc = libssh2_channel_free(sshc->ssh_channel);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+    if(rc < 0) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data,
+            "Failed to free libssh2 scp subsystem: %d %s",
+            rc, err_msg);
+    }
+    sshc->ssh_channel = NULL;
+  }
+  CURL_TRC_SSH(data, "SCP DONE phase complete");
+  myssh_to(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_session_free(struct Curl_easy *data,
+                                       struct ssh_conn *sshc)
+{
+  CURLcode result = sshc_cleanup(sshc, data, FALSE);
+  struct connectdata *conn = data->conn;
+  if(result)
+    return result;
+  memset(sshc, 0, sizeof(struct ssh_conn));
+  connclose(conn, "SSH session free");
+  sshc->state = SSH_SESSION_FREE; /* current */
+  myssh_to(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
 /*
  * ssh_statemachine() runs the SSH state machine as far as it can without
  * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
+ * to is set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
  * meaning it wants to be called again when the socket is ready
  */
 static CURLcode ssh_statemachine(struct Curl_easy *data,
@@ -2569,7 +2914,6 @@
                                  bool *block)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   *block = 0; /* we are not blocking by default */
 
   do {
@@ -2647,13 +2991,11 @@
       break;
 
     case SSH_SFTP_REALPATH:
-      result = sshp ? ssh_state_sftp_realpath(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_realpath(data, sshc, sshp);
       break;
 
     case SSH_SFTP_QUOTE_INIT:
-      result = sshp ? ssh_state_sftp_quote_init(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_quote_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_POSTQUOTE_INIT:
@@ -2661,8 +3003,7 @@
       break;
 
     case SSH_SFTP_QUOTE:
-      result = sshp ? ssh_state_sftp_quote(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_quote(data, sshc, sshp);
       break;
 
     case SSH_SFTP_NEXT_QUOTE:
@@ -2670,13 +3011,11 @@
       break;
 
     case SSH_SFTP_QUOTE_STAT:
-      result = sshp ? ssh_state_sftp_quote_stat(data, sshc, sshp, block) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_quote_stat(data, sshc, sshp, block);
       break;
 
     case SSH_SFTP_QUOTE_SETSTAT:
-      result = sshp ? ssh_state_sftp_quote_setstat(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_quote_setstat(data, sshc, sshp);
       break;
 
     case SSH_SFTP_QUOTE_SYMLINK:
@@ -2704,160 +3043,63 @@
       break;
 
     case SSH_SFTP_GETINFO:
-      if(data->set.get_filetime) {
-        myssh_to(data, sshc, SSH_SFTP_FILETIME);
-      }
-      else {
-        myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
-      }
+      result = ssh_state_sftp_getinfo(data, sshc);
       break;
 
-    case SSH_SFTP_FILETIME: {
-      LIBSSH2_SFTP_ATTRIBUTES attrs;
-      int rc;
-
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
-                                curlx_uztoui(strlen(sshp->path)),
-                                LIBSSH2_SFTP_STAT, &attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        result = CURLE_AGAIN;
-        break;
-      }
-      if(rc == 0) {
-        data->info.filetime = (time_t)attrs.mtime;
-      }
-
-      myssh_to(data, sshc, SSH_SFTP_TRANS_INIT);
+    case SSH_SFTP_FILETIME:
+      result = ssh_state_sftp_filetime(data, sshc, sshp);
       break;
-    }
 
     case SSH_SFTP_TRANS_INIT:
-      if(data->state.upload)
-        myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
-      else if(sshp) {
-        if(sshp->path[strlen(sshp->path) - 1] == '/')
-          myssh_to(data, sshc, SSH_SFTP_READDIR_INIT);
-        else
-          myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
-      }
-      else
-        result = CURLE_FAILED_INIT;
+      result = ssh_state_sftp_trans_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_UPLOAD_INIT:
-      result = sshp ? sftp_upload_init(data, sshc, sshp, block) :
-               CURLE_FAILED_INIT;
-      if(result) {
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-      }
+      result = ssh_state_sftp_upload_init(data, sshc, sshp, block);
       break;
 
     case SSH_SFTP_CREATE_DIRS_INIT:
-      if(!sshp)
-        result = CURLE_FAILED_INIT;
-      else if(strlen(sshp->path) > 1) {
-        sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
-        myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS);
-      }
-      else {
-        myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
-      }
+      result = ssh_state_sftp_create_dirs_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_CREATE_DIRS:
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      sshc->slash_pos = strchr(sshc->slash_pos, '/');
-      if(sshc->slash_pos) {
-        *sshc->slash_pos = 0;
-
-        infof(data, "Creating directory '%s'", sshp->path);
-        myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
-        break;
-      }
-      myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT);
+      result = ssh_state_sftp_create_dirs(data, sshc, sshp);
       break;
 
     case SSH_SFTP_CREATE_DIRS_MKDIR:
-      result = sshp ? ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR_INIT:
-      result = sshp ? ssh_state_sftp_readdir_init(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_readdir_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR:
-      result = sshp ? sftp_readdir(data, sshc, sshp, block) :
-               CURLE_FAILED_INIT;
-      if(result) {
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-      }
+      result = ssh_state_sftp_readdir(data, sshc, sshp, block);
       break;
 
     case SSH_SFTP_READDIR_LINK:
-      result = sshp ? ssh_state_sftp_readdir_link(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_readdir_link(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR_BOTTOM:
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      result = curlx_dyn_addn(&sshp->readdir, "\n", 1);
-      if(!result)
-        result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                   curlx_dyn_ptr(&sshp->readdir),
-                                   curlx_dyn_len(&sshp->readdir));
-
-      if(result) {
-        curlx_dyn_free(&sshp->readdir);
-        myssh_to(data, sshc, SSH_STOP);
-      }
-      else {
-        curlx_dyn_reset(&sshp->readdir);
-        myssh_to(data, sshc, SSH_SFTP_READDIR);
-      }
+      result = ssh_state_sftp_readdir_bottom(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR_DONE:
-      if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN)
-        result = CURLE_AGAIN;
-      else {
-        sshc->sftp_handle = NULL;
-
-        /* no data to transfer */
-        Curl_xfer_setup_nop(data);
-        myssh_to(data, sshc, SSH_STOP);
-      }
+      result = ssh_state_sftp_readdir_done(data, sshc);
       break;
 
     case SSH_SFTP_DOWNLOAD_INIT:
-      result = sshp ? ssh_state_sftp_download_init(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_download_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_DOWNLOAD_STAT:
-      result = sshp ? sftp_download_stat(data, sshc, sshp, block) :
-               CURLE_FAILED_INIT;
-      if(result) {
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-      }
+      result = ssh_state_sftp_download_stat(data, sshc, sshp, block);
       break;
 
     case SSH_SFTP_CLOSE:
-      result = sshp ? ssh_state_sftp_close(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_sftp_close(data, sshc, sshp);
       break;
 
     case SSH_SFTP_SHUTDOWN:
@@ -2865,114 +3107,35 @@
       break;
 
     case SSH_SCP_TRANS_INIT:
-      result = sshp ? Curl_getworkingpath(data, sshc->homedir, &sshp->path) :
-               CURLE_FAILED_INIT;
-      if(result) {
-        myssh_to(data, sshc, SSH_STOP);
-        break;
-      }
-
-      if(data->state.upload) {
-        if(data->state.infilesize < 0) {
-          failf(data, "SCP requires a known file size for upload");
-          result = CURLE_UPLOAD_FAILED;
-          myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
-          break;
-        }
-        myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT);
-      }
-      else {
-        myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT);
-      }
+      result = ssh_state_scp_trans_init(data, sshc, sshp);
       break;
 
     case SSH_SCP_UPLOAD_INIT:
-      result = sshp ? ssh_state_scp_upload_init(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_scp_upload_init(data, sshc, sshp);
       break;
 
     case SSH_SCP_DOWNLOAD_INIT:
-      result = sshp ? ssh_state_scp_download_init(data, sshc, sshp) :
-               CURLE_FAILED_INIT;
+      result = ssh_state_scp_download_init(data, sshc, sshp);
       break;
 
     case SSH_SCP_DONE:
-      if(data->state.upload)
-        myssh_to(data, sshc, SSH_SCP_SEND_EOF);
-      else
-        myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+      result = ssh_state_scp_done(data, sshc);
       break;
 
     case SSH_SCP_SEND_EOF:
-      if(sshc->ssh_channel) {
-        int rc = libssh2_channel_send_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          result = CURLE_AGAIN;
-          break;
-        }
-        if(rc) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to send libssh2 channel EOF: %d %s",
-                rc, err_msg);
-        }
-      }
-      myssh_to(data, sshc, SSH_SCP_WAIT_EOF);
+      result = ssh_state_scp_send_eof(data, sshc);
       break;
 
     case SSH_SCP_WAIT_EOF:
-      if(sshc->ssh_channel) {
-        int rc = libssh2_channel_wait_eof(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          result = CURLE_AGAIN;
-          break;
-        }
-        if(rc) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
-        }
-      }
-      myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE);
+      result = ssh_state_scp_wait_eof(data, sshc);
       break;
 
     case SSH_SCP_WAIT_CLOSE:
-      if(sshc->ssh_channel) {
-        int rc = libssh2_channel_wait_closed(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          result = CURLE_AGAIN;
-          break;
-        }
-        if(rc) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Channel failed to close: %d %s", rc, err_msg);
-        }
-      }
-      myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE);
+      result = ssh_state_scp_wait_close(data, sshc);
       break;
 
     case SSH_SCP_CHANNEL_FREE:
-      if(sshc->ssh_channel) {
-        int rc = libssh2_channel_free(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          result = CURLE_AGAIN;
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 scp subsystem: %d %s",
-                rc, err_msg);
-        }
-        sshc->ssh_channel = NULL;
-      }
-      CURL_TRC_SSH(data, "SCP DONE phase complete");
-      myssh_to(data, sshc, SSH_STOP);
+      result = ssh_state_scp_channel_free(data, sshc);
       break;
 
     case SSH_SESSION_DISCONNECT:
@@ -2980,14 +3143,7 @@
       break;
 
     case SSH_SESSION_FREE:
-      result = sshc_cleanup(sshc, data, FALSE);
-      if(result)
-        break;
-      /* the code we are about to return */
-      memset(sshc, 0, sizeof(struct ssh_conn));
-      connclose(conn, "SSH session free");
-      sshc->state = SSH_SESSION_FREE; /* current */
-      myssh_to(data, sshc, SSH_STOP);
+      result = ssh_state_session_free(data, sshc);
       break;
 
     case SSH_QUIT:
@@ -3024,12 +3180,12 @@
   if(!sshc || (sock == CURL_SOCKET_BAD))
     return CURLE_FAILED_INIT;
 
-  waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon;
+  waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags;
   if(waitfor) {
     int flags = 0;
-    if(waitfor & KEEP_RECV)
+    if(waitfor & REQ_IO_RECV)
       flags |= CURL_POLL_IN;
-    if(waitfor & KEEP_SEND)
+    if(waitfor & REQ_IO_SEND)
       flags |= CURL_POLL_OUT;
     DEBUGASSERT(flags);
     CURL_TRC_SSH(data, "pollset, flags=%x", flags);
@@ -3058,8 +3214,9 @@
     dir = libssh2_session_block_directions(sshc->ssh_session);
     if(dir) {
       /* translate the libssh2 define bits into our own bit defines */
-      sshc->waitfor = ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) |
-                      ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0);
+      sshc->waitfor =
+        ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? REQ_IO_RECV : 0) |
+        ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? REQ_IO_SEND : 0);
     }
   }
   if(!dir)
@@ -3146,7 +3303,7 @@
   struct SSHPROTO *sshp = entry;
   (void)key;
   (void)klen;
-  Curl_safefree(sshp->path);
+  curlx_safefree(sshp->path);
   curlx_dyn_free(&sshp->readdir);
   curlx_dyn_free(&sshp->readdir_link);
   curlx_free(sshp);
@@ -3520,7 +3677,7 @@
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
 
-  data->req.keepon = 0; /* clear all bits */
+  CURL_REQ_CLEAR_IO(data);
   return result;
 }
 
@@ -3835,7 +3992,7 @@
   scp_disconnect,                       /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ssh_attach,                           /* attach */
   ZERO_NULL,                            /* follow */
 };
@@ -3858,7 +4015,7 @@
   sftp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ssh_attach,                           /* attach */
   ZERO_NULL,                            /* follow */
 };
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h
index 1afb1e8..44e72ab 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -29,9 +29,6 @@
 extern const struct Curl_protocol Curl_protocol_sftp;
 extern const struct Curl_protocol Curl_protocol_scp;
 
-extern const struct Curl_scheme Curl_scheme_sftp;
-extern const struct Curl_scheme Curl_scheme_scp;
-
 #ifdef USE_SSH
 
 #ifdef USE_LIBSSH2
@@ -162,7 +159,7 @@
   int secondCreateDirs;         /* counter use by the code to see if the
                                    second attempt has been made to change
                                    to/create a directory */
-  int waitfor;                  /* KEEP_RECV/KEEP_SEND bits overriding
+  int waitfor;                  /* REQ_IO_RECV/REQ_IO_SEND bits overriding
                                    pollset given flags */
   char *slash_pos;              /* used by the SFTP_CREATE_DIRS state */
 
diff --git a/Utilities/cmcurl/lib/vssh/vssh.c b/Utilities/cmcurl/lib/vssh/vssh.c
index d499a63..0ba4a9e 100644
--- a/Utilities/cmcurl/lib/vssh/vssh.c
+++ b/Utilities/cmcurl/lib/vssh/vssh.c
@@ -331,34 +331,3 @@
 }
 
 #endif /* USE_SSH */
-
-/*
- * SFTP protocol handler.
- */
-const struct Curl_scheme Curl_scheme_sftp = {
-  "SFTP",                               /* scheme */
-#ifndef USE_SSH
-  NULL,
-#else
-  &Curl_protocol_sftp,
-#endif
-  CURLPROTO_SFTP,                       /* protocol */
-  CURLPROTO_SFTP,                       /* family */
-  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
-  PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
-  PORT_SSH                              /* defport */
-};
-
-const struct Curl_scheme Curl_scheme_scp = {
-  "SCP",                                /* scheme */
-#ifndef USE_SSH
-  NULL,
-#else
-  &Curl_protocol_scp,
-#endif
-  CURLPROTO_SCP,                        /* protocol */
-  CURLPROTO_SCP,                        /* family */
-  PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
-  PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE,
-  PORT_SSH,                             /* defport */
-};
diff --git a/Utilities/cmcurl/lib/vtls/apple.c b/Utilities/cmcurl/lib/vtls/apple.c
index a6a5ba0..e28a40c 100644
--- a/Utilities/cmcurl/lib/vtls/apple.c
+++ b/Utilities/cmcurl/lib/vtls/apple.c
@@ -130,7 +130,7 @@
     struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
     if(!ssl_config->no_revoke) {
       if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) {
-        /* Even without this set, validation will seemingly-unavoidably fail
+        /* Even without this set, validation seemingly-unavoidably fails
          * for certificates that trustd already knows to be revoked.
          * This policy further allows trustd to consult CRLs and OCSP data
          * to determine revocation status (which it may then cache). */
@@ -142,7 +142,7 @@
          * of a cert being NOT REVOKED. Which not in general available for
          * certificates on the Internet.
          * It seems that applications using this policy are expected to PIN
-         * their certificate public keys or verification will fail.
+         * their certificate public keys or verification fails.
          * This does not seem to be what we want here. */
         if(!ssl_config->revoke_best_effort) {
           revocation_flags |= kSecRevocationRequirePositiveResponse;
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index a0a465c..9b6a4fa 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -127,7 +127,7 @@
   }
 
   result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread);
-  CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", blen, result, nread);
+  CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zu", blen, result, nread);
   backend->gtls.io_result = result;
   if(result) {
     /* !checksrc! disable ERRNOVAR 1 */
@@ -203,10 +203,15 @@
   f = curlx_fopen(file, "rb");
   if(!f)
     return loaded_file;
-  if(fseek(f, 0, SEEK_END) != 0 ||
-     (filelen = ftell(f)) < 0 ||
-     fseek(f, 0, SEEK_SET) != 0 ||
-     !(ptr = curlx_malloc((size_t)filelen)))
+  if(fseek(f, 0, SEEK_END) != 0)
+    goto out;
+  filelen = ftell(f);
+  if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH)
+    goto out;
+  if(fseek(f, 0, SEEK_SET) != 0)
+    goto out;
+  ptr = curlx_malloc((size_t)filelen);
+  if(!ptr)
     goto out;
   if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
     curlx_free(ptr);
@@ -257,6 +262,7 @@
     return CURLE_AGAIN;
   }
   else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+#ifdef CURLVERBOSE
     const char *strerr = NULL;
 
     if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
@@ -268,6 +274,7 @@
       strerr = gnutls_strerror(rc);
 
     infof(data, "gnutls_handshake() warning: %s", strerr);
+#endif
     return CURLE_AGAIN;
   }
   else if((rc < 0) && backend->gtls.io_result) {
@@ -304,7 +311,7 @@
 
 #define GNUTLS_CIPHERS "NORMAL:%PROFILE_MEDIUM:-ARCFOUR-128:" \
   "-CTYPE-ALL:+CTYPE-X509"
-/* If GnuTLS was compiled without support for SRP it will error out if SRP is
+/* If GnuTLS was compiled without support for SRP it errors out if SRP is
    requested in the priority string, so treat it specially
  */
 #define GNUTLS_SRP "+SRP"
@@ -710,7 +717,7 @@
 
   /* get the session ID data size */
   gnutls_session_get_data(session, NULL, &sdata_len);
-  if(!sdata_len) /* gnutls does this for some version combinations */
+  if(!sdata_len) /* GnuTLS does this for some version combinations */
     return CURLE_OK;
 
   sdata = curlx_malloc(sdata_len); /* get a buffer for it */
@@ -786,7 +793,7 @@
   if(when) { /* after message has been processed */
     struct Curl_easy *data = CF_DATA_CURRENT(cf);
     if(data) {
-      CURL_TRC_CF(data, cf, "handshake: %s message type %d",
+      CURL_TRC_CF(data, cf, "handshake: %s message type %u",
                   incoming ? "incoming" : "outgoing", htype);
       switch(htype) {
       case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
@@ -818,7 +825,7 @@
 #ifdef USE_GNUTLS_SRP
   if(conn_config->username) {
     /* Only add SRP to the cipher list if SRP is requested. Otherwise
-     * GnuTLS will disable TLS 1.3 support. */
+     * GnuTLS disables TLS 1.3 support. */
     result = curlx_dyn_add(&buf, priority);
     if(!result)
       result = curlx_dyn_add(&buf, ":" GNUTLS_SRP);
@@ -1116,7 +1123,7 @@
   Curl_alpn_copy(&alpns, alpns_requested);
 
   /* This might be a reconnect, so we check for a session ID in the cache
-     to speed up things. We need to do this before constructing the gnutls
+     to speed up things. We need to do this before constructing the GnuTLS
      session since we need to set flags depending on the kind of reuse. */
   if(conn_config->cache_session && !conn_config->verifystatus) {
     result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
@@ -1138,7 +1145,8 @@
       else {
         infof(data, "SSL reusing session with ALPN '%s'",
               scs->alpn ? scs->alpn : "-");
-        if(ssl_config->earlydata && scs->alpn && !cf->conn->connect_only) {
+        if(ssl_config->earlydata && scs->alpn &&
+           !cf->conn->bits.connect_only) {
           bool do_early_data = FALSE;
           if(sess_reuse_cb) {
             result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
@@ -1178,7 +1186,7 @@
 #endif
 
   /* convert the ALPN string from our arguments to a list of strings that
-   * gnutls wants and will convert internally back to this string for sending
+   * GnuTLS wants and does convert internally back to this string for sending
    * to the server. nice. */
   if(!gtls_alpns_count && alpns.count) {
     size_t i;
@@ -1306,7 +1314,7 @@
   if(key)
     gnutls_pubkey_deinit(key);
 
-  Curl_safefree(buff1);
+  curlx_safefree(buff1);
 
   return result;
 }
@@ -1579,7 +1587,7 @@
   long * const certverifyresult = &ssl_config->certverifyresult;
 
   (void)cf;
-  /* This function will return the peer's raw certificate (chain) as sent by
+  /* This function returns the peer's raw certificate (chain) as sent by
      the peer. These certificates are in raw format (DER encoded for
      X.509). In case of a X.509 then a certificate list may be present. The
      first certificate in the list is the peer's certificate, following the
@@ -1611,7 +1619,7 @@
 
   if(data->set.ssl.certinfo && chain.certs) {
     if(chain.num_certs > MAX_ALLOWED_CERT_AMOUNT) {
-      failf(data, "%u certificates is more than allowed (%u)",
+      failf(data, "%u certificates is more than allowed (%d)",
             chain.num_certs, MAX_ALLOWED_CERT_AMOUNT);
       result = CURLE_SSL_CONNECT_ERROR;
       goto out;
@@ -1637,7 +1645,7 @@
   if(config->verifypeer) {
     bool verified = FALSE;
     unsigned int verify_status = 0;
-    /* This function will try to verify the peer's certificate and return
+    /* This function tries to verify the peer's certificate and return
        its status (trusted, invalid etc.). The value of status should be
        one or more of the gnutls_certificate_status_t enumerated elements
        bitwise or'd. To avoid denial of service attacks some default
@@ -1693,7 +1701,7 @@
 
   /* initialize an X.509 certificate structure. */
   if(gnutls_x509_crt_init(&x509_cert)) {
-    failf(data, "failed to init gnutls x509_crt");
+    failf(data, "failed to init GnuTLS x509_crt");
     *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND;
     result = CURLE_SSL_CONNECT_ERROR;
     goto out;
@@ -1777,7 +1785,7 @@
   if(config->issuercert) {
     gnutls_datum_t issuerp;
     if(gnutls_x509_crt_init(&x509_issuer)) {
-      failf(data, "failed to init gnutls x509_crt for issuer");
+      failf(data, "failed to init GnuTLS x509_crt for issuer");
       result = CURLE_SSL_ISSUER_ERROR;
       goto out;
     }
@@ -1796,7 +1804,7 @@
           config->issuercert ? config->issuercert : "none");
   }
 
-  /* This function will check if the given certificate's subject matches the
+  /* This function checks if the given certificate's subject matches the
      given hostname. This is a basic implementation of the matching described
      in RFC2818 (HTTPS), which takes into account wildcards, and the subject
      alternative name PKIX extension. Returns non zero on success, and zero on
@@ -1890,7 +1898,7 @@
       goto out;
     }
     else if(!n) {
-      /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in.
+      /* GnuTLS is buggy, it *SHOULD* return the amount of bytes it took in.
        * Instead it returns 0 if everything was written. */
       n = (ssize_t)blen;
     }
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h
index a5e55cd..49106dd 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.h
+++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -89,7 +89,7 @@
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const struct alpn_spec *alpns,
+                            const struct alpn_spec *alpns_requested,
                             Curl_gtls_ctx_setup_cb *cb_setup,
                             void *cb_user_data,
                             void *ssl_user_data,
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c
index 0206e32..4ae2387 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.c
+++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -22,27 +22,23 @@
  *
  ***************************************************************************/
 #include "curl_setup.h"
+#include "vtls/keylog.h"
 
-#if defined(USE_OPENSSL) || \
-  defined(USE_GNUTLS) || \
-  defined(USE_WOLFSSL) || \
-  (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
-  defined(USE_QUICHE) || \
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
   defined(USE_RUSTLS)
 
-#include "vtls/keylog.h"
 #include "escape.h"
 #include "curlx/fopen.h"
 
 /* The fp for the open SSLKEYLOGFILE, or NULL if not open */
 static FILE *keylog_file_fp;
+/* Used for verbose logging */
+static char *keylog_file_name;
 
 void Curl_tls_keylog_open(void)
 {
-  char *keylog_file_name;
-
   if(!keylog_file_fp) {
-    keylog_file_name = curl_getenv("SSLKEYLOGFILE");
+    keylog_file_name = getenv("SSLKEYLOGFILE");
     if(keylog_file_name) {
       keylog_file_fp = curlx_fopen(keylog_file_name, FOPEN_APPENDTEXT);
       if(keylog_file_fp) {
@@ -56,7 +52,6 @@
           keylog_file_fp = NULL;
         }
       }
-      Curl_safefree(keylog_file_name);
     }
   }
 }
@@ -74,6 +69,11 @@
   return keylog_file_fp != NULL;
 }
 
+const char *Curl_tls_keylog_file_name(void)
+{
+  return keylog_file_name;
+}
+
 bool Curl_tls_keylog_write_line(const char *line)
 {
   /* The current maximum valid keylog line length LF and NUL is 195. */
@@ -145,4 +145,16 @@
   return TRUE;
 }
 
-#endif /* TLS or QUIC backend */
+#else /* TLS backend */
+
+bool Curl_tls_keylog_enabled(void)
+{
+  return FALSE;
+}
+
+const char *Curl_tls_keylog_file_name(void)
+{
+  return NULL;
+}
+
+#endif  /* TLS backend */
diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h
index 7e741b9..68ded47 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.h
+++ b/Utilities/cmcurl/lib/vtls/keylog.h
@@ -53,12 +53,17 @@
 bool Curl_tls_keylog_enabled(void);
 
 /*
+ * Returns a pointer to the filename keys are being written to, if enabled.
+ */
+const char *Curl_tls_keylog_file_name(void);
+
+/*
  * Appends a key log file entry.
  * Returns true iff the key log file is open and a valid entry was provided.
  */
 bool Curl_tls_keylog_write(const char *label,
-                           const unsigned char client_random[32],
-                           const unsigned char *secret, size_t secretlen);
+                         const unsigned char client_random[CLIENT_RANDOM_SIZE],
+                         const unsigned char *secret, size_t secretlen);
 
 /*
  * Appends a line to the key log file, ensure it is terminated by an LF.
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 2bac406..e282266 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -265,9 +265,11 @@
 {
   uint16_t id = Curl_cipher_suite_walk_str(str, end);
   size_t len = *end - *str;
+  static const char ecjpake_suite[] = "TLS_ECJPAKE_WITH_AES_128_CCM_8";
 
   if(!id) {
-    if(curl_strnequal("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len))
+    if((len == sizeof(ecjpake_suite) - 1) &&
+       curl_strnequal(ecjpake_suite, *str, len))
       id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8;
   }
   return id;
@@ -423,7 +425,7 @@
     cert_count++;
 
   if(cert_count > MAX_ALLOWED_CERT_AMOUNT) {
-    infof(data, "Certificates is more than allowed (%u), skipping certinfo",
+    infof(data, "More certificates than allowed (%d), skipping certinfo",
           MAX_ALLOWED_CERT_AMOUNT);
     return;
   }
@@ -517,8 +519,8 @@
       curlx_free(newblob);
     }
 #else
-    /* DER encoded certs do not need to be null terminated because it is a
-       binary format. So if we are not compiling with PEM_PARSE we can avoid
+    /* DER encoded certs do not need to be null-terminated because it is a
+       binary format. Thus, if we are not compiling with PEM_PARSE we can avoid
        the extra memory copies altogether. */
     ret = mbedtls_x509_crt_parse_der(&backend->cacert, ca_info_blob->data,
                                      ca_info_blob->len);
@@ -630,8 +632,8 @@
       curlx_free(newblob);
     }
 #else
-    /* DER encoded certs do not need to be null terminated because it is a
-       binary format. So if we are not compiling with PEM_PARSE we can avoid
+    /* DER encoded certs do not need to be null-terminated because it is a
+       binary format. Thus, if we are not compiling with PEM_PARSE we can avoid
        the extra memory copies altogether. */
     ret = mbedtls_x509_crt_parse_der(&backend->clicert, ssl_cert_blob->data,
                                      ssl_cert_blob->len);
@@ -932,8 +934,8 @@
   if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ?
                               connssl->peer.sni : connssl->peer.hostname)) {
     /* 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. */
+       the name to set in the SNI extension. Thus even if curl connects to
+       a host specified as an IP address, this function must be used. */
     failf(data, "Failed to set SNI");
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -1210,7 +1212,7 @@
   connssl->io_need = CURL_SSL_IO_NEED_NONE;
   /* mbedTLS is picky when a mbedtls_ssl_write() was previously blocked.
    * It requires to be called with the same amount of bytes again, or it
-   * will lose bytes, e.g. reporting all was sent but they were not.
+   * loses bytes, e.g. reporting all was sent but they were not.
    * Remember the blocked length and use that when set. */
   if(backend->send_blocked) {
     DEBUGASSERT(backend->send_blocked_len <= len);
@@ -1353,15 +1355,15 @@
 
   (void)data;
   DEBUGASSERT(backend);
-  if(backend->initialized) {
-    mbedtls_pk_free(&backend->pk);
-    mbedtls_x509_crt_free(&backend->clicert);
-    mbedtls_x509_crt_free(&backend->cacert);
+  mbedtls_pk_free(&backend->pk);
+  mbedtls_x509_crt_free(&backend->clicert);
+  mbedtls_x509_crt_free(&backend->cacert);
 #ifdef MBEDTLS_X509_CRL_PARSE_C
-    mbedtls_x509_crl_free(&backend->crl);
+  mbedtls_x509_crl_free(&backend->crl);
 #endif
-    Curl_safefree(backend->ciphersuites);
-    mbedtls_ssl_config_free(&backend->config);
+  curlx_safefree(backend->ciphersuites);
+  mbedtls_ssl_config_free(&backend->config);
+  if(backend->initialized) {
     mbedtls_ssl_free(&backend->ssl);
     backend->initialized = FALSE;
   }
@@ -1511,11 +1513,9 @@
   ret = mbedtls_ctr_drbg_seed(&rng.drbg, mbedtls_entropy_func, &rng.entropy,
                               NULL, 0);
 
-  if(ret) {
-    failf(NULL, "failed: mbedtls_ctr_drbg_seed returned -0x%x",
-          (unsigned int)-ret);
+  if(ret)
+    /* mbedtls_ctr_drbg_seed returned error */
     return 0;
-  }
 
   /* To prevent an adversary from reading your random data,
      you can enable prediction resistance.
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index 50bf1e0..4629ca4 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -27,40 +27,7 @@
  */
 #include "curl_setup.h"
 
-#if defined(USE_QUICHE) || defined(USE_OPENSSL)
-
-#ifdef USE_WIN32_CRYPTO
-#include <wincrypt.h>
-/* If <wincrypt.h> is included directly, or indirectly via <schannel.h>,
- * <winldap.h>, <iphlpapi.h>, or something else, <wincrypt.h> does this:
- *   #define X509_NAME  ((LPCSTR)7)
- *
- * And in BoringSSL/AWC-LC's <openssl/base.h> there is:
- *  typedef struct X509_name_st X509_NAME;
- *  etc.
- *
- * The redefined symbols break these OpenSSL headers when included after
- * <wincrypt.h>.
- * The workaround is to undefine those defines here (and only here).
- *
- * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c,
- * to apply to other sources using OpenSSL includes. Each compilation unit
- * needs undefine them between the first <wincrypt.h> include and the first
- * OpenSSL include.
- *
- * OpenSSL does this in <openssl/ssl.h> and <openssl/x509v3.h>, but it
- * also does the #undef by including <openssl/ossl_typ.h>. <3.1.0 only does
- * it on the first include.
- *
- * LibreSSL automatically undefines these symbols before using them.
- */
-#undef X509_NAME
-#undef X509_EXTENSIONS
-#undef PKCS7_ISSUER_AND_SERIAL
-#undef PKCS7_SIGNER_INFO
-#undef OCSP_REQUEST
-#undef OCSP_RESPONSE
-#endif
+#ifdef USE_OPENSSL
 
 #include "urldata.h"
 #include "curl_trc.h"
@@ -70,6 +37,7 @@
 #include "curlx/inet_pton.h"
 #include "vtls/openssl.h"
 #include "connect.h"
+#include "cf-dns.h"
 #include "progress.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_int.h"
@@ -84,6 +52,9 @@
 #include "curlx/strcopy.h"
 #include "curlx/strdup.h"
 #include "vtls/apple.h"
+#ifdef USE_ECH
+#include "curlx/base64.h"
+#endif
 
 #include <openssl/rand.h>
 #include <openssl/x509v3.h>
@@ -308,7 +279,7 @@
 #else
   RSA_get0_key(rsa, &n, &e, NULL);
 #endif /* HAVE_EVP_PKEY_GET_PARAMS */
-  BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0);
+  BIO_printf(mem, "%d", (int)(n ? BN_num_bits(n) : 0));
   result = push_certinfo(data, mem, "RSA Public Key", i);
   if(!result) {
     result = print_pubkey_BN(rsa, n, i);
@@ -413,7 +384,7 @@
 
   numcerts = sk_X509_num(sk);
   if(numcerts > MAX_ALLOWED_CERT_AMOUNT) {
-    failf(data, "%d certificates is more than allowed (%u)", (int)numcerts,
+    failf(data, "%d certificates is more than allowed (%d)", (int)numcerts,
           MAX_ALLOWED_CERT_AMOUNT);
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -544,10 +515,6 @@
   return result;
 }
 
-#endif /* USE_QUICHE || USE_OPENSSL */
-
-#ifdef USE_OPENSSL
-
 static int ossl_bio_cf_create(BIO *bio)
 {
   BIO_set_shutdown(bio, 1);
@@ -658,7 +625,7 @@
   }
 
   /* Before returning server replies to the SSL instance, we need
-   * to have setup the x509 store or verification will fail. */
+   * to have setup the x509 store or verification fails. */
   if(!octx->x509_store_setup) {
     r2 = Curl_ssl_setup_x509_store(cf, data, octx);
     if(r2) {
@@ -1783,7 +1750,7 @@
   }
   OSSL_LIB_CTX_free(data->state.libctx);
   data->state.libctx = NULL;
-  Curl_safefree(data->state.propq);
+  curlx_safefree(data->state.propq);
   data->state.provider_loaded = FALSE;
 }
 
@@ -2138,9 +2105,9 @@
         case GEN_DNS: /* name/pattern comparison */
           /* The OpenSSL man page explicitly says: "In general it cannot be
              assumed that the data returned by ASN1_STRING_data() is null
-             terminated or does not contain embedded nulls." But also that
-             "The actual format of the data will depend on the actual string
-             type itself: for example for an IA5String the data will be ASCII"
+             terminated or does not contain embedded nulls.", but also that
+             "The actual format of the data depends on the actual string
+             type itself: for example for an IA5String the data is ASCII"
 
              It has been however verified that in 0.9.6 and 0.9.7, IA5String
              is always null-terminated.
@@ -2198,7 +2165,7 @@
         i = j;
     }
 
-    /* we have the name entry and we will now convert this to a string
+    /* we have the name entry and we now convert this to a string
        that we can use for comparison. Doing this we support BMPstring,
        UTF8, etc. */
 
@@ -2405,31 +2372,6 @@
 
 static const char *ssl_msg_type(int ssl_ver, int msg)
 {
-#ifdef SSL2_VERSION_MAJOR  /* OpenSSL 1.0.2, LibreSSL <=3.9.2 */
-  if(ssl_ver == SSL2_VERSION_MAJOR) {
-    switch(msg) {
-    case SSL2_MT_ERROR:
-      return "Error";
-    case SSL2_MT_CLIENT_HELLO:
-      return "Client hello";
-    case SSL2_MT_CLIENT_MASTER_KEY:
-      return "Client key";
-    case SSL2_MT_CLIENT_FINISHED:
-      return "Client finished";
-    case SSL2_MT_SERVER_HELLO:
-      return "Server hello";
-    case SSL2_MT_SERVER_VERIFY:
-      return "Server verify";
-    case SSL2_MT_SERVER_FINISHED:
-      return "Server finished";
-    case SSL2_MT_REQUEST_CERTIFICATE:
-      return "Request CERT";
-    case SSL2_MT_CLIENT_CERTIFICATE:
-      return "Client CERT";
-    }
-  }
-  else
-#endif
   if(ssl_ver == SSL3_VERSION_MAJOR) {
     switch(msg) {
     case SSL3_MT_HELLO_REQUEST:
@@ -2528,11 +2470,6 @@
     return;
 
   switch(ssl_ver) {
-#ifdef SSL2_VERSION /* removed in recent versions */
-  case SSL2_VERSION:
-    verstr = "SSLv2";
-    break;
-#endif
 #ifdef SSL3_VERSION
   case SSL3_VERSION:
     verstr = "SSLv3";
@@ -2583,7 +2520,7 @@
     ssl_ver >>= 8; /* check the upper 8 bits only below */
 
     /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL
-     * always pass-up content-type as 0. But the interesting message-type
+     * always pass-up content-type as 0, but the interesting message-type
      * is at 'buf[0]'.
      */
     if(ssl_ver == SSL3_VERSION_MAJOR && content_type)
@@ -2678,7 +2615,7 @@
   case CURL_SSLVERSION_MAX_DEFAULT:  /* max selected */
   default:
     /* SSL_CTX_set_max_proto_version states that: setting the maximum to 0
-       will enable protocol versions up to the highest version supported by
+       enables protocol versions up to the highest version supported by
        the library */
     ossl_ssl_version_max = 0;
     break;
@@ -2850,6 +2787,7 @@
 
 #ifdef USE_WIN32_CRYPTO
 static CURLcode ossl_win_load_store(struct Curl_easy *data,
+                                    struct Curl_cfilter *cf,
                                     const char *win_store,
                                     X509_STORE *store,
                                     bool *padded)
@@ -2862,11 +2800,13 @@
   hStore = CertOpenSystemStoreA(0, win_store);
   if(hStore) {
     PCCERT_CONTEXT pContext = NULL;
-    /* The array of enhanced key usage OIDs will vary per certificate and
+    /* The array of enhanced key usage OIDs varies 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;
+    VERBOSE(size_t total = 0);
+    VERBOSE(size_t imported = 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
@@ -2883,8 +2823,10 @@
       if(!pContext)
         break;
 
+      VERBOSE(++total);
+
 #if defined(DEBUGBUILD) && defined(CURLVERBOSE)
-      else {
+      {
         char cert_name[256];
         if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
                                NULL, cert_name, sizeof(cert_name)))
@@ -2972,6 +2914,7 @@
          such as duplicate certificate, which is allowed by MS but not
          OpenSSL. */
       if(X509_STORE_add_cert(store, x509) == 1) {
+        VERBOSE(++imported);
 #ifdef DEBUGBUILD
         infof(data, "SSL: Imported cert");
 #endif
@@ -2984,6 +2927,10 @@
     CertFreeCertificateContext(pContext);
     CertCloseStore(hStore, 0);
 
+    CURL_TRC_CF(data, cf,
+                "ossl_win_load_store() found: %zu imported: %zu certs in %s.",
+                total, imported, win_store);
+
     if(result)
       return result;
   }
@@ -2999,7 +2946,7 @@
   /* Import certificates from the Windows root certificate store if
      requested.
      https://stackoverflow.com/questions/9507184/
-     https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+     https://github.com/d3x0r/SACK/blob/ff15424d3c581b86d40f818532e5a400c516d39d/src/netlib/ssl_layer.c#L1410
      https://datatracker.ietf.org/doc/html/rfc5280 */
   const char *win_stores[] = {
     "ROOT",   /* Trusted Root Certification Authorities */
@@ -3011,7 +2958,7 @@
   *padded = FALSE;
   for(i = 0; i < CURL_ARRAYSIZE(win_stores); ++i) {
     bool store_added = FALSE;
-    result = ossl_win_load_store(data, win_stores[i], store, &store_added);
+    result = ossl_win_load_store(data, cf, win_stores[i], store, &store_added);
     if(result)
       return result;
     if(store_added) {
@@ -3126,7 +3073,7 @@
 
 #ifdef CURL_CA_FALLBACK
   if(octx->store_is_empty) {
-    /* verifying the peer without any CA certificates will not
+    /* verifying the peer without any CA certificates does not
        work so use OpenSSL's built-in default as fallback */
     X509_STORE_set_default_paths(store);
     infof(data, "  OpenSSL default paths (fallback)");
@@ -3428,7 +3375,7 @@
 #ifdef HAVE_OPENSSL_EARLYDATA
             if(ssl_config->earlydata && scs->alpn &&
                SSL_SESSION_get_max_early_data(ssl_session) &&
-               !cf->conn->connect_only &&
+               !cf->conn->bits.connect_only &&
                (SSL_version(octx->ssl) == TLS1_3_VERSION)) {
               bool do_early_data = FALSE;
               if(sess_reuse_cb) {
@@ -3477,6 +3424,16 @@
 }
 
 #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
+bool Curl_ossl_need_httpsrr(struct Curl_easy *data)
+{
+  if(!CURLECH_ENABLED(data))
+    return FALSE;
+  if((data->set.tls_ech & CURLECH_GREASE) ||
+     (data->set.tls_ech & CURLECH_CLA_CFG))
+   return FALSE;
+  return TRUE;
+}
+
 static CURLcode ossl_init_ech(struct ossl_ctx *octx,
                               struct Curl_cfilter *cf,
                               struct Curl_easy *data,
@@ -3486,9 +3443,9 @@
   size_t ech_config_len = 0;
   char *outername = data->set.str[STRING_ECH_PUBLIC];
   int trying_ech_now = 0;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
-  if(!ECH_ENABLED(data))
+  if(!CURLECH_ENABLED(data))
     return CURLE_OK;
 
   if(data->set.tls_ech & CURLECH_GREASE) {
@@ -3542,44 +3499,32 @@
     infof(data, "ECH: ECHConfig from command line");
   }
   else {
-    struct Curl_dns_entry *dns = NULL;
+    const struct Curl_https_rrinfo *rinfo =
+      Curl_conn_dns_get_https(data, cf->sockindex);
 
-    if(peer->hostname)
-      dns = Curl_dnscache_get(data, peer->hostname, peer->port,
-                              cf->conn->ip_version);
-    if(!dns) {
-      infof(data, "ECH: requested but no DNS info available");
-      if(data->set.tls_ech & CURLECH_HARD)
-        return CURLE_SSL_CONNECT_ERROR;
-    }
-    else {
-      struct Curl_https_rrinfo *rinfo = NULL;
+    if(rinfo && rinfo->echconfiglist) {
+      const unsigned char *ecl = rinfo->echconfiglist;
+      size_t elen = rinfo->echconfiglist_len;
 
-      rinfo = dns->hinfo;
-      if(rinfo && rinfo->echconfiglist) {
-        unsigned char *ecl = rinfo->echconfiglist;
-        size_t elen = rinfo->echconfiglist_len;
-
-        infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-        if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
-          infof(data, "ECH: SSL_set1_ech_config_list failed");
-          if(data->set.tls_ech & CURLECH_HARD)
-            return CURLE_SSL_CONNECT_ERROR;
-        }
-        else {
-          trying_ech_now = 1;
-          infof(data, "ECH: imported ECHConfigList of length %zu", elen);
-        }
-      }
-      else {
-        infof(data, "ECH: requested but no ECHConfig available");
+      infof(data, "ECH: ECHConfig from HTTPS RR");
+      if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
+        infof(data, "ECH: SSL_set1_ech_config_list failed");
         if(data->set.tls_ech & CURLECH_HARD)
           return CURLE_SSL_CONNECT_ERROR;
       }
-      Curl_resolv_unlink(data, &dns);
+      else {
+        trying_ech_now = 1;
+        infof(data, "ECH: imported ECHConfigList of length %zu", elen);
+      }
+    }
+    else {
+      infof(data, "ECH: requested but no ECHConfig available");
+      if(data->set.tls_ech & CURLECH_HARD)
+        return CURLE_SSL_CONNECT_ERROR;
     }
   }
 #ifdef HAVE_BORINGSSL_LIKE
+  (void)peer;
   if(trying_ech_now && outername) {
     infof(data, "ECH: setting public_name not supported with BoringSSL");
     return CURLE_SSL_CONNECT_ERROR;
@@ -3605,7 +3550,13 @@
 
   return CURLE_OK;
 }
-#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST */
+#else /* HAVE_SSL_SET1_ECH_CONFIG_LIST */
+bool Curl_ossl_need_httpsrr(struct Curl_easy *data)
+{
+  (void)data;
+  return FALSE;
+}
+#endif /* else HAVE_SSL_SET1_ECH_CONFIG_LIST */
 
 static CURLcode ossl_init_ssl(struct ossl_ctx *octx,
                               struct Curl_cfilter *cf,
@@ -3672,7 +3623,7 @@
     case CURL_SSLVERSION_TLSv1_1:
     case CURL_SSLVERSION_TLSv1_2:
     case CURL_SSLVERSION_TLSv1_3:
-      /* it will be handled later with the context options */
+      /* it is handled later with the context options */
       *pmethod = TLS_client_method();
       break;
     case CURL_SSLVERSION_SSLv2:
@@ -4077,7 +4028,7 @@
 #ifdef HAVE_SSL_SET0_WBIO
   /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works
    * without backward compat quirks. Every call takes one reference, so we
-   * up it and pass. SSL* then owns it and will free.
+   * up it and pass. SSL* then owns and frees it.
    * We check on the function in configure, since LibreSSL and friends
    * each have their own versions to add support for this. */
   BIO_up_ref(bio);
@@ -4100,8 +4051,8 @@
 
 #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
 /* If we have retry configs, then trace those out */
-static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl,
-                                         int reason)
+static int ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl,
+                                        int reason)
 {
   CURLcode result = CURLE_OK;
   size_t rcl = 0;
@@ -4117,10 +4068,11 @@
   size_t out_name_len = 0;
   int servername_type = 0;
 #endif
+  NOVERBOSE((void)reason);
 
   /* nothing to trace if not doing ECH */
-  if(!ECH_ENABLED(data))
-    return;
+  if(!CURLECH_ENABLED(data))
+    return rv;
 #ifndef HAVE_BORINGSSL_LIKE
   rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl);
 #else
@@ -4153,9 +4105,11 @@
   else
     infof(data, "ECH: no retry_configs (rv = %d)", rv);
 #ifndef HAVE_BORINGSSL_LIKE
-  OPENSSL_free((void *)rcs);
+  OPENSSL_free(inner);
+  OPENSSL_free(rcs);
+  OPENSSL_free(outer);
 #endif
-  return;
+  return rv;
 }
 
 #endif
@@ -4244,8 +4198,12 @@
       reason = ERR_GET_REASON(errdetail);
 
       if((lib == ERR_LIB_SSL) &&
-         ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
-          (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+         ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED)
+/* Missing from OpenSSL 4+ OPENSSL_NO_DEPRECATED_3_0 builds */
+#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED
+          || (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED)
+#endif
+         )) {
         result = CURLE_PEER_FAILED_VERIFICATION;
 
         lerr = SSL_get_verify_result(octx->ssl);
@@ -4317,51 +4275,52 @@
     Curl_ossl_report_handshake(data, octx);
 
 #if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) && !defined(HAVE_BORINGSSL_LIKE)
-    if(ECH_ENABLED(data)) {
+    if(CURLECH_ENABLED(data)) {
       char *inner = NULL, *outer = NULL;
-      const char *status = NULL;
       int rv;
+      VERBOSE(const char *status);
 
       rv = SSL_ech_get1_status(octx->ssl, &inner, &outer);
       switch(rv) {
       case SSL_ECH_STATUS_SUCCESS:
-        status = "succeeded";
+        VERBOSE(status = "succeeded");
         break;
       case SSL_ECH_STATUS_GREASE_ECH:
-        status = "sent GREASE, got retry-configs";
+        VERBOSE(status = "sent GREASE, got retry-configs");
         break;
       case SSL_ECH_STATUS_GREASE:
-        status = "sent GREASE";
+        VERBOSE(status = "sent GREASE");
         break;
       case SSL_ECH_STATUS_NOT_TRIED:
-        status = "not attempted";
+        VERBOSE(status = "not attempted");
         break;
       case SSL_ECH_STATUS_NOT_CONFIGURED:
-        status = "not configured";
+        VERBOSE(status = "not configured");
         break;
       case SSL_ECH_STATUS_BACKEND:
-        status = "backend (unexpected)";
+        VERBOSE(status = "backend (unexpected)");
         break;
       case SSL_ECH_STATUS_FAILED:
-        status = "failed";
+        VERBOSE(status = "failed");
         break;
       case SSL_ECH_STATUS_BAD_CALL:
-        status = "bad call (unexpected)";
+        VERBOSE(status = "bad call (unexpected)");
         break;
       case SSL_ECH_STATUS_BAD_NAME: {
         struct ssl_primary_config *conn_config =
           Curl_ssl_cf_get_primary_config(cf);
         if(!conn_config->verifypeer && !conn_config->verifyhost &&
            inner && !strcmp(inner, connssl->peer.hostname)) {
-          status = "bad name (tolerated without peer verification)";
+          VERBOSE(status = "bad name (tolerated without peer verification)");
           rv = SSL_ECH_STATUS_SUCCESS;
         }
-        else
-          status = "bad name (unexpected)";
+        else {
+          VERBOSE(status = "bad name (unexpected)");
+        }
         break;
       }
       default:
-        status = "unexpected status";
+        VERBOSE(status = "unexpected status");
         infof(data, "ECH: unexpected status %d", rv);
       }
       infof(data, "ECH: result: status is %s, inner is %s, outer is %s",
@@ -4437,7 +4396,7 @@
     /*
      * These checks are verifying we got back the same values as when we
      * sized the buffer. it is pretty weak since they should always be the
-     * same. But it gives us something to test.
+     * same, but it gives us something to test.
      */
     if((len1 != len2) || !temp || ((temp - buff1) != len1))
       break; /* failed */
@@ -4744,16 +4703,26 @@
     unsigned char *ocsp_data = NULL;
 #endif
     long ocsp_len = 0;
+    bool ocsp_missing = FALSE;
     if(conn_config->verifystatus && !octx->reused_session)
       ocsp_len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &ocsp_data);
 
     /* SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP
        response data or -1 if there is no OCSP response data. */
-    if(ocsp_len < 0)
+    if(ocsp_len < 0) {
       ocsp_len = 0; /* no data available */
+      ocsp_missing = TRUE;
+    }
     result = Curl_vtls_apple_verify(cf, data, peer, chain.num_certs,
                                     ossl_chain_get_der, &chain,
                                     ocsp_data, ocsp_len);
+    if(!result && ocsp_missing && conn_config->verifystatus &&
+       !octx->reused_session) {
+      /* verified, but OCSP stapling is required and server sent none */
+      *pverified = TRUE;
+      failf(data, "No OCSP response received");
+      return CURLE_SSL_INVALIDCERTSTATUS;
+    }
   }
   *pverified = !result;
   return result;
@@ -4992,6 +4961,11 @@
   connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
   if(ssl_connect_1 == connssl->connecting_state) {
+    if(Curl_ossl_need_httpsrr(data) &&
+       !Curl_conn_dns_resolved_https(data, cf->sockindex)) {
+      CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
+      return CURLE_OK;
+    }
     CURL_TRC_CF(data, cf, "ossl_connect, step1");
     result = ossl_connect_step1(cf, data);
     if(result)
@@ -5085,7 +5059,7 @@
   memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
   if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) {
     /* The previous SSL_write() call was blocked, using that length.
-     * We need to use that again or OpenSSL will freak out. A shorter
+     * We need to use that again or OpenSSL freaks out. A shorter
      * length should not happen and is a bug in libcurl. */
     if(octx->blocked_ssl_write_len > memlen) {
       DEBUGASSERT(0);
@@ -5237,7 +5211,7 @@
           result = CURLE_RECV_ERROR;
         }
         else {
-          /* We should no longer get here nowadays. But handle
+          /* We should no longer get here nowadays, but handle
            * the error in case of some weirdness in the OSSL stack */
           int sockerr = SOCKERRNO;
           if(sockerr)
@@ -5270,13 +5244,15 @@
   return result;
 }
 
-static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
+static CURLcode ossl_get_channel_binding(struct Curl_easy *data,
+                                         int sockindex,
                                          struct dynbuf *binding)
 {
   X509 *cert;
-  int algo_nid;
-  const EVP_MD *algo_type;
-  const char *algo_name;
+  int mdnid;
+  bool no_digest_acceptable = FALSE;
+  const EVP_MD *algo_type = NULL;
+  const char *algo_name = NULL;
   unsigned int length;
   unsigned char buf[EVP_MAX_MD_SIZE];
 
@@ -5308,43 +5284,108 @@
     /* No server certificate, do not do channel binding */
     return CURLE_OK;
 
-  if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) {
+#ifdef HAVE_OPENSSL3
+  {
+    int pknid, secbits;
+    uint32_t flags;
+    EVP_PKEY *pkey = X509_get0_pubkey(cert);
+
+    if(!X509_get_signature_info(cert, &mdnid, &pknid, &secbits, &flags)) {
+      failf(data, "certificate signature algorithm not recognized");
+      result = CURLE_SSL_INVALIDCERTSTATUS;
+      goto out;
+    }
+
+    if(mdnid != NID_undef) {
+      if(mdnid == NID_md5 || mdnid == NID_sha1) {
+        algo_type = EVP_sha256();
+      }
+      else
+        algo_type = EVP_get_digestbynid(mdnid);
+    }
+    else if(pkey && !EVP_PKEY_is_a(pkey, OBJ_nid2sn(pknid))) {
+      /* The cert's pkey is different from the algorithm used to sign
+       * the certificate. Since the reported `mdnid` is undefined, there
+       * is no digest algorithm available here. This happens in PQC
+       * and is accepted, resulting in no addition to the binding. */
+      no_digest_acceptable = TRUE;
+    }
+    else if(pkey) {
+      /* cert's pkey type is the same as the cert signer (or same family).
+       * Ask for the mandatory/advisory digest algorithm for the pkey.
+       */
+      char mdname[128] = "";
+      int rc = EVP_PKEY_get_default_digest_name(pkey, mdname, sizeof(mdname));
+      bool md_is_undef = !strcmp(mdname, "UNDEF");
+
+      if(rc == 2 && md_is_undef) {
+        /* OpenSSL declares "undef" the *mandatory* digest for this key.
+         * This is some PQC shit, accept it, no addition to binding. */
+        no_digest_acceptable = TRUE;
+      }
+      else if(rc > 0 && mdname[0] != '\0' && !md_is_undef) {
+        infof(data, "Digest algorithm : %s%s (derived from public key)"
+              ", but unavailable",
+              mdname, rc == 2 ? " [mandatory]" : " [advisory]");
+      }
+    }
+  }
+#else /* HAVE_OPENSSL3 */
+
+  if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, NULL)) {
     failf(data,
           "Unable to find digest NID for certificate signature algorithm");
     result = CURLE_SSL_INVALIDCERTSTATUS;
-    goto error;
+    goto out;
   }
 
   /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */
-  if(algo_nid == NID_md5 || algo_nid == NID_sha1) {
+  if(mdnid == NID_md5 || mdnid == NID_sha1) {
     algo_type = EVP_sha256();
   }
   else {
-    algo_type = EVP_get_digestbynid(algo_nid);
+    algo_type = EVP_get_digestbynid(mdnid);
     if(!algo_type) {
-      algo_name = OBJ_nid2sn(algo_nid);
+      algo_name = OBJ_nid2sn(mdnid);
       failf(data, "Could not find digest algorithm %s (NID %d)",
-            algo_name ? algo_name : "(null)", algo_nid);
+            algo_name ? algo_name : "(null)", mdnid);
       result = CURLE_SSL_INVALIDCERTSTATUS;
-      goto error;
+      goto out;
     }
   }
 
-  if(!X509_digest(cert, algo_type, buf, &length)) {
-    failf(data, "X509_digest() failed");
+#endif /* HAVE_OPENSSL3, else */
+
+  if(!algo_type) {
+    if(no_digest_acceptable) {
+      infof(data, "certificate exposes no signing digest algorithm, "
+            "nothing to add to channel binding");
+      result = CURLE_OK;
+      goto out;
+    }
+    /* unacceptable, something is wrong, fail */
+    algo_name = OBJ_nid2sn(mdnid);
+    failf(data, "Unable to find digest algorithm %s (NID %d) "
+          "for channel binding", algo_name ? algo_name : "(null)", mdnid);
     result = CURLE_SSL_INVALIDCERTSTATUS;
-    goto error;
+    goto out;
+  }
+
+  if(!X509_digest(cert, algo_type, buf, &length)) {
+    failf(data, "X509_digest() failed for channel binding");
+    result = CURLE_SSL_INVALIDCERTSTATUS;
+    goto out;
   }
 
   /* Append "tls-server-end-point:" */
   result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1);
   if(result)
-    goto error;
+    goto out;
 
   /* Append digest */
   result = curlx_dyn_addn(binding, buf, length);
 
-error:
+out:
   X509_free(cert);
   return result;
 }
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h
index cfffe9d..61d4a17 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.h
+++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -26,6 +26,40 @@
 #include "curl_setup.h"
 
 #ifdef USE_OPENSSL
+
+#ifdef USE_WIN32_CRYPTO
+#include <wincrypt.h>
+/* If <wincrypt.h> is included directly, or indirectly via <schannel.h>,
+ * <winldap.h>, <iphlpapi.h>, or something else, <wincrypt.h> does this:
+ *   #define X509_NAME  ((LPCSTR)7)
+ *
+ * In BoringSSL/AWC-LC's <openssl/base.h> there is:
+ *  typedef struct X509_name_st X509_NAME;
+ *  etc.
+ *
+ * The redefined symbols break these OpenSSL headers when included after
+ * <wincrypt.h>.
+ * The workaround is to undefine those defines here (and only here).
+ *
+ * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c,
+ * to apply to other sources using OpenSSL includes. Each compilation unit
+ * needs undefine them between the first <wincrypt.h> include and the first
+ * OpenSSL include.
+ *
+ * OpenSSL does this in <openssl/ssl.h> and <openssl/x509v3.h>, but it
+ * also does the #undef by including <openssl/ossl_typ.h>. <3.1.0 only does
+ * it on the first include.
+ *
+ * LibreSSL automatically undefines these symbols before using them.
+ */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif /* USE_WIN32_CRYPTO */
+
 /*
  * This header should only be needed to get included by vtls.c, openssl.c
  * and ngtcp2.c
@@ -113,6 +147,9 @@
                             void *ssl_user_data,
                             Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
 
+/* Is a resolved HTTPS-RR needed for initializing OpenSSL? */
+bool Curl_ossl_need_httpsrr(struct Curl_easy *data);
+
 #ifndef HAVE_OPENSSL3
 #define SSL_get1_peer_certificate SSL_get_peer_certificate
 #endif
@@ -121,7 +158,7 @@
 
 /**
  * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and
- * easy handle `data`. Will allow reuse of a shared cache if suitable
+ * easy handle `data`. Allows reuse of a shared cache if suitable
  * and configured.
  */
 CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index 061b444..d886c85 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -32,6 +32,7 @@
 #include "curlx/fopen.h"
 #include "curlx/strerr.h"
 #include "urldata.h"
+#include "cf-dns.h"
 #include "curl_trc.h"
 #include "httpsrr.h"
 #include "vtls/vtls.h"
@@ -40,6 +41,9 @@
 #include "vtls/keylog.h"
 #include "vtls/cipher_suite.h"
 #include "vtls/x509asn1.h"
+#ifdef USE_ECH
+#include "curlx/base64.h"
+#endif
 
 struct rustls_ssl_backend_data {
   const struct rustls_client_config *config;
@@ -294,8 +298,8 @@
  *    we get either an error or EAGAIN/EWOULDBLOCK.
  *
  * it is okay to call this function with plainbuf == NULL and plainlen == 0.
- * In that case, it will not read anything into Rustls' plaintext input buffer.
- * It will only drain Rustls' plaintext output buffer into the socket.
+ * In that case, it does not read anything into Rustls' plaintext input buffer.
+ * It only drains Rustls' plaintext output buffer into the socket.
  */
 static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                         const void *plainbuf, size_t plainlen,
@@ -369,7 +373,7 @@
     *pnwritten += (ssize_t)plainwritten;
 
 out:
-  CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zd",
+  CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu",
               plainlen, result, *pnwritten);
   return result;
 }
@@ -570,7 +574,7 @@
   }
 
 #ifdef USE_ECH
-  if(ECH_ENABLED(data)) {
+  if(CURLECH_ENABLED(data)) {
     tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
     tls_versions_len = 1;
     infof(data, "rustls: ECH enabled, forcing TLSv1.3");
@@ -906,16 +910,26 @@
 }
 
 #ifdef USE_ECH
+
+static bool cr_ech_need_httpsrr(struct Curl_easy *data)
+{
+  if(!CURLECH_ENABLED(data))
+    return FALSE;
+  if((data->set.tls_ech & CURLECH_GREASE) ||
+     (data->set.tls_ech & CURLECH_CLA_CFG))
+   return FALSE;
+  return TRUE;
+}
+
 static CURLcode
 init_config_builder_ech(struct Curl_easy *data,
-                        const struct ssl_connect_data *connssl,
+                        struct Curl_cfilter *cf,
                         struct rustls_client_config_builder *builder)
 {
   const rustls_hpke *hpke = rustls_supported_hpke();
   unsigned char *ech_config = NULL;
   size_t ech_config_len = 0;
   struct Curl_dns_entry *dns = NULL;
-  struct Curl_https_rrinfo *rinfo = NULL;
   CURLcode result = CURLE_OK;
   rustls_result rr;
 
@@ -960,16 +974,9 @@
     }
   }
   else {
-    if(connssl->peer.hostname) {
-      dns = Curl_dnscache_get(data, connssl->peer.hostname,
-                              connssl->peer.port, data->conn->ip_version);
-    }
-    if(!dns) {
-      failf(data, "rustls: ECH requested but no DNS info available");
-      result = CURLE_SSL_CONNECT_ERROR;
-      goto cleanup;
-    }
-    rinfo = dns->hinfo;
+    const struct Curl_https_rrinfo *rinfo =
+      Curl_conn_dns_get_https(data, cf->sockindex);
+
     if(!rinfo || !rinfo->echconfiglist) {
       failf(data, "rustls: ECH requested but no ECHConfig available");
       result = CURLE_SSL_CONNECT_ERROR;
@@ -994,7 +1001,7 @@
     curlx_free(ech_config);
   }
   if(dns) {
-    Curl_resolv_unlink(data, &dns);
+    Curl_dns_entry_unlink(data, &dns);
   }
   return result;
 }
@@ -1065,8 +1072,8 @@
   }
 
 #ifdef USE_ECH
-  if(ECH_ENABLED(data)) {
-    result = init_config_builder_ech(data, connssl, config_builder);
+  if(CURLECH_ENABLED(data)) {
+    result = init_config_builder_ech(data, cf, config_builder);
     if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
       rustls_client_config_builder_free(config_builder);
       return result;
@@ -1117,7 +1124,7 @@
 
 /* Given an established network connection, do a TLS handshake.
  *
- * This function will set `*done` to true once the handshake is complete.
+ * This function sets `*done` to true once the handshake is complete.
  * This function never reads the value of `*done*`.
  */
 static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -1128,22 +1135,33 @@
     (struct rustls_ssl_backend_data *)connssl->backend;
   const struct rustls_connection *rconn = NULL;
   CURLcode tmperr = CURLE_OK;
-  int result;
+  CURLcode result;
   bool wants_read;
   bool wants_write;
+  ssize_t nread;
 
   DEBUGASSERT(backend);
 
   CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
   *done = FALSE;
 
-  if(!backend->conn) {
-    result = cr_init_backend(cf, data,
-               (struct rustls_ssl_backend_data *)connssl->backend);
-    CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
-    if(result != CURLE_OK) {
-      return result;
+#ifdef USE_ECH
+    /* if we do ECH and need the HTTPS-RR information for it,
+     * we delay the connect until it arrives or DNS resolve fails. */
+    if(cr_ech_need_httpsrr(data) &&
+       !Curl_conn_dns_resolved_https(data, cf->sockindex)) {
+      CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
+      return CURLE_OK;
     }
+#endif /* USE_ECH */
+
+  if(!backend->conn) {
+    result =
+      cr_init_backend(cf, data,
+                      (struct rustls_ssl_backend_data *)connssl->backend);
+    CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
+    if(result)
+      return result;
     connssl->state = ssl_connection_negotiating;
   }
   rconn = backend->conn;
@@ -1199,7 +1217,7 @@
         while(rustls_connection_get_peer_certificate(rconn, num_certs)) {
           num_certs++;
           if(num_certs > MAX_ALLOWED_CERT_AMOUNT) {
-            failf(data, "%zu certificates is more than allowed (%u)",
+            failf(data, "%zu certificates is more than allowed (%d)",
                   num_certs, MAX_ALLOWED_CERT_AMOUNT);
             return CURLE_SSL_CONNECT_ERROR;
           }
@@ -1263,7 +1281,13 @@
 
     if(wants_read) {
       CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls.");
-      if(tls_recv_more(cf, data, &tmperr) < 0) {
+      nread = tls_recv_more(cf, data, &tmperr);
+      if(nread == 0) {
+        connssl->peer_closed = TRUE;
+        failf(data, "TLS connect error: Connection closed abruptly");
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+      if(nread < 0) {
         if(tmperr == CURLE_AGAIN) {
           CURL_TRC_CF(data, cf, "reading would block");
           connssl->io_need = CURL_SSL_IO_NEED_RECV;
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 650a0e3..fb5ff0e 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -175,20 +175,20 @@
   for(; i <= (ssl_version_max >> 16); ++i) {
     switch(i) {
     case CURL_SSLVERSION_TLSv1_0:
-      (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT;
+      *enabled_protocols |= SP_PROT_TLS1_0_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_1:
-      (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT;
+      *enabled_protocols |= SP_PROT_TLS1_1_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_2:
-      (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT;
+      *enabled_protocols |= SP_PROT_TLS1_2_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_3:
 
       /* Windows Server 2022 and newer */
       if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
                                       VERSION_GREATER_THAN_EQUAL)) {
-        (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT;
+        *enabled_protocols |= SP_PROT_TLS1_3_CLIENT;
         break;
       }
       else { /* Windows 10 and older */
@@ -277,7 +277,7 @@
   return 0; /* not found */
 }
 
-#define NUM_CIPHERS 47 /* There are 47 options listed above */
+#define NUM_CIPHERS 47 /* There are a maximum of 47 options listed above */
 
 static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
                                 ALG_ID *algIds)
@@ -636,7 +636,7 @@
   }
   else {
     /* Pre-Windows 10 1809 or the user set a legacy algorithm list.
-       Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
+       Schannel does not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
     ALG_ID algIds[NUM_CIPHERS];
     char *ciphers = conn_config->cipher_list;
     SCHANNEL_CRED schannel_cred = { 0 };
@@ -914,18 +914,18 @@
     unsigned short *list_len = NULL;
     struct alpn_proto_buf proto;
 
-    /* The first four bytes will be an unsigned int indicating number
+    /* The first four bytes is an unsigned int indicating number
        of bytes of data in the rest of the buffer. */
     extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]);
     cur += (int)sizeof(unsigned int);
 
-    /* The next four bytes are an indicator that this buffer will contain
+    /* The next four bytes are an indicator that this buffer contains
        ALPN data, as opposed to NPN, for example. */
     *(unsigned int *)(void *)&alpn_buffer[cur] =
       SecApplicationProtocolNegotiationExt_ALPN;
     cur += (int)sizeof(unsigned int);
 
-    /* The next two bytes will be an unsigned short indicating the number
+    /* The next two bytes is an unsigned short indicating the number
        of bytes used to list the preferred protocols. */
     list_len = (unsigned short *)(void *)(&alpn_buffer[cur]);
     cur += (int)sizeof(unsigned short);
@@ -994,7 +994,7 @@
 
   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
     char buffer[STRERROR_LEN];
-    Curl_safefree(backend->ctxt);
+    curlx_safefree(backend->ctxt);
     switch(sspi_status) {
     case SEC_E_INSUFFICIENT_MEMORY:
       failf(data, "schannel: initial InitializeSecurityContext failed: %s",
@@ -1330,12 +1330,12 @@
     memcpy(inbuf[0].pvBuffer, backend->encdata.buffer,
            backend->encdata.offset);
 
-    /* The socket must be writeable (or a poll error occurred) before we call
+    /* The socket must be writable (or a poll error occurred) before we call
        InitializeSecurityContext to continue processing the received TLS
        records. This is because that function is not idempotent and we do not
        support partial save/resume sending replies of handshake tokens. */
     if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) {
-      SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket"));
+      SCH_DEV(infof(data, "schannel: handshake waiting for writable socket"));
       connssl->io_need = CURL_SSL_IO_NEED_SEND;
       curlx_free(inbuf[0].pvBuffer);
       return CURLE_OK;
@@ -1348,7 +1348,7 @@
       &outbuf_desc, &backend->ret_flags, NULL);
 
     /* free buffer for received handshake data */
-    Curl_safefree(inbuf[0].pvBuffer);
+    curlx_safefree(inbuf[0].pvBuffer);
 
     /* check if the handshake was incomplete */
     switch(sspi_status) {
@@ -1394,7 +1394,7 @@
     case SEC_I_INCOMPLETE_CREDENTIALS:
       if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
         /* If the server has requested a client certificate, attempt to
-           continue the handshake without one. This will allow connections to
+           continue the handshake without one. This allows connections to
            servers which request a client certificate but do not require
            it. */
         backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
@@ -1475,7 +1475,7 @@
   }
 
   /* Verify the hostname manually when certificate verification is disabled,
-     because in that case Schannel will not verify it. */
+     because in that case Schannel does not verify it. */
   if(!conn_config->verifypeer && conn_config->verifyhost)
     return Curl_verify_host(cf, data);
 
@@ -1568,7 +1568,7 @@
         CertCloseStore(cred->client_cert_store, 0);
         cred->client_cert_store = NULL;
       }
-      Curl_safefree(cred);
+      curlx_safefree(cred);
     }
   }
 }
@@ -1670,7 +1670,7 @@
 
     traverse_cert_store(ccert_context, cert_counter_callback, &certs_count);
     if(certs_count > MAX_ALLOWED_CERT_AMOUNT) {
-      failf(data, "%d certificates is more than allowed (%u)",
+      failf(data, "%d certificates is more than allowed (%d)",
             certs_count, MAX_ALLOWED_CERT_AMOUNT);
       CertFreeCertificateContext(ccert_context);
       return CURLE_SSL_CONNECT_ERROR;
@@ -1757,7 +1757,13 @@
   SCH_RENEG_CALLER_IS_SEND
 };
 
-#define MAX_RENEG_BLOCK_TIME (7 * 1000) /* 7 seconds in milliseconds */
+/* The maximum time we allow for Schannel renegotiation which may in some
+   rare cases block either due to libcurl (waiting on the socket) or Windows
+   (waiting on an interactive security prompt). Note Schannel "renegotiation"
+   is not necessarily literal TLS renegotiation, but means DecryptMessage
+   returned SEC_I_RENEGOTIATE which means at least the security context needs
+   to be re-established. */
+#define MAX_RENEG_BLOCK_TIME (60 * 1000) /* 60 seconds in milliseconds */
 
 /* This function renegotiates the connection due to a server request received
    by schannel_recv. This function returns CURLE_AGAIN if the renegotiation is
@@ -1845,11 +1851,11 @@
      * occur if the user is waiting on the socket only in one direction.
      *
      * For example, if the user has called recv then they may not be waiting
-     * for a writeable socket and vice versa, so we block to avoid that.
+     * for a writable socket and vice versa, so we block to avoid that.
      *
      * In practice a wait is unlikely to occur. For caller recv if handshake
-     * data needs to be sent then we block for a writeable socket that should
-     * be writeable immediately except for OS resource constraints. For caller
+     * data needs to be sent then we block for a writable socket that should
+     * be writable immediately except for OS resource constraints. For caller
      * send if handshake data needs to be received then we block for a readable
      * socket, which could take some time, but it is more likely the user has
      * called recv since they had called it prior (only recv can start
@@ -1905,7 +1911,7 @@
       SCH_DEV(infof(data, "schannel: renegotiation wait until socket is"
                     "%s%s for up to %" FMT_TIMEDIFF_T " ms",
                     ((readfd != CURL_SOCKET_BAD) ? " readable" : ""),
-                    ((writefd != CURL_SOCKET_BAD) ? " writeable" : ""),
+                    ((writefd != CURL_SOCKET_BAD) ? " writable" : ""),
                     timeout_ms));
 
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout_ms);
@@ -2028,7 +2034,7 @@
       sent. The unwritten encrypted bytes would be the first bytes to
       send on the next invocation.
       Here's the catch with this - if we tell the client that all the
-      bytes have been sent, will the client call this method again to
+      bytes have been sent, does the client call this method again to
       send the buffered data?  Looking at who calls this function, it
       seems the answer is NO.
     */
@@ -2082,7 +2088,7 @@
     result = CURLE_SEND_ERROR;
   }
 
-  Curl_safefree(ptr);
+  curlx_safefree(ptr);
 
   if(len == *pnwritten)
     /* Encrypted message including header, data and trailer entirely sent.
@@ -2550,7 +2556,7 @@
   if(backend->ctxt) {
     DEBUGF(infof(data, "schannel: clear security context handle"));
     Curl_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle);
-    Curl_safefree(backend->ctxt);
+    curlx_safefree(backend->ctxt);
   }
 
   /* free SSPI Schannel API credential handle */
@@ -2563,7 +2569,7 @@
 
   /* free internal buffer for received encrypted data */
   if(backend->encdata.buffer) {
-    Curl_safefree(backend->encdata.buffer);
+    curlx_safefree(backend->encdata.buffer);
     backend->encdata.length = 0;
     backend->encdata.offset = 0;
     backend->encdata_is_incomplete = FALSE;
@@ -2571,7 +2577,7 @@
 
   /* free internal buffer for received decrypted data */
   if(backend->decdata.buffer) {
-    Curl_safefree(backend->decdata.buffer);
+    curlx_safefree(backend->decdata.buffer);
     backend->decdata.length = 0;
     backend->decdata.offset = 0;
   }
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index 414b2e9..9be6fe3 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -262,7 +262,7 @@
 
   /*
    * Read the CA file completely into memory before parsing it. This
-   * optimizes for the common case where the CA file will be relatively
+   * optimizes for the common case where the CA file is relatively
    * small ( < 1 MiB ).
    */
   ca_file_handle = curlx_CreateFile(ca_file,
@@ -290,7 +290,7 @@
   }
 
   if(file_size.QuadPart > MAX_CAFILE_SIZE) {
-    failf(data, "schannel: CA file exceeds max size of %u bytes",
+    failf(data, "schannel: CA file exceeds max size of %d bytes",
           MAX_CAFILE_SIZE);
     result = CURLE_SSL_CACERT_BADFILE;
     goto cleanup;
@@ -336,7 +336,7 @@
   if(ca_file_handle != INVALID_HANDLE_VALUE) {
     CloseHandle(ca_file_handle);
   }
-  Curl_safefree(ca_file_buffer);
+  curlx_safefree(ca_file_buffer);
 
   return result;
 }
@@ -366,7 +366,7 @@
 #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG
 #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2
 #endif
-    /* CertGetNameString will provide the 8-bit character string without
+    /* CertGetNameString provides the 8-bit character string without
      * any decoding */
     DWORD name_flags =
       CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG;
@@ -438,13 +438,12 @@
 
 static bool get_num_host_info(struct num_ip_data *ip_blob, LPCSTR hostname)
 {
-  bool result = FALSE;
   struct in_addr ia;
   int res = curlx_inet_pton(AF_INET, hostname, &ia);
   if(res) {
     ip_blob->size = sizeof(struct in_addr);
     memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr));
-    result = TRUE;
+    return TRUE;
   }
   else {
     struct in6_addr ia6;
@@ -452,10 +451,10 @@
     if(res) {
       ip_blob->size = sizeof(struct in6_addr);
       memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr));
-      result = TRUE;
+      return TRUE;
     }
   }
-  return result;
+  return FALSE;
 }
 
 static bool get_alt_name_info(struct Curl_easy *data,
@@ -463,20 +462,19 @@
                               PCERT_ALT_NAME_INFO *alt_name_info,
                               LPDWORD alt_name_info_size)
 {
-  bool result = FALSE;
   PCERT_INFO cert_info = NULL;
   PCERT_EXTENSION extension = NULL;
   CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL };
 
   if(!ctx) {
     failf(data, "schannel: Null certificate context.");
-    return result;
+    return FALSE;
   }
 
   cert_info = ctx->pCertInfo;
   if(!cert_info) {
     failf(data, "schannel: Null certificate info.");
-    return result;
+    return FALSE;
   }
 
   extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
@@ -484,7 +482,7 @@
                                 cert_info->rgExtension);
   if(!extension) {
     failf(data, "schannel: CertFindExtension() returned no extension.");
-    return result;
+    return FALSE;
   }
 
   if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
@@ -496,11 +494,10 @@
                           alt_name_info,
                           alt_name_info_size)) {
     failf(data, "schannel: CryptDecodeObjectEx() returned no alternate name "
-                "information.");
-    return result;
+          "information.");
+    return FALSE;
   }
-  result = TRUE;
-  return result;
+  return TRUE;
 }
 
 /* Verify the server's hostname */
@@ -572,7 +569,7 @@
       goto cleanup;
     }
 
-    /* CertGetNameString guarantees that the returned name will not contain
+    /* CertGetNameString guarantees that the returned name does not contain
      * embedded null bytes. This appears to be undocumented behavior.
      */
     cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR));
@@ -650,7 +647,7 @@
 
 cleanup:
   LocalFree(alt_name_info);
-  Curl_safefree(cert_hostname_buff);
+  curlx_safefree(cert_hostname_buff);
 
   if(pCertContextServer)
     CertFreeCertificateContext(pCertContextServer);
@@ -763,7 +760,7 @@
       else
         engine_config.cbSize = sizeof(struct cert_chain_engine_config_win7);
 
-      /* CertCreateCertificateChainEngine will check the expected size of the
+      /* CertCreateCertificateChainEngine checks the expected size of the
        * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size
        * does not match the expected size. When this occurs, it indicates that
        * CAINFO is not supported on the version of Windows in use.
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index f7201d1..891facf 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -50,6 +50,7 @@
 #include "vtls/vtls.h" /* generic SSL protos etc */
 #include "vtls/vtls_int.h"
 #include "vtls/vtls_scache.h"
+#include "vtls/keylog.h"
 
 #include "vtls/openssl.h"        /* OpenSSL versions */
 #include "vtls/gtls.h"           /* GnuTLS versions */
@@ -272,22 +273,22 @@
 
 static void free_primary_ssl_config(struct ssl_primary_config *sslc)
 {
-  Curl_safefree(sslc->CApath);
-  Curl_safefree(sslc->CAfile);
-  Curl_safefree(sslc->issuercert);
-  Curl_safefree(sslc->clientcert);
-  Curl_safefree(sslc->cipher_list);
-  Curl_safefree(sslc->cipher_list13);
-  Curl_safefree(sslc->pinned_key);
-  Curl_safefree(sslc->cert_blob);
-  Curl_safefree(sslc->ca_info_blob);
-  Curl_safefree(sslc->issuercert_blob);
-  Curl_safefree(sslc->curves);
-  Curl_safefree(sslc->signature_algorithms);
-  Curl_safefree(sslc->CRLfile);
+  curlx_safefree(sslc->CApath);
+  curlx_safefree(sslc->CAfile);
+  curlx_safefree(sslc->issuercert);
+  curlx_safefree(sslc->clientcert);
+  curlx_safefree(sslc->cipher_list);
+  curlx_safefree(sslc->cipher_list13);
+  curlx_safefree(sslc->pinned_key);
+  curlx_safefree(sslc->cert_blob);
+  curlx_safefree(sslc->ca_info_blob);
+  curlx_safefree(sslc->issuercert_blob);
+  curlx_safefree(sslc->curves);
+  curlx_safefree(sslc->signature_algorithms);
+  curlx_safefree(sslc->CRLfile);
 #ifdef USE_TLS_SRP
-  Curl_safefree(sslc->username);
-  Curl_safefree(sslc->password);
+  curlx_safefree(sslc->username);
+  curlx_safefree(sslc->password);
 #endif
 }
 
@@ -395,7 +396,7 @@
 CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data,
                                    struct connectdata *conn)
 {
-  /* Clone "primary" SSL configurations from the esay handle to
+  /* Clone "primary" SSL configurations from the easy 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))
@@ -520,7 +521,7 @@
 static void cf_ctx_free(struct ssl_connect_data *ctx)
 {
   if(ctx) {
-    Curl_safefree(ctx->negotiated.alpn);
+    curlx_safefree(ctx->negotiated.alpn);
     Curl_bufq_free(&ctx->earlydata);
     curlx_free(ctx->backend);
     curlx_free(ctx);
@@ -787,7 +788,7 @@
       encode = curlx_base64_encode(sha256sumdigest,
                                    CURL_SHA256_DIGEST_LENGTH,
                                    &cert_hash, &cert_hash_len);
-    Curl_safefree(sha256sumdigest);
+    curlx_safefree(sha256sumdigest);
 
     if(encode)
       return encode;
@@ -802,7 +803,7 @@
       pinned_hash_len = end_pos ?
                         (size_t)(end_pos - pinned_hash) : strlen(pinned_hash);
 
-      /* compare base64 sha256 digests" */
+      /* compare base64 sha256 digests */
       if(cert_hash_len == pinned_hash_len &&
          !memcmp(cert_hash, pinned_hash, cert_hash_len)) {
         DEBUGF(infof(data, "public key hash matches pinned value"));
@@ -815,14 +816,14 @@
       /* next one or we are at the end */
       pinned_hash = end_pos ? (end_pos + 1) : NULL;
     }
-    Curl_safefree(cert_hash);
+    curlx_safefree(cert_hash);
   }
   else {
     long filesize;
     size_t size, pem_len;
     CURLcode pem_read;
     struct dynbuf buf;
-    char unsigned *pem_ptr = NULL;
+    unsigned char *pem_ptr = NULL;
     size_t left;
     FILE *fp = curlx_fopen(pinnedpubkey, "rb");
     if(!fp)
@@ -869,7 +870,7 @@
     }
 
     /*
-     * Otherwise we will assume it is PEM and try to decode it after placing
+     * Otherwise we assume it is PEM and try to decode it after placing
      * null-terminator
      */
     pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len);
@@ -885,7 +886,7 @@
       result = CURLE_OK;
 end:
     curlx_dyn_free(&buf);
-    Curl_safefree(pem_ptr);
+    curlx_safefree(pem_ptr);
     curlx_fclose(fp);
   }
 
@@ -1179,12 +1180,12 @@
 
 void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
 {
-  Curl_safefree(peer->sni);
+  curlx_safefree(peer->sni);
   if(peer->dispname != peer->hostname)
     curlx_free(peer->dispname);
   peer->dispname = NULL;
-  Curl_safefree(peer->hostname);
-  Curl_safefree(peer->scache_key);
+  curlx_safefree(peer->hostname);
+  curlx_safefree(peer->scache_key);
   peer->type = CURL_SSL_PEER_DNS;
 }
 
@@ -1221,7 +1222,7 @@
 CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
                             struct Curl_cfilter *cf,
                             const char *tls_id,
-                            int transport)
+                            uint8_t transport)
 {
   const char *ehostname, *edispname;
   CURLcode result = CURLE_OUT_OF_MEMORY;
@@ -1248,7 +1249,7 @@
   {
     ehostname = cf->conn->host.name;
     edispname = cf->conn->host.dispname;
-    peer->port = cf->conn->remote_port;
+    peer->port = (uint16_t)cf->conn->remote_port;
   }
 
   /* hostname MUST exist and not be empty */
@@ -1367,6 +1368,13 @@
     if(connssl->state == ssl_connection_complete) {
       connssl->handshake_done = *Curl_pgrs_now(data);
     }
+    if(Curl_tls_keylog_enabled()) {
+      infof(data, "SSLKEYLOGFILE set, all TLS secrets are logged to '%s'",
+            Curl_tls_keylog_file_name());
+#ifdef LIBRESSL_VERSION_NUMBER
+      infof(data, "Note LibreSSL only supports SSLKEYLOGFILE for TLS <= 1.2");
+#endif
+    }
     /* Connection can be deferred when sending early data */
     DEBUGASSERT(connssl->state == ssl_connection_complete ||
                 connssl->state == ssl_connection_deferred);
@@ -1393,7 +1401,7 @@
     if(blen > connssl->earlydata_max)
       blen = connssl->earlydata_max;
     result = Curl_bufq_write(&connssl->earlydata, buf, blen, &nwritten);
-    CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd",
+    CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zu",
                 blen, nwritten);
     if(result)
       return result;
@@ -1455,16 +1463,16 @@
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct cf_call_data save;
-  bool result;
+  bool pending;
 
   CF_DATA_SAVE(save, cf, data);
   if(connssl->ssl_impl->data_pending &&
      connssl->ssl_impl->data_pending(cf, data))
-    result = TRUE;
+    pending = TRUE;
   else
-    result = cf->next->cft->has_data_pending(cf->next, data);
+    pending = cf->next->cft->has_data_pending(cf->next, data);
   CF_DATA_RESTORE(cf, save);
-  return result;
+  return pending;
 }
 
 static CURLcode ssl_cf_send(struct Curl_cfilter *cf,
@@ -1684,7 +1692,7 @@
 
 struct Curl_cftype Curl_cft_ssl_proxy = {
   "SSL-PROXY",
-  CF_TYPE_SSL|CF_TYPE_PROXY,
+  CF_TYPE_SSL | CF_TYPE_PROXY,
   CURL_LOG_LVL_NONE,
   ssl_cf_destroy,
   ssl_cf_connect,
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index b9335bb..6db67cf 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -49,8 +49,14 @@
 #define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */
 
 #ifdef USE_ECH
-#include "curlx/base64.h"
-#define ECH_ENABLED(data) \
+/* CURLECH_ bits for the tls_ech option */
+#define CURLECH_DISABLE    (1 << 0)
+#define CURLECH_GREASE     (1 << 1)
+#define CURLECH_ENABLE     (1 << 2)
+#define CURLECH_HARD       (1 << 3)
+#define CURLECH_CLA_CFG    (1 << 4)
+
+#define CURLECH_ENABLED(data) \
   ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE))
 #endif /* USE_ECH */
 
@@ -90,8 +96,8 @@
   char *sni;             /* SNI version of hostname or NULL if not usable */
   char *scache_key;      /* for lookups in session cache */
   ssl_peer_type type;    /* type of the peer information */
-  int port;              /* port we are talking to */
-  int transport;         /* one of TRNSPRT_* defines */
+  uint16_t port;         /* port we are talking to */
+  uint8_t transport;     /* one of TRNSPRT_* defines */
 };
 
 CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
@@ -134,7 +140,7 @@
                                 bool proxy);
 
 /* Update certain connection SSL config flags after they have
- * been changed on the easy handle. Will work for `verifypeer`,
+ * been changed on the easy handle. Works for `verifypeer`,
  * `verifyhost` and `verifystatus`. */
 void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
 
@@ -144,7 +150,7 @@
 CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
                             struct Curl_cfilter *cf,
                             const char *tls_id,
-                            int transport);
+                            uint8_t transport);
 /**
  * Free all allocated data and reset peer information.
  */
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.c b/Utilities/cmcurl/lib/vtls/vtls_scache.c
index 673abcf..59fa256 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_scache.c
+++ b/Utilities/cmcurl/lib/vtls/vtls_scache.c
@@ -200,7 +200,7 @@
   }
 
   if(ssl->version || ssl->version_max) {
-    r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
+    r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version,
                        (ssl->version_max >> 16));
     if(r)
       goto out;
@@ -238,11 +238,6 @@
     r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
     if(r)
       goto out;
-    if(ssl->cert_blob) {
-      r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
-      if(r)
-        goto out;
-    }
     if(ssl->ca_info_blob) {
       r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
       if(r)
@@ -254,6 +249,11 @@
         goto out;
     }
   }
+  if(ssl->cert_blob) {
+    r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
+    if(r)
+      goto out;
+  }
   if(ssl->pinned_key && ssl->pinned_key[0]) {
     r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
     if(r)
@@ -301,6 +301,7 @@
   size_t peer_count;
   int default_lifetime_secs;
   long age;
+  BIT(is_locked);
 };
 
 static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
@@ -401,12 +402,12 @@
     peer->sobj = NULL;
   }
   peer->sobj_free = NULL;
-  Curl_safefree(peer->clientcert);
+  curlx_safefree(peer->clientcert);
 #ifdef USE_TLS_SRP
-  Curl_safefree(peer->srp_username);
-  Curl_safefree(peer->srp_password);
+  curlx_safefree(peer->srp_username);
+  curlx_safefree(peer->srp_password);
 #endif
-  Curl_safefree(peer->ssl_peer_key);
+  curlx_safefree(peer->ssl_peer_key);
   peer->age = 0;
   peer->hmac_set = FALSE;
 }
@@ -583,15 +584,26 @@
 /* Lock shared SSL session data */
 void Curl_ssl_scache_lock(struct Curl_easy *data)
 {
-  if(CURL_SHARE_ssl_scache(data))
-    Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+  if(scache) {
+    if(CURL_SHARE_ssl_scache(data))
+      Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION,
+                      CURL_LOCK_ACCESS_SINGLE);
+    DEBUGASSERT(!scache->is_locked);
+    scache->is_locked = TRUE;
+  }
 }
 
 /* Unlock shared SSL session data */
 void Curl_ssl_scache_unlock(struct Curl_easy *data)
 {
-  if(CURL_SHARE_ssl_scache(data))
-    Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+  if(scache) {
+    DEBUGASSERT(scache->is_locked);
+    scache->is_locked = FALSE;
+    if(CURL_SHARE_ssl_scache(data))
+      Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+  }
 }
 
 static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
@@ -734,12 +746,19 @@
 
   peer = cf_ssl_get_free_peer(scache);
   if(peer) {
+    char buffer[64];
     const char *ccert = conn_config ? conn_config->clientcert : NULL;
     const char *username = NULL, *password = NULL;
 #ifdef USE_TLS_SRP
     username = conn_config ? conn_config->username : NULL;
     password = conn_config ? conn_config->password : NULL;
 #endif
+    if(!ccert && conn_config && conn_config->cert_blob) {
+      /* when using a client cert blob, create a name for it */
+      curl_msnprintf(buffer, sizeof(buffer),
+                     "cert-%p", conn_config->cert_blob->data);
+      ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */
+    }
     result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
                                      username, password, NULL, NULL);
     if(result)
@@ -993,6 +1012,12 @@
 
 #define CURL_SSL_TICKET_MAX   (16 * 1024)
 
+bool Curl_ssl_scache_is_locked(struct Curl_easy *data)
+{
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+  return scache && scache->is_locked;
+}
+
 static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
 {
   CURLcode result;
@@ -1075,49 +1100,47 @@
   struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct Curl_ssl_scache_peer *peer = NULL;
   struct Curl_ssl_session *s = NULL;
-  bool locked = FALSE;
-  CURLcode r;
+  CURLcode result;
 
   if(!scache) {
-    r = CURLE_BAD_FUNCTION_ARGUMENT;
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto out;
   }
   if(!ssl_peer_key && (!shmac || !shmac_len)) {
-    r = CURLE_BAD_FUNCTION_ARGUMENT;
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto out;
   }
 
-  r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
-  if(r)
+  result = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
+  if(result)
     goto out;
 
   Curl_ssl_scache_lock(data);
-  locked = TRUE;
 
   if(ssl_peer_key) {
-    r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
-    if(r)
+    result = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
+    if(result)
       goto out;
   }
   else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
     /* Either salt+hmac was garbled by caller or is from a curl version
      * that does things differently */
-    r = CURLE_BAD_FUNCTION_ARGUMENT;
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto out;
   }
   else {
     const unsigned char *salt = shmac;
     const unsigned char *hmac = shmac + sizeof(peer->key_salt);
 
-    r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
-    if(r)
+    result = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
+    if(result)
       goto out;
     if(!peer) {
       peer = cf_ssl_get_free_peer(scache);
       if(peer) {
-        r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
-                                    NULL, NULL, salt, hmac);
-        if(r)
+        result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
+                                         NULL, NULL, salt, hmac);
+        if(result)
           goto out;
       }
     }
@@ -1133,10 +1156,10 @@
   }
 
 out:
-  if(locked)
+  if(scache && scache->is_locked)
     Curl_ssl_scache_unlock(data);
   Curl_ssl_session_destroy(s);
-  return r;
+  return result;
 }
 
 CURLcode Curl_ssl_session_export(struct Curl_easy *data,
@@ -1156,8 +1179,6 @@
 
   if(!export_fn)
     return CURLE_BAD_FUNCTION_ARGUMENT;
-  if(!scache)
-    return CURLE_OK;
 
   Curl_ssl_scache_lock(data);
 
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.h b/Utilities/cmcurl/lib/vtls/vtls_scache.h
index b9db0f1..a6a36f1 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_scache.h
+++ b/Utilities/cmcurl/lib/vtls/vtls_scache.h
@@ -52,7 +52,7 @@
 /* Create a key from peer and TLS configuration information that is
  * unique for how the connection filter wants to establish a TLS
  * connection to the peer.
- * If the filter is a TLS proxy filter, it will use the proxy relevant
+ * If the filter is a TLS proxy filter, it uses the proxy relevant
  * information.
  * @param cf      the connection filter wanting to use it
  * @param peer    the peer the filter wants to talk to
@@ -84,7 +84,7 @@
 /* Unlock session cache mutex */
 void Curl_ssl_scache_unlock(struct Curl_easy *data);
 
-/* Get TLS session object from the cache for the ssl_peer_ey.
+/* Get TLS session object from the cache for the ssl_peer_key.
  * scache mutex must be locked (see Curl_ssl_scache_lock).
  * Caller must make sure that the ownership of returned session object
  * is properly taken (e.g. its refcount is incremented
@@ -112,7 +112,7 @@
  * @param data    the transfer involved
  * @param ssl_peer_key the key for lookup
  * @param sobj    the TLS session object
- * @param sobj_free_cb callback to free the session objectt
+ * @param sobj_free_cb callback to free the session object
  */
 CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
@@ -199,6 +199,8 @@
 
 #ifdef USE_SSLS_EXPORT
 
+bool Curl_ssl_scache_is_locked(struct Curl_easy *data);
+
 CURLcode Curl_ssl_session_import(struct Curl_easy *data,
                                  const char *ssl_peer_key,
                                  const unsigned char *shmac, size_t shmac_len,
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index aa841a7..15c81c2 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -33,8 +33,8 @@
 #include <wolfssl/options.h>
 #include <wolfssl/version.h>
 
-#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */
-#error "wolfSSL version should be at least 3.4.6"
+#if LIBWOLFSSL_VERSION_HEX < 0x05000000 /* wolfSSL 5.0.0 (2021-11-01) */
+#error "wolfSSL version should be at least 5.0.0"
 #endif
 #if defined(OPENSSL_COEXIST) && LIBWOLFSSL_VERSION_HEX < 0x05007006
 #error "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL"
@@ -56,6 +56,7 @@
 #include "urldata.h"
 #include "curl_trc.h"
 #include "httpsrr.h"
+#include "cf-dns.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_int.h"
 #include "vtls/vtls_scache.h"
@@ -65,6 +66,9 @@
 #include "curlx/strdup.h"
 #include "curlx/strcopy.h"
 #include "vtls/x509asn1.h"
+#ifdef USE_ECH
+#include "curlx/base64.h"
+#endif
 
 #include <wolfssl/ssl.h>
 #include <wolfssl/error-ssl.h>
@@ -109,7 +113,7 @@
  * Availability note:
  * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in
  * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
- * option is not set, then TLS 1.3 will not be logged.
+ * option is not set, then TLS 1.3 is not logged.
  * For TLS 1.2 and before, we use wolfSSL_get_keys().
  * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
  * (--enable-opensslextra or --enable-all).
@@ -168,7 +172,6 @@
   unsigned char *ms, *sr, *cr;
   unsigned int msLen, srLen, crLen, i, x = 0;
 
-#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */
   /* wolfSSL_GetVersion is available since 3.13, we use it instead of
    * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or
    * --enable-all). Failing to perform this check could result in an unusable
@@ -184,7 +187,6 @@
      * is not directly usable. */
     return;
   }
-#endif
 
   if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) !=
      WOLFSSL_SUCCESS) {
@@ -546,7 +548,7 @@
         infof(data, "SSL reusing session with ALPN '%s'",
               scs->alpn ? scs->alpn : "-");
         if(ssl_config->earlydata &&
-           !cf->conn->connect_only &&
+           !cf->conn->bits.connect_only &&
            !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) {
           bool do_early_data = FALSE;
           if(sess_reuse_cb) {
@@ -872,8 +874,7 @@
 }
 #endif
 
-/* 4.2.0 (2019) */
-#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA)
+#ifndef OPENSSL_EXTRA
 static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX *ctx, int version)
 {
   int res;
@@ -909,11 +910,11 @@
 }
 #define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version
 #define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
-#endif
+#endif /* OPENSSL_EXTRA */
 
-static CURLcode client_certificate(struct Curl_easy *data,
-                                   struct ssl_config_data *ssl_config,
-                                   struct wssl_ctx *wctx)
+static CURLcode wssl_client_cert(struct Curl_easy *data,
+                                 struct ssl_config_data *ssl_config,
+                                 struct wssl_ctx *wctx)
 {
   /* Load the client certificate, and private key */
 #ifndef NO_FILESYSTEM
@@ -1076,80 +1077,29 @@
 }
 
 #define QUIC_GROUPS "P-256:P-384:P-521"
-
-CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
-                            struct Curl_cfilter *cf,
-                            struct Curl_easy *data,
-                            struct ssl_peer *peer,
-                            const struct alpn_spec *alpns_requested,
-                            Curl_wssl_ctx_setup_cb *cb_setup,
-                            void *cb_user_data,
-                            void *ssl_user_data,
-                            Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
-{
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  struct ssl_primary_config *conn_config;
-  WOLFSSL_METHOD *req_method = NULL;
-  struct alpn_spec alpns;
-  char *curves;
-#ifdef WOLFSSL_HAVE_KYBER
-  word16 pqkem = 0;
-  size_t idx = 0;
-#endif
-  CURLcode result = CURLE_FAILED_INIT;
-  unsigned char transport;
-  int tls_min, tls_max;
-
-  DEBUGASSERT(!wctx->ssl_ctx);
-  DEBUGASSERT(!wctx->ssl);
-  conn_config = Curl_ssl_cf_get_primary_config(cf);
-  if(!conn_config) {
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-  Curl_alpn_copy(&alpns, alpns_requested);
-  DEBUGASSERT(cf->next);
-  transport = Curl_conn_cf_get_transport(cf->next, data);
-
-#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
-  req_method = wolfSSLv23_client_method();
-#else
-  req_method = wolfTLS_client_method();
-#endif
-  if(!req_method) {
-    failf(data, "wolfSSL: could not create a client method");
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  if(wctx->ssl_ctx)
-    wolfSSL_CTX_free(wctx->ssl_ctx);
-
-  wctx->ssl_ctx = wolfSSL_CTX_new(req_method);
-  if(!wctx->ssl_ctx) {
-    failf(data, "wolfSSL: could not create a context");
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max);
-  if(result)
-    goto out;
-
-#ifndef WOLFSSL_TLS13
-  {
-    const char *ciphers = conn_config->cipher_list;
-    if(ciphers) {
-      if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) {
-        failf(data, "failed setting cipher list: %s", ciphers);
-        result = CURLE_SSL_CIPHER;
-        goto out;
-      }
-      infof(data, "Cipher selection: %s", ciphers);
-    }
-  }
-#else
+#ifdef WOLFSSL_TLS13
 #define MAX_CIPHER_LEN 4096
+#endif
+
+static CURLcode wssl_init_ciphers(struct Curl_easy *data,
+                                  struct wssl_ctx *wctx,
+                                  struct ssl_primary_config *conn_config,
+                                  int tls_min, int tls_max)
+{
+#ifndef WOLFSSL_TLS13
+  const char *ciphers = conn_config->cipher_list;
+  (void)tls_min;
+  (void)tls_max;
+  if(ciphers) {
+    if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) {
+      failf(data, "failed setting cipher list: %s", ciphers);
+      return CURLE_SSL_CIPHER;
+    }
+    infof(data, "Cipher selection: %s", ciphers);
+  }
+  return CURLE_OK;
+#else
+  CURLcode result = CURLE_OK;
   if(conn_config->cipher_list || conn_config->cipher_list13) {
     const char *ciphers12 = conn_config->cipher_list;
     const char *ciphers13 = conn_config->cipher_list13;
@@ -1173,45 +1123,276 @@
       else
         result = wssl_add_default_ciphers(FALSE, &c);
     }
-    if(result)
-      goto out;
-
-    if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) {
-      failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c));
-      curlx_dyn_free(&c);
-      result = CURLE_SSL_CIPHER;
-      goto out;
+    if(!result) {
+      if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) {
+        failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c));
+        result = CURLE_SSL_CIPHER;
+      }
+      else
+        infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c));
     }
-    infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c));
     curlx_dyn_free(&c);
   }
+  return result;
 #endif
+}
 
-  curves = conn_config->curves;
+static CURLcode wssl_init_curves(struct Curl_easy *data,
+                                 struct wssl_ctx *wctx,
+                                 struct ssl_primary_config *conn_config,
+                                 unsigned char transport
+#ifdef WOLFSSL_HAVE_KYBER
+                                 , word16 *out_pqkem
+#endif
+                                 )
+{
+  char *curves = conn_config->curves;
   if(!curves && (transport == TRNSPRT_QUIC))
     curves = (char *)CURL_UNCONST(QUIC_GROUPS);
 
   if(curves) {
 #ifdef WOLFSSL_HAVE_KYBER
+    size_t idx;
     for(idx = 0; gnm[idx].name != NULL; idx++) {
-      if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
-        pqkem = gnm[idx].group;
+      if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) {
+        *out_pqkem = gnm[idx].group;
         break;
       }
     }
 
-    if(pqkem == 0)
+    if(*out_pqkem == 0)
 #endif
     {
       if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) {
         failf(data, "failed setting curves list: '%s'", curves);
-        result = CURLE_SSL_CIPHER;
-        goto out;
+        return CURLE_SSL_CIPHER;
+      }
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx,
+                                     struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct ssl_peer *peer,
+                                     struct alpn_spec *alpns,
+                                     void *ssl_user_data,
+                                     unsigned char transport,
+#ifdef WOLFSSL_HAVE_KYBER
+                                     word16 pqkem,
+#endif
+                                     Curl_wssl_init_session_reuse_cb
+                                       *sess_reuse_cb)
+{
+  /* Let's make an SSL structure */
+  wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
+  if(!wctx->ssl) {
+    failf(data, "SSL: could not create a handle");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+#ifdef HAVE_EX_DATA
+  wolfSSL_set_app_data(wctx->ssl, ssl_user_data);
+#else
+  (void)ssl_user_data;
+#endif
+#ifdef WOLFSSL_QUIC
+  if(transport == TRNSPRT_QUIC)
+    wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0);
+#else
+  (void)transport;
+#endif
+
+#ifdef WOLFSSL_HAVE_KYBER
+  if(pqkem) {
+    if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) !=
+       WOLFSSL_SUCCESS) {
+      failf(data, "unable to use PQ KEM");
+    }
+  }
+#endif
+
+  /* Check if there is a cached ID we can/should use here! */
+  if(Curl_ssl_scache_use(cf, data)) {
+    /* Set session from cache if there is one */
+    (void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key,
+                             sess_reuse_cb);
+  }
+
+#ifdef HAVE_ALPN
+  if(alpns->count) {
+    struct alpn_proto_buf proto;
+    memset(&proto, 0, sizeof(proto));
+    Curl_alpn_to_proto_str(&proto, alpns);
+
+    if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data,
+                       (unsigned int)proto.len,
+                       WOLFSSL_ALPN_CONTINUE_ON_MISMATCH)
+       != WOLFSSL_SUCCESS) {
+      failf(data, "SSL: failed setting ALPN protocols");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data);
+  }
+#endif /* HAVE_ALPN */
+
+#ifdef OPENSSL_EXTRA
+  if(Curl_tls_keylog_enabled()) {
+    /* Ensure the Client Random is preserved. */
+    wolfSSL_KeepArrays(wctx->ssl);
+#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
+    wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL);
+#endif
+  }
+#endif /* OPENSSL_EXTRA */
+
+#ifdef HAVE_SECURE_RENEGOTIATION
+  if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) {
+    failf(data, "SSL: failed setting secure renegotiation");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+#endif /* HAVE_SECURE_RENEGOTIATION */
+
+  return CURLE_OK;
+}
+
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+static CURLcode wssl_init_ech(struct wssl_ctx *wctx,
+                              struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  int trying_ech_now = 0;
+
+  if(data->set.str[STRING_ECH_PUBLIC]) {
+    infof(data, "ECH: outername not (yet) supported"
+          " with wolfSSL");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  if(data->set.tls_ech == CURLECH_GREASE) {
+    infof(data, "ECH: GREASE is done by default by"
+          " wolfSSL: no need to ask");
+  }
+  if(data->set.tls_ech & CURLECH_CLA_CFG &&
+     data->set.str[STRING_ECH_CONFIG]) {
+    char *b64val = data->set.str[STRING_ECH_CONFIG];
+    word32 b64len = 0;
+
+    b64len = (word32)strlen(b64val);
+    if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val,
+                                             b64len) != WOLFSSL_SUCCESS) {
+      if(data->set.tls_ech & CURLECH_HARD)
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+    else {
+      trying_ech_now = 1;
+      infof(data, "ECH: ECHConfig from command line");
+    }
+  }
+  else {
+    const struct Curl_https_rrinfo *rinfo =
+      Curl_conn_dns_get_https(data, cf->sockindex);
+
+    if(rinfo && rinfo->echconfiglist) {
+      const unsigned char *ecl = rinfo->echconfiglist;
+      size_t elen = rinfo->echconfiglist_len;
+
+      infof(data, "ECH: ECHConfig from HTTPS RR");
+      if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
+         WOLFSSL_SUCCESS) {
+        infof(data, "ECH: wolfSSL_SetEchConfigs failed");
+        if(data->set.tls_ech & CURLECH_HARD) {
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+      }
+      else {
+        trying_ech_now = 1;
+        infof(data, "ECH: imported ECHConfigList of length %zu", elen);
+      }
+    }
+    else {
+      infof(data, "ECH: requested but no ECHConfig available");
+      if(data->set.tls_ech & CURLECH_HARD) {
+        return CURLE_SSL_CONNECT_ERROR;
       }
     }
   }
 
-  result = client_certificate(data, ssl_config, wctx);
+  if(trying_ech_now &&
+     wolfSSL_set_min_proto_version(wctx->ssl, TLS1_3_VERSION) != 1) {
+    infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+  return CURLE_OK;
+}
+#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */
+
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const struct alpn_spec *alpns_requested,
+                            Curl_wssl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            void *ssl_user_data,
+                            Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
+{
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  struct ssl_primary_config *conn_config;
+  WOLFSSL_METHOD *req_method = NULL;
+  struct alpn_spec alpns;
+#ifdef WOLFSSL_HAVE_KYBER
+  word16 pqkem = 0;
+#endif
+  CURLcode result = CURLE_FAILED_INIT;
+  unsigned char transport;
+  int tls_min, tls_max;
+
+  DEBUGASSERT(!wctx->ssl_ctx);
+  DEBUGASSERT(!wctx->ssl);
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config) {
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+  Curl_alpn_copy(&alpns, alpns_requested);
+  DEBUGASSERT(cf->next);
+  transport = Curl_conn_cf_get_transport(cf->next, data);
+
+  req_method = wolfTLS_client_method();
+  if(!req_method) {
+    failf(data, "wolfSSL: could not create a client method");
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  if(wctx->ssl_ctx)
+    wolfSSL_CTX_free(wctx->ssl_ctx);
+
+  wctx->ssl_ctx = wolfSSL_CTX_new(req_method);
+  if(!wctx->ssl_ctx) {
+    failf(data, "wolfSSL: could not create a context");
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max);
+  if(result)
+    goto out;
+
+  result = wssl_init_ciphers(data, wctx, conn_config, tls_min, tls_max);
+  if(result)
+    goto out;
+
+  result = wssl_init_curves(data, wctx, conn_config, transport
+#ifdef WOLFSSL_HAVE_KYBER
+                            , &pqkem
+#endif
+    );
+  if(result)
+    goto out;
+
+  result = wssl_client_cert(data, ssl_config, wctx);
   if(result)
     goto out;
 
@@ -1219,9 +1400,8 @@
    * 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. */
-  wolfSSL_CTX_set_verify(wctx->ssl_ctx,
-                         conn_config->verifypeer ? WOLFSSL_VERIFY_PEER :
-                         WOLFSSL_VERIFY_NONE, NULL);
+  wolfSSL_CTX_set_verify(wctx->ssl_ctx, conn_config->verifypeer ?
+                         WOLFSSL_VERIFY_PEER : WOLFSSL_VERIFY_NONE, NULL);
 
 #ifdef HAVE_SNI
   if(peer->sni) {
@@ -1268,7 +1448,7 @@
 #ifdef NO_FILESYSTEM
   else if(conn_config->verifypeer) {
     failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built"
-          " with \"no file system\". Either disable peer verification"
+          " with no file system. Either disable peer verification"
           " (insecure) or if you are building an application with libcurl you"
           " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
     result = CURLE_SSL_CONNECT_ERROR;
@@ -1276,157 +1456,20 @@
   }
 #endif
 
-  /* Let's make an SSL structure */
-  wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
-  if(!wctx->ssl) {
-    failf(data, "SSL: could not create a handle");
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-#ifdef HAVE_EX_DATA
-  wolfSSL_set_app_data(wctx->ssl, ssl_user_data);
-#else
-  (void)ssl_user_data;
-#endif
-#ifdef WOLFSSL_QUIC
-  if(transport == TRNSPRT_QUIC)
-    wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0);
-#endif
-
+  result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data,
+                                transport,
 #ifdef WOLFSSL_HAVE_KYBER
-  if(pqkem) {
-    if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) {
-      failf(data, "unable to use PQ KEM");
-    }
-  }
+                                pqkem,
 #endif
-
-  /* Check if there is a cached ID we can/should use here! */
-  if(Curl_ssl_scache_use(cf, data)) {
-    /* Set session from cache if there is one */
-    (void)wssl_setup_session(cf, data, wctx, &alpns,
-                             peer->scache_key, sess_reuse_cb);
-  }
-
-#ifdef HAVE_ALPN
-  if(alpns.count) {
-    struct alpn_proto_buf proto;
-    memset(&proto, 0, sizeof(proto));
-    Curl_alpn_to_proto_str(&proto, &alpns);
-
-    if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data,
-                       (unsigned int)proto.len,
-                       WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) {
-      failf(data, "SSL: failed setting ALPN protocols");
-      result = CURLE_SSL_CONNECT_ERROR;
-      goto out;
-    }
-    CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data);
-  }
-#endif /* HAVE_ALPN */
-
-#ifdef OPENSSL_EXTRA
-  if(Curl_tls_keylog_enabled()) {
-    /* Ensure the Client Random is preserved. */
-    wolfSSL_KeepArrays(wctx->ssl);
-#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
-    wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL);
-#endif
-  }
-#endif /* OPENSSL_EXTRA */
-
-#ifdef HAVE_SECURE_RENEGOTIATION
-  if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) {
-    failf(data, "SSL: failed setting secure renegotiation");
-    result = CURLE_SSL_CONNECT_ERROR;
+                                sess_reuse_cb);
+  if(result)
     goto out;
-  }
-#endif /* HAVE_SECURE_RENEGOTIATION */
 
 #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
-  if(ECH_ENABLED(data)) {
-    int trying_ech_now = 0;
-
-    if(data->set.str[STRING_ECH_PUBLIC]) {
-      infof(data, "ECH: outername not (yet) supported with wolfSSL");
-      result = CURLE_SSL_CONNECT_ERROR;
+  if(CURLECH_ENABLED(data)) {
+    result = wssl_init_ech(wctx, cf, data);
+    if(result)
       goto out;
-    }
-    if(data->set.tls_ech == CURLECH_GREASE) {
-      infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask");
-    }
-    if(data->set.tls_ech & CURLECH_CLA_CFG &&
-       data->set.str[STRING_ECH_CONFIG]) {
-      char *b64val = data->set.str[STRING_ECH_CONFIG];
-      word32 b64len = 0;
-
-      b64len = (word32)strlen(b64val);
-      if(b64len &&
-         wolfSSL_SetEchConfigsBase64(wctx->ssl,
-                                     b64val, b64len) != WOLFSSL_SUCCESS) {
-        if(data->set.tls_ech & CURLECH_HARD) {
-          result = CURLE_SSL_CONNECT_ERROR;
-          goto out;
-        }
-      }
-      else {
-        trying_ech_now = 1;
-        infof(data, "ECH: ECHConfig from command line");
-      }
-    }
-    else {
-      struct ssl_connect_data *connssl = cf->ctx;
-      struct Curl_dns_entry *dns = NULL;
-
-      dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port,
-                              cf->conn->ip_version);
-      if(!dns) {
-        infof(data, "ECH: requested but no DNS info available");
-        if(data->set.tls_ech & CURLECH_HARD) {
-          result = CURLE_SSL_CONNECT_ERROR;
-          goto out;
-        }
-      }
-      else {
-        struct Curl_https_rrinfo *rinfo = NULL;
-
-        rinfo = dns->hinfo;
-        if(rinfo && rinfo->echconfiglist) {
-          unsigned char *ecl = rinfo->echconfiglist;
-          size_t elen = rinfo->echconfiglist_len;
-
-          infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-          if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
-             WOLFSSL_SUCCESS) {
-            infof(data, "ECH: wolfSSL_SetEchConfigs failed");
-            if(data->set.tls_ech & CURLECH_HARD) {
-              result = CURLE_SSL_CONNECT_ERROR;
-              goto out;
-            }
-          }
-          else {
-            trying_ech_now = 1;
-            infof(data, "ECH: imported ECHConfigList of length %ld", elen);
-          }
-        }
-        else {
-          infof(data, "ECH: requested but no ECHConfig available");
-          if(data->set.tls_ech & CURLECH_HARD) {
-            result = CURLE_SSL_CONNECT_ERROR;
-            goto out;
-          }
-        }
-        Curl_resolv_unlink(data, &dns);
-      }
-    }
-
-    if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl,
-                                                       TLS1_3_VERSION) != 1) {
-      infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
-      result = CURLE_SSL_CONNECT_ERROR;
-      goto out;
-    }
   }
 #endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */
 
@@ -1444,6 +1487,21 @@
   return result;
 }
 
+bool Curl_wssl_need_httpsrr(struct Curl_easy *data)
+{
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+  if(!CURLECH_ENABLED(data))
+    return FALSE;
+  if((data->set.tls_ech & CURLECH_GREASE) ||
+     (data->set.tls_ech & CURLECH_CLA_CFG))
+   return FALSE;
+  return TRUE;
+#else
+  (void)data;
+  return FALSE;
+#endif
+}
+
 /*
  * This function loads all the client/CA certificates and CRLs. Setup the TLS
  * layer and do all necessary magic.
@@ -1461,9 +1519,8 @@
   if(connssl->state == ssl_connection_complete)
     return CURLE_OK;
 
-  result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer,
-                              connssl->alpn, NULL, NULL, cf,
-                              wssl_on_session_reuse);
+  result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, connssl->alpn,
+                              NULL, NULL, cf, wssl_on_session_reuse);
   if(result)
     return result;
 
@@ -1730,7 +1787,7 @@
     }
     else if(DOMAIN_NAME_MISMATCH == detail) {
       /* There is no easy way to override only the CN matching.
-       * This will enable the override of both mismatching SubjectAltNames
+       * This enables the override of both mismatching SubjectAltNames
        * as also mismatching CN fields */
       failf(data, " subject alt name(s) or common name do not match \"%s\"",
             connssl->peer.dispname);
@@ -2014,13 +2071,13 @@
     case WOLFSSL_ERROR_NONE:
     case WOLFSSL_ERROR_WANT_READ:
     case WOLFSSL_ERROR_WANT_WRITE:
-      if(!wssl->io_result && connssl->peer_closed) {
-        CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
-        return CURLE_OK;
+      if(!wssl->io_result && !connssl->peer_closed) {
+        /* there is data pending, re-invoke wolfSSL_read() */
+        CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
+        return CURLE_AGAIN;
       }
-      /* there is data pending, re-invoke wolfSSL_read() */
-      CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
-      return CURLE_AGAIN;
+      /* fall through to default error handling below */
+      FALLTHROUGH();
     default:
       if(wssl->io_result == CURLE_AGAIN) {
         CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
@@ -2047,11 +2104,7 @@
 
 size_t Curl_wssl_version(char *buffer, size_t size)
 {
-#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
   return curl_msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
-#elif defined(WOLFSSL_VERSION)
-  return curl_msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
-#endif
 }
 
 static int wssl_init(void)
@@ -2095,13 +2148,9 @@
 void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl)
 {
   (void)wssl;
-#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
   infof(data, "SSL connection using %s / %s",
         wolfSSL_get_version(wssl->ssl),
         wolfSSL_get_cipher_name(wssl->ssl));
-#else
-  infof(data, "SSL connected");
-#endif
 }
 
 static CURLcode wssl_connect(struct Curl_cfilter *cf,
@@ -2122,6 +2171,15 @@
   connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
   if(ssl_connect_1 == connssl->connecting_state) {
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+    /* if we do ECH and need the HTTPS-RR information for it,
+     * we delay the connect until it arrives or DNS resolve fails. */
+    if(Curl_wssl_need_httpsrr(data) &&
+       !Curl_conn_dns_resolved_https(data, cf->sockindex)) {
+      CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
+      return CURLE_OK;
+    }
+#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */
     result = wssl_connect_step1(cf, data);
     if(result)
       return result;
@@ -2142,8 +2200,8 @@
   }
 
   if(ssl_connect_3 == connssl->connecting_state) {
-    /* Once the handshake has errored, it stays in that state and will
-     * error again on every call. */
+    /* Once the handshake has errored, it stays in that state and
+     * errors again on every call. */
     if(wssl->hs_result) {
       result = wssl->hs_result;
       goto out;
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h
index b097b60..2490bf3 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.h
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.h
@@ -29,6 +29,8 @@
 
 #include "urldata.h"
 
+#include <wolfssl/options.h>
+
 struct alpn_spec;
 struct ssl_peer;
 struct Curl_ssl_session;
@@ -65,12 +67,15 @@
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const struct alpn_spec *alpns,
+                            const struct alpn_spec *alpns_requested,
                             Curl_wssl_ctx_setup_cb *cb_setup,
                             void *cb_user_data,
                             void *ssl_user_data,
                             Curl_wssl_init_session_reuse_cb *sess_reuse_cb);
 
+/* Is a resolved HTTPS-RR needed for initializing wolfSSL? */
+bool Curl_wssl_need_httpsrr(struct Curl_easy *data);
+
 CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct wssl_ctx *wssl);
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c
index b687a60..788dfb2 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.c
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -51,41 +51,6 @@
 #define CURL_ASN1_MAX                   ((size_t)0x40000)      /* 256K */
 
 #ifdef WANT_EXTRACT_CERTINFO
-/* ASN.1 classes. */
-/* #define CURL_ASN1_UNIVERSAL             0 */
-/* #define CURL_ASN1_APPLICATION           1 */
-/* #define CURL_ASN1_CONTEXT_SPECIFIC      2 */
-/* #define CURL_ASN1_PRIVATE               3 */
-
-/* ASN.1 types. */
-#define CURL_ASN1_BOOLEAN               1
-#define CURL_ASN1_INTEGER               2
-#define CURL_ASN1_BIT_STRING            3
-#define CURL_ASN1_OCTET_STRING          4
-#define CURL_ASN1_NULL                  5
-#define CURL_ASN1_OBJECT_IDENTIFIER     6
-/* #define CURL_ASN1_OBJECT_DESCRIPTOR     7 */
-/* #define CURL_ASN1_INSTANCE_OF           8 */
-/* #define CURL_ASN1_REAL                  9 */
-#define CURL_ASN1_ENUMERATED            10
-/* #define CURL_ASN1_EMBEDDED              11 */
-#define CURL_ASN1_UTF8_STRING           12
-/* #define CURL_ASN1_RELATIVE_OID          13 */
-/* #define CURL_ASN1_SEQUENCE              16 */
-/* #define CURL_ASN1_SET                   17 */
-#define CURL_ASN1_NUMERIC_STRING        18
-#define CURL_ASN1_PRINTABLE_STRING      19
-#define CURL_ASN1_TELETEX_STRING        20
-/* #define CURL_ASN1_VIDEOTEX_STRING       21 */
-#define CURL_ASN1_IA5_STRING            22
-#define CURL_ASN1_UTC_TIME              23
-#define CURL_ASN1_GENERALIZED_TIME      24
-/* #define CURL_ASN1_GRAPHIC_STRING        25 */
-#define CURL_ASN1_VISIBLE_STRING        26
-/* #define CURL_ASN1_GENERAL_STRING        27 */
-#define CURL_ASN1_UNIVERSAL_STRING      28
-/* #define CURL_ASN1_CHARACTER_STRING      29 */
-#define CURL_ASN1_BMP_STRING            30
 
 /* ASN.1 OID table entry. */
 struct Curl_OID {
@@ -160,10 +125,6 @@
  * Please note there is no pretension here to rewrite a full SSL library.
  */
 
-static const char *getASN1Element(struct Curl_asn1Element *elem,
-                                  const char *beg, const char *end)
-  WARN_UNUSED_RESULT;
-
 #define CURL_ASN1_MAX_RECURSIONS    16
 
 static const char *getASN1Element_(struct Curl_asn1Element *elem,
@@ -199,32 +160,35 @@
   b = (unsigned char)*beg++;
   if(!(b & 0x80))
     len = b;
-  else if(!(b &= 0x7F)) {
-    /* Unspecified length. Since we have all the data, we can determine the
-       effective length by skipping element until an end element is found. */
-    if(!elem->constructed)
-      return NULL;
-    elem->beg = beg;
-    while(beg < end && *beg) {
-      beg = getASN1Element_(&lelem, beg, end, lvl + 1);
-      if(!beg)
-        return NULL;
-    }
-    if(beg >= end)
-      return NULL;
-    elem->end = beg;
-    return beg + 1;
-  }
-  else if((unsigned)b > (size_t)(end - beg))
-    return NULL; /* Does not fit in source. */
   else {
-    /* Get long length. */
-    len = 0;
-    do {
-      if(len & 0xFF000000L)
-        return NULL;  /* Lengths > 32 bits are not supported. */
-      len = (len << 8) | (unsigned char) *beg++;
-    } while(--b);
+    b &= 0x7F;
+    if(!b) {
+      /* Unspecified length. Since we have all the data, we can determine the
+         effective length by skipping element until an end element is found. */
+      if(!elem->constructed)
+        return NULL;
+      elem->beg = beg;
+      while(beg < end && *beg) {
+        beg = getASN1Element_(&lelem, beg, end, lvl + 1);
+        if(!beg)
+          return NULL;
+      }
+      if(beg >= end)
+        return NULL;
+      elem->end = beg;
+      return beg + 1;
+    }
+    else if((unsigned)b > (size_t)(end - beg))
+      return NULL; /* Does not fit in source. */
+    else {
+      /* Get long length. */
+      len = 0;
+      do {
+        if(len & 0xFF000000L)
+          return NULL;  /* Lengths > 32 bits are not supported. */
+        len = (len << 8) | (unsigned char) *beg++;
+      } while(--b);
+    }
   }
   if(len > (size_t)(end - beg))
     return NULL;  /* Element data does not fit in source. */
@@ -233,8 +197,13 @@
   return elem->end;
 }
 
-static const char *getASN1Element(struct Curl_asn1Element *elem,
-                                  const char *beg, const char *end)
+/*
+ * @unittest 1657
+ */
+UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem,
+                                    const char *beg, const char *end);
+UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem,
+                                    const char *beg, const char *end)
 {
   return getASN1Element_(elem, beg, end, 0);
 }
@@ -255,17 +224,6 @@
   return NULL;
 }
 
-#ifdef UNITTESTS
-/* used by unit1657.c */
-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
-                                  const char *beg, const char *end)
-{
-  if(getASN1Element(elem, beg, end))
-    return CURLE_OK;
-  return CURLE_BAD_FUNCTION_ARGUMENT;
-}
-#endif
-
 /*
  * Convert an ASN.1 Boolean value into its string representation.
  *
@@ -275,9 +233,13 @@
 static CURLcode bool2str(struct dynbuf *store,
                          const char *beg, const char *end)
 {
+  uint8_t val;
   if(end - beg != 1)
     return CURLE_BAD_FUNCTION_ARGUMENT;
-  return curlx_dyn_add(store, *beg ? "TRUE" : "FALSE");
+  val = (uint8_t)*beg;
+  if((val != 0xff) && (val != 0x00))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  return curlx_dyn_add(store, val ? "TRUE" : "FALSE");
 }
 
 /*
@@ -300,7 +262,12 @@
 {
   /* Convert an ASN.1 bit string to a printable string. */
 
-  if(++beg > end)
+  if(beg < end) {
+    uint8_t unused_bits = (uint8_t)*beg++;
+    if(unused_bits > 7)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  else
     return CURLE_BAD_FUNCTION_ARGUMENT;
   return octet2str(store, beg, end);
 }
@@ -312,8 +279,11 @@
  */
 static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
 {
-  unsigned int val = 0;
+  uint32_t uval = 0;
+  int32_t sval = 0;
   size_t n = end - beg;
+  size_t i;
+  const unsigned char *p = (const unsigned char *)beg;
 
   if(!n)
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -321,14 +291,20 @@
   if(n > 4)
     return octet2str(store, beg, end);
 
-  /* Represent integers <= 32-bit as a single value. */
-  if(*beg & 0x80)
-    val = ~val;
+  if(p[0] & 0x80)
+    uval = ~0U;
 
-  do
-    val = (val << 8) | *(const unsigned char *)beg++;
-  while(beg < end);
-  return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+  for(i = 0; i < n; i++)
+    uval = (uval << 8) | p[i];
+
+  sval = (int32_t)uval;
+
+  /* small values are easier to read as decimals */
+  if(sval > -10000 && sval < 10000)
+    return curlx_dyn_addf(store, "%d", sval);
+
+  /* represent larger values as hex */
+  return curlx_dyn_addf(store, "0x%x", uval);
 }
 
 /*
@@ -417,33 +393,66 @@
 }
 
 /*
- * Convert an ASN.1 OID into its dotted string representation.
+ * Convert an ASN.1 OID into its dotted string representation. Each field is
+ * limited to unsigned 32-bit values.
+ *
+ * A minor limitation is in the second number, which can be no larger than 32
+ * bits - 80 == 4294967215.
  *
  * Return error code.
+ *
+ * @unittest 1666
  */
-static CURLcode encodeOID(struct dynbuf *store,
-                          const char *beg, const char *end)
+UNITTEST CURLcode encodeOID(struct dynbuf *store,
+                            const char *beg, const char *end);
+UNITTEST CURLcode encodeOID(struct dynbuf *store,
+                            const char *beg, const char *end)
 {
-  unsigned int x;
-  unsigned int y;
+  uint32_t x;
+  uint32_t y;
   CURLcode result = CURLE_OK;
 
-  /* Process the first two numbers. */
-  y = *(const unsigned char *)beg++;
-  x = y / 40;
-  y -= x * 40;
+  if(end <= beg)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  result = curlx_dyn_addf(store, "%u.%u", x, y);
+  /* Process the first two numbers. The initial digit cannot be larger than
+     2 */
+  y = *(const unsigned char *)beg++;
+  if(y <= 80)
+    x = y / 40;
+  else
+    x = 2;
+  result = curlx_dyn_addf(store, "%u", x);
+  if(result)
+    return result;
+
+  if(y & 0x80) {
+    uint32_t t = (y & 0x7f); /* the second number */
+    if(!t)
+      /* reject leading 0x80 padding */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    do {
+      if((t & 0xFE000000) || (beg >= end))
+        return CURLE_BAD_FUNCTION_ARGUMENT;
+      y = *(const unsigned char *)beg++;
+      t = (t << 7) | (y & 0x7F);
+    } while(y & 0x80);
+    y = t;
+  }
+
+  result = curlx_dyn_addf(store, ".%u", y - (x * 40));
   if(result)
     return result;
 
   /* Process the trailing numbers. */
-  while(beg < end) {
+  while((beg < end) && !result) {
     x = 0;
+    y = 0;
     do {
-      if(x & 0xFF000000)
-        return CURLE_OK;
-      else if(beg == end)
+      /* 0x80 as the first value is invalid since it only adds padding */
+      if((y & 0x80) && !x)
+        return CURLE_BAD_FUNCTION_ARGUMENT;
+      else if((x & 0xFE000000) || (beg == end))
         return CURLE_BAD_FUNCTION_ARGUMENT;
       y = *(const unsigned char *)beg++;
       x = (x << 7) | (y & 0x7F);
@@ -460,32 +469,33 @@
  */
 
 static CURLcode OID2str(struct dynbuf *store,
-                        const char *beg, const char *end, bool symbolic)
+                        const char *beg, const char *end)
 {
   CURLcode result = CURLE_OK;
   if(beg < end) {
-    if(symbolic) {
-      struct dynbuf buf;
-      curlx_dyn_init(&buf, CURL_X509_STR_MAX);
-      result = encodeOID(&buf, beg, end);
+    struct dynbuf buf;
+    curlx_dyn_init(&buf, CURL_X509_STR_MAX);
+    result = encodeOID(&buf, beg, end);
 
-      if(!result) {
-        const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
-        if(op)
-          result = curlx_dyn_add(store, op->textoid);
-        else
-          result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
-      }
-      curlx_dyn_free(&buf);
+    if(!result) {
+      const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
+      if(op)
+        result = curlx_dyn_add(store, op->textoid);
+      else
+        result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
     }
-    else
-      result = encodeOID(store, beg, end);
+    curlx_dyn_free(&buf);
   }
   return result;
 }
 
-static CURLcode GTime2str(struct dynbuf *store,
-                          const char *beg, const char *end)
+/*
+ * @unittest 1656
+ */
+UNITTEST CURLcode GTime2str(struct dynbuf *store,
+                            const char *beg, const char *end);
+UNITTEST CURLcode GTime2str(struct dynbuf *store,
+                            const char *beg, const char *end)
 {
   const char *tzp;
   const char *fracp;
@@ -562,15 +572,6 @@
                         sep, (int)tzl, tzp);
 }
 
-#ifdef UNITTESTS
-/* used by unit1656.c */
-CURLcode Curl_x509_GTime2str(struct dynbuf *store,
-                             const char *beg, const char *end)
-{
-  return GTime2str(store, beg, end);
-}
-#endif
-
 /*
  * Convert an ASN.1 UTC time to a printable string.
  *
@@ -604,11 +605,9 @@
     tzp = "GMT";
     end = tzp + 3;
   }
-  else
-    tzp++;
 
   tzl = end - tzp;
-  return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+  return curlx_dyn_addf(store, "%d%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
                         20 - (*beg >= '5'), beg, beg + 2, beg + 4,
                         beg + 6, beg + 8, sec,
                         (int)tzl, tzp);
@@ -618,17 +617,19 @@
  * Convert an ASN.1 element to a printable string.
  *
  * Return error
+ *
+ * @unittest 1667
  */
-static CURLcode ASN1tostr(struct dynbuf *store,
-                          struct Curl_asn1Element *elem, int type)
+UNITTEST CURLcode ASN1tostr(struct dynbuf *store,
+                            struct Curl_asn1Element *elem);
+UNITTEST CURLcode ASN1tostr(struct dynbuf *store,
+                            struct Curl_asn1Element *elem)
 {
   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
+  int type = elem->tag;
   if(elem->constructed)
     return result; /* No conversion of structured elements. */
 
-  if(!type)
-    type = elem->tag;   /* Type not forced: use element tag as type. */
-
   switch(type) {
   case CURL_ASN1_BOOLEAN:
     result = bool2str(store, elem->beg, elem->end);
@@ -647,7 +648,7 @@
     result = curlx_dyn_addn(store, "", 1);
     break;
   case CURL_ASN1_OBJECT_IDENTIFIER:
-    result = OID2str(store, elem->beg, elem->end, TRUE);
+    result = OID2str(store, elem->beg, elem->end);
     break;
   case CURL_ASN1_UTC_TIME:
     result = UTime2str(store, elem->beg, elem->end);
@@ -712,7 +713,7 @@
         goto error;
       }
       curlx_dyn_reset(&temp);
-      result = ASN1tostr(&temp, &oid, 0);
+      result = ASN1tostr(&temp, &oid);
       if(result)
         goto error;
 
@@ -747,7 +748,7 @@
         goto error;
 
       /* Generate value. */
-      result = ASN1tostr(store, &value, 0);
+      result = ASN1tostr(store, &value);
       if(result)
         goto error;
       curlx_dyn_reset(&temp);
@@ -905,7 +906,7 @@
     if(!p)
       return CURLE_BAD_FUNCTION_ARGUMENT;
   }
-  return OID2str(store, oid.beg, oid.end, TRUE);
+  return OID2str(store, oid.beg, oid.end);
 }
 
 /*
@@ -956,7 +957,7 @@
 
   /* Generate a certificate information record for the public key. */
 
-  result = ASN1tostr(&out, elem, 0);
+  result = ASN1tostr(&out, elem);
   if(!result) {
     if(data->set.ssl.certinfo)
       result = ssl_push_certinfo_dyn(data, certnum, label, &out);
@@ -1144,7 +1145,7 @@
   }
 
   /* Serial number. */
-  result = ASN1tostr(&out, &cert.serialNumber, 0);
+  result = ASN1tostr(&out, &cert.serialNumber);
   if(result)
     goto done;
   if(data->set.ssl.certinfo) {
@@ -1167,7 +1168,7 @@
   curlx_dyn_reset(&out);
 
   /* Start Date. */
-  result = ASN1tostr(&out, &cert.notBefore, 0);
+  result = ASN1tostr(&out, &cert.notBefore);
   if(result)
     goto done;
   if(data->set.ssl.certinfo) {
@@ -1178,7 +1179,7 @@
   curlx_dyn_reset(&out);
 
   /* Expire Date. */
-  result = ASN1tostr(&out, &cert.notAfter, 0);
+  result = ASN1tostr(&out, &cert.notAfter);
   if(result)
     goto done;
   if(data->set.ssl.certinfo) {
@@ -1209,7 +1210,7 @@
   curlx_dyn_reset(&out);
 
   /* Signature. */
-  result = ASN1tostr(&out, &cert.signature, 0);
+  result = ASN1tostr(&out, &cert.signature);
   if(result)
     goto done;
   if(data->set.ssl.certinfo) {
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h
index 829dfd8..dda55ac 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.h
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -26,12 +26,51 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
+/* forward-declare to make it visible in all builds */
+struct Curl_asn1Element;
+
 #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
   defined(USE_MBEDTLS) || defined(USE_RUSTLS)
 
 #include "cfilters.h"
 #include "urldata.h"
 
+/* ASN.1 classes. */
+/* #define CURL_ASN1_UNIVERSAL             0 */
+/* #define CURL_ASN1_APPLICATION           1 */
+/* #define CURL_ASN1_CONTEXT_SPECIFIC      2 */
+/* #define CURL_ASN1_PRIVATE               3 */
+
+/* ASN.1 types. */
+#define CURL_ASN1_BOOLEAN               1
+#define CURL_ASN1_INTEGER               2
+#define CURL_ASN1_BIT_STRING            3
+#define CURL_ASN1_OCTET_STRING          4
+#define CURL_ASN1_NULL                  5
+#define CURL_ASN1_OBJECT_IDENTIFIER     6
+/* #define CURL_ASN1_OBJECT_DESCRIPTOR     7 */
+/* #define CURL_ASN1_INSTANCE_OF           8 */
+/* #define CURL_ASN1_REAL                  9 */
+#define CURL_ASN1_ENUMERATED            10
+/* #define CURL_ASN1_EMBEDDED              11 */
+#define CURL_ASN1_UTF8_STRING           12
+/* #define CURL_ASN1_RELATIVE_OID          13 */
+/* #define CURL_ASN1_SEQUENCE              16 */
+/* #define CURL_ASN1_SET                   17 */
+#define CURL_ASN1_NUMERIC_STRING        18
+#define CURL_ASN1_PRINTABLE_STRING      19
+#define CURL_ASN1_TELETEX_STRING        20
+/* #define CURL_ASN1_VIDEOTEX_STRING       21 */
+#define CURL_ASN1_IA5_STRING            22
+#define CURL_ASN1_UTC_TIME              23
+#define CURL_ASN1_GENERALIZED_TIME      24
+/* #define CURL_ASN1_GRAPHIC_STRING        25 */
+#define CURL_ASN1_VISIBLE_STRING        26
+/* #define CURL_ASN1_GENERAL_STRING        27 */
+#define CURL_ASN1_UNIVERSAL_STRING      28
+/* #define CURL_ASN1_CHARACTER_STRING      29 */
+#define CURL_ASN1_BMP_STRING            30
+
 /*
  * Types.
  */
@@ -75,20 +114,6 @@
                                const char *beg, const char *end);
 CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
                          const char *beg, const char *end);
-
-#ifdef UNITTESTS
-#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \
-  defined(USE_RUSTLS)
-
-/* used by unit1656.c */
-CURLcode Curl_x509_GTime2str(struct dynbuf *store,
-                             const char *beg, const char *end);
-/* used by unit1657.c */
-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
-                                  const char *beg, const char *end);
-#endif /* USE_GNUTLS || USE_SCHANNEL || USE_MBEDTLS || RUSTLS */
-#endif /* UNITTESTS */
-
 #endif /* USE_GNUTLS || USE_WOLFSSL || USE_SCHANNEL || USE_MBEDTLS ||
           USE_RUSTLS */
 #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
index 6046561..d7840f1 100644
--- a/Utilities/cmcurl/lib/ws.c
+++ b/Utilities/cmcurl/lib/ws.c
@@ -116,8 +116,8 @@
  * and keep track of boundaries. */
 struct websocket {
   struct Curl_easy *data; /* used for write callback handling */
-  struct ws_decoder dec;  /* decode of we frames */
-  struct ws_encoder enc;  /* decode of we frames */
+  struct ws_decoder dec;  /* decode of ws frames */
+  struct ws_encoder enc;  /* encode of ws frames */
   struct bufq recvbuf;    /* raw data from the server */
   struct bufq sendbuf;    /* raw data to be sent to the server */
   struct curl_ws_frame recvframe;  /* the current WS FRAME received */
@@ -564,7 +564,7 @@
     dec->state = WS_DEC_INIT;
     break;
   default:
-    /* we covered all enums above, but some code analyzers are whimps */
+    /* we covered all enums above, but some code analyzers are wimps */
     result = CURLE_FAILED_INIT;
   }
   return result;
@@ -758,7 +758,7 @@
   }
 
   if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
-    failf(data, "[WS] decode ending with %zd frame bytes remaining",
+    failf(data, "[WS] decode ending with %zu frame bytes remaining",
           Curl_bufq_len(&ctx->buf));
     return CURLE_RECV_ERROR;
   }
@@ -1049,9 +1049,9 @@
     if((curl_off_t)buflen >
        (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) {
       /* too large buflen beyond payload length of frame */
-      failf(data, "[WS] unaligned frame size (sending %zu instead of %"
-                  FMT_OFF_T ")",
-            buflen, ws->enc.payload_remain + ws->sendbuf_payload);
+      failf(data, "[WS] unaligned frame size (sending %zu instead of "
+            "%" FMT_OFF_T ")", buflen,
+            (curl_off_t)(ws->enc.payload_remain + ws->sendbuf_payload));
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
   }
@@ -1399,7 +1399,7 @@
     if(result)
       goto out;
     DEBUGASSERT(nread == nwritten);
-    k->keepon &= ~KEEP_RECV; /* read no more content */
+    CURL_REQ_CLEAR_RECV(data); /* read no more content */
   }
   else { /* !connect_only */
     if(data->set.method == HTTPREQ_PUT) {
@@ -1409,7 +1409,7 @@
         goto out;
 
       if(!data->set.ws_raw_mode) {
-        /* Add our client readerr encoding WS BINARY frames */
+        /* Add our client reader encoding WS BINARY frames */
         result = Curl_creader_create(&ws_enc_reader, data, &ws_cr_encode,
                                      CURL_CR_CONTENT_ENCODE);
         if(result)
@@ -1423,10 +1423,10 @@
       /* start over with sending */
       data->req.eos_read = FALSE;
       data->req.upload_done = FALSE;
-      k->keepon |= KEEP_SEND;
+      CURL_REQ_SET_SEND(data);
     }
 
-    /* And pass any additional data to the writers */
+    /* Then pass any additional data to the writers */
     if(nread) {
       result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread);
       if(result)
@@ -1527,16 +1527,16 @@
   return curl_easy_recv(data, buf, buflen, pnread);
 }
 
-CURLcode curl_ws_recv(CURL *d, void *buffer,
-                      size_t buflen, size_t *nread,
+CURLcode curl_ws_recv(CURL *curl, void *buffer,
+                      size_t buflen, size_t *recv,
                       const struct curl_ws_frame **metap)
 {
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   struct connectdata *conn;
   struct websocket *ws;
   struct ws_collect ctx;
 
-  *nread = 0;
+  *recv = 0;
   *metap = NULL;
   if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer))
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1609,10 +1609,10 @@
   update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
               ctx.payload_len, ctx.bufidx);
   *metap = &ws->recvframe;
-  *nread = ws->recvframe.len;
+  *recv = ws->recvframe.len;
   CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
               FMT_OFF_T ", %" FMT_OFF_T " left)",
-              buflen, *nread, ws->recvframe.offset,
+              buflen, *recv, ws->recvframe.offset,
               ws->recvframe.bytesleft);
   /* all's well, try to send any pending control. we do not know
    * when the application will call `curl_ws_send()` again. */
@@ -1689,6 +1689,9 @@
   CURLcode result = CURLE_OK;
   size_t nwritten;
 
+  if(!data)
+    return result;
+
   (void)ws;
   while(buflen) {
     result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten);
@@ -1746,6 +1749,8 @@
     if(result)
       return result;
     result = ws_send_raw_blocking(data, ws, buffer, buflen);
+    if(!result)
+      *pnwritten = buflen;
   }
   else {
     /* We need any pending data to be sent or EAGAIN this call. */
@@ -1760,7 +1765,7 @@
   return result;
 }
 
-CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
+CURLcode curl_ws_send(CURL *curl, const void *buffer_arg,
                       size_t buflen, size_t *sent,
                       curl_off_t fragsize,
                       unsigned int flags)
@@ -1768,7 +1773,7 @@
   struct websocket *ws;
   const uint8_t *buffer = buffer_arg;
   CURLcode result = CURLE_OK;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   size_t ndummy;
   size_t *pnsent = sent ? sent : &ndummy;
 
@@ -1848,11 +1853,11 @@
   return Curl_http_setup_conn(data, conn);
 }
 
-const struct curl_ws_frame *curl_ws_meta(CURL *d)
+const struct curl_ws_frame *curl_ws_meta(CURL *curl)
 {
   /* we only return something for websocket, called from within the callback
      when not using raw mode */
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
   if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) &&
      data->conn && !data->set.ws_raw_mode) {
     struct websocket *ws;
@@ -1863,16 +1868,17 @@
   return NULL;
 }
 
-CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d,
+CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl,
                                          unsigned int flags,
                                          curl_off_t frame_len)
 {
   struct websocket *ws;
   CURLcode result = CURLE_OK;
-  struct Curl_easy *data = d;
+  struct Curl_easy *data = curl;
 
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
+
   if(data->set.ws_raw_mode) {
     failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled");
     return CURLE_FAILED_INIT;
@@ -1893,12 +1899,6 @@
     goto out;
   }
 
-  if(data->set.ws_raw_mode) {
-    failf(data, "[WS] cannot start frame in raw mode");
-    result = CURLE_SEND_ERROR;
-    goto out;
-  }
-
   if(ws->enc.payload_remain) {
     failf(data, "[WS] previous frame not finished");
     result = CURLE_SEND_ERROR;
@@ -1915,7 +1915,7 @@
   return result;
 }
 
-static const struct Curl_protocol Curl_protocol_ws = {
+const struct Curl_protocol Curl_protocol_ws = {
   ws_setup_conn,                        /* setup_connection */
   Curl_http,                            /* do_it */
   Curl_http_done,                       /* done */
@@ -1930,7 +1930,7 @@
   ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
   Curl_http_write_resp_hd,              /* write_resp_hd */
-  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* connection_is_dead */
   ZERO_NULL,                            /* attach connection */
   Curl_http_follow,                     /* follow */
 };
@@ -1938,13 +1938,13 @@
 #else
 
 CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
-                      size_t *nread,
+                      size_t *recv,
                       const struct curl_ws_frame **metap)
 {
   (void)curl;
   (void)buffer;
   (void)buflen;
-  (void)nread;
+  (void)recv;
   (void)metap;
   return CURLE_NOT_BUILT_IN;
 }
@@ -1980,32 +1980,3 @@
 }
 
 #endif /* !CURL_DISABLE_WEBSOCKETS */
-
-const struct Curl_scheme Curl_scheme_ws = {
-  "WS",                                 /* scheme */
-#ifdef CURL_DISABLE_WEBSOCKETS
-  ZERO_NULL,
-#else
-  &Curl_protocol_ws,
-#endif
-  CURLPROTO_WS,                         /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_CREDSPERREQUEST |             /* flags */
-  PROTOPT_USERPWDCTRL,
-  PORT_HTTP                             /* defport */
-}
-;
-
-const struct Curl_scheme Curl_scheme_wss = {
-  "WSS",                                /* scheme */
-#if defined(CURL_DISABLE_WEBSOCKETS) || !defined(USE_SSL)
-  ZERO_NULL,
-#else
-  &Curl_protocol_ws,
-#endif
-  CURLPROTO_WSS,                        /* protocol */
-  CURLPROTO_HTTP,                       /* family */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
-  PROTOPT_USERPWDCTRL,
-  PORT_HTTPS                            /* defport */
-};
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h
index 757eae0..71b5b3f 100644
--- a/Utilities/cmcurl/lib/ws.h
+++ b/Utilities/cmcurl/lib/ws.h
@@ -25,11 +25,10 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-extern const struct Curl_scheme Curl_scheme_ws;
-extern const struct Curl_scheme Curl_scheme_wss;
-
 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
 
+extern const struct Curl_protocol Curl_protocol_ws;
+
 /* meta key for storing protocol meta at connection */
 #define CURL_META_PROTO_WS_CONN   "meta:proto:ws:conn"
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
index 56d1c2b..5943694 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
@@ -3201,6 +3201,10 @@
 	bitlen = hf->bitlen;
 	len_avail = hf->len_size;
 	hf->tree_used = 0;
+	/* Initialize table to invalid values */
+	for (i = 0; i < tbl_size; i++) {
+		tbl[i] = (uint16_t)hf->len_size;
+	}
 	for (i = 0; i < len_avail; i++) {
 		uint16_t *p;
 		int len, cnt;