Merge topic 'enum_set-enhancements'

4d48958965 enum_set enhancements, step 3

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !10335
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 124bf7e..82d0968 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -439,9 +439,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:hip5.5-nvidia:
+t:hip6.3-nvidia:
     extends:
-        - .hip5.5_nvidia
+        - .hip6.3_nvidia
         - .cmake_test_linux_release
         - .linux_x86_64_tags_cuda
         - .run_dependent
@@ -449,9 +449,9 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:hip5.5-radeon:
+t:hip6.3-radeon:
     extends:
-        - .hip5.5_radeon
+        - .hip6.3_radeon
         - .cmake_test_linux_release
         - .linux_x86_64_tags_radeon
         - .run_dependent
diff --git a/.gitlab/ci/configure_hip5.5_nvidia.cmake b/.gitlab/ci/configure_hip6.3_nvidia.cmake
similarity index 100%
rename from .gitlab/ci/configure_hip5.5_nvidia.cmake
rename to .gitlab/ci/configure_hip6.3_nvidia.cmake
diff --git a/.gitlab/ci/configure_hip5.5_radeon.cmake b/.gitlab/ci/configure_hip6.3_radeon.cmake
similarity index 100%
rename from .gitlab/ci/configure_hip5.5_radeon.cmake
rename to .gitlab/ci/configure_hip6.3_radeon.cmake
diff --git a/.gitlab/ci/docker/hip5.5/deps_packages.lst b/.gitlab/ci/docker/hip5.5/deps_packages.lst
deleted file mode 100644
index 3276055..0000000
--- a/.gitlab/ci/docker/hip5.5/deps_packages.lst
+++ /dev/null
@@ -1,21 +0,0 @@
-# Install development tools.
-g++
-curl
-git
-
-# NVIDIA CUDA Compiler
-cuda-keyring
-cuda-nvcc-11-8
-cuda-profiler-api-11-8
-
-# NVIDIA CUDA Toolkit
-# These are not needed for HIP, but having them in
-# the environment allows us to run CUDA tests too.
-cuda-nvrtc-dev-11-8
-cuda-nvtx-11-8
-libcublas-dev-11-8
-libcufft-dev-11-8
-libcurand-dev-11-8
-libcusolver-dev-11-8
-libcusparse-dev-11-8
-libnpp-dev-11-8
diff --git a/.gitlab/ci/docker/hip5.5/Dockerfile b/.gitlab/ci/docker/hip6.3/Dockerfile
similarity index 92%
rename from .gitlab/ci/docker/hip5.5/Dockerfile
rename to .gitlab/ci/docker/hip6.3/Dockerfile
index 3a4aa53..a9f8303 100644
--- a/.gitlab/ci/docker/hip5.5/Dockerfile
+++ b/.gitlab/ci/docker/hip6.3/Dockerfile
@@ -1,9 +1,9 @@
 # syntax=docker/dockerfile:1
 
-ARG BASE_IMAGE=rocm/dev-ubuntu-22.04:5.5
+ARG BASE_IMAGE=rocm/dev-ubuntu-24.04:6.3.2
 
 FROM ${BASE_IMAGE} AS cuda-keyring
-ADD https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb /root/
+ADD https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb /root/
 RUN --mount=type=tmpfs,target=/var/log \
     dpkg -i /root/cuda-keyring_1.1-1_all.deb \
  && rm /root/cuda-keyring_1.1-1_all.deb
@@ -22,7 +22,7 @@
 MAINTAINER Brad King <brad.king@kitware.com>
 
 ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility
-ENV NVIDIA_REQUIRE_CUDA=cuda>=11.8
+ENV NVIDIA_REQUIRE_CUDA=cuda>=12.6
 ENV NVIDIA_VISIBLE_DEVICES=all
 ENV PATH="/opt/rocm/bin:$PATH"
 
diff --git a/.gitlab/ci/docker/hip6.3/deps_packages.lst b/.gitlab/ci/docker/hip6.3/deps_packages.lst
new file mode 100644
index 0000000..2ecf81e
--- /dev/null
+++ b/.gitlab/ci/docker/hip6.3/deps_packages.lst
@@ -0,0 +1,23 @@
+# Install development tools.
+g++
+curl
+git
+
+# NVIDIA CUDA Compiler
+cuda-keyring
+cuda-nvcc-12-6
+cuda-profiler-api-12-6
+
+# NVIDIA CUDA Toolkit
+# These are not needed for HIP, but having them in
+# the environment allows us to run CUDA tests too.
+cuda-nvrtc-dev-12-6
+cuda-nvtx-12-6
+cuda-opencl-dev-12-6
+libcublas-dev-12-6
+libcufft-dev-12-6
+libcurand-dev-12-6
+libcusolver-dev-12-6
+libcusparse-dev-12-6
+libnpp-dev-12-6
+libnvjitlink-dev-12-6
diff --git a/.gitlab/ci/docker/hip5.5/docker-clean b/.gitlab/ci/docker/hip6.3/docker-clean
similarity index 100%
rename from .gitlab/ci/docker/hip5.5/docker-clean
rename to .gitlab/ci/docker/hip6.3/docker-clean
diff --git a/.gitlab/ci/docker/hip5.5/dpkg-exclude b/.gitlab/ci/docker/hip6.3/dpkg-exclude
similarity index 100%
rename from .gitlab/ci/docker/hip5.5/dpkg-exclude
rename to .gitlab/ci/docker/hip6.3/dpkg-exclude
diff --git a/.gitlab/ci/docker/hip5.5/install_deps.sh b/.gitlab/ci/docker/hip6.3/install_deps.sh
similarity index 100%
rename from .gitlab/ci/docker/hip5.5/install_deps.sh
rename to .gitlab/ci/docker/hip6.3/install_deps.sh
diff --git a/.gitlab/ci/env_hip5.5_nvidia.sh b/.gitlab/ci/env_hip5.5_nvidia.sh
deleted file mode 100644
index 67d1ef2..0000000
--- a/.gitlab/ci/env_hip5.5_nvidia.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-export HIP_PLATFORM=nvidia
-export CUDA_PATH=/usr/local/cuda-11.8
-export PATH=/usr/local/cuda-11.8/bin:$PATH
-export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64
diff --git a/.gitlab/ci/env_hip6.3_nvidia.sh b/.gitlab/ci/env_hip6.3_nvidia.sh
new file mode 100644
index 0000000..3b326cb
--- /dev/null
+++ b/.gitlab/ci/env_hip6.3_nvidia.sh
@@ -0,0 +1,4 @@
+export HIP_PLATFORM=nvidia
+export CUDA_PATH=/usr/local/cuda-12.6
+export PATH=/usr/local/cuda-12.6/bin:$PATH
+export LD_LIBRARY_PATH=/usr/local/cuda-12.6/lib64
diff --git a/.gitlab/ci/typos.bash b/.gitlab/ci/typos.bash
index 7293a03..4c92383 100755
--- a/.gitlab/ci/typos.bash
+++ b/.gitlab/ci/typos.bash
@@ -7,8 +7,17 @@
 echo "Running 'typos' on source code..."
 typos || result=1
 
-# FIXME(typos): checking commit messages hits false positives
-# on "words" inside commit hashes.  We'd need a way to disable
-# checking of combined identifiers to avoid this.
+cfg='.typos.toml'
+tmp_cfg="${TEMP:-/tmp}/$cfg"
+# Uncomment `extend-ignore-identifiers-re` in the top-level config file
+# to make Git hashes (possibly used in commit messages) valid "identifiers".
+sed 's/^#\s*\(extend-ignore-identifiers-re\)/\1/' "$cfg" >"$tmp_cfg"
+
+if [ -n "$CI_MERGE_REQUEST_DIFF_BASE_SHA" ]; then
+  for COMMIT in $(git rev-list "^$CI_MERGE_REQUEST_DIFF_BASE_SHA" "$CI_COMMIT_SHA"); do
+    echo "Running 'typos' on commit message of $COMMIT..."
+    git show --format=%B -s "$COMMIT" | typos -c "$tmp_cfg" - || result=1
+  done
+fi
 
 exit $result
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index eacacad..461b34e 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -434,19 +434,21 @@
 
 ### HIP builds
 
-.hip5.5:
-    image: "kitware/cmake:ci-hip5.5-x86_64-2023-09-18"
+.hip6.3:
+    image: "kitware/cmake:ci-hip6.3-x86_64-2025-02-14"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
         CMAKE_ARCH: x86_64
         CTEST_LABELS: "HIP"
 
-.hip5.5_radeon:
-    extends: .hip5.5
+.hip6.3_radeon:
+    extends: .hip6.3
 
     variables:
-        CMAKE_CONFIGURATION: hip5.5_radeon
+        # FIXME(rocclr): device modules fail loading from binaries in paths with spaces
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
+        CMAKE_CONFIGURATION: hip6.3_radeon
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
 .debian12_hip_radeon:
@@ -463,11 +465,11 @@
         CMAKE_CONFIGURATION: fedora41_hip_radeon
         CTEST_LABELS: "HIP"
 
-.hip5.5_nvidia:
-    extends: .hip5.5
+.hip6.3_nvidia:
+    extends: .hip6.3
 
     variables:
-        CMAKE_CONFIGURATION: hip5.5_nvidia
+        CMAKE_CONFIGURATION: hip6.3_nvidia
         CTEST_LABELS: "HIP"
 
 ### C++ modules
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml
index d19dd74..d024f41 100644
--- a/.gitlab/os-windows.yml
+++ b/.gitlab/os-windows.yml
@@ -35,25 +35,25 @@
     variables:
         VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
         VCVARSPLATFORM: "x64"
-        VCVARSVERSION: "14.42.34433"
+        VCVARSVERSION: "14.43.34808"
 
 .windows_vcvarsall_vs2022_x86:
     variables:
         VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
         VCVARSPLATFORM: "x86"
-        VCVARSVERSION: "14.42.34433"
+        VCVARSVERSION: "14.43.34808"
 
 .windows_vcvarsall_vs2022_x64_arm64:
     variables:
         VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
         VCVARSPLATFORM: "x64_arm64"
-        VCVARSVERSION: "14.42.34433"
+        VCVARSVERSION: "14.43.34808"
 
 .windows_arm64_vcvarsall_vs2022:
     variables:
         VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
         VCVARSPLATFORM: "arm64"
-        VCVARSVERSION: "14.42.34433"
+        VCVARSVERSION: "14.43.34808"
 
 .windows_vs2022_x64_pch:
     extends:
@@ -119,7 +119,7 @@
         CMAKE_CONFIGURATION: windows_vs2022_x64
         CMAKE_GENERATOR: "Visual Studio 17 2022"
         CMAKE_GENERATOR_PLATFORM: "x64"
-        CMAKE_GENERATOR_TOOLSET: "v143,version=14.42.34433"
+        CMAKE_GENERATOR_TOOLSET: "v143,version=14.43.34808"
         CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
 
 .windows_vs2019_x64:
@@ -282,7 +282,7 @@
         CMAKE_CONFIGURATION: windows_arm64_vs2022
         CMAKE_GENERATOR: "Visual Studio 17 2022"
         CMAKE_GENERATOR_PLATFORM: "ARM64"
-        CMAKE_GENERATOR_TOOLSET: "v143,version=14.42.34433"
+        CMAKE_GENERATOR_TOOLSET: "v143,version=14.43.34808"
         CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
 
 .mingw_osdn_io:
@@ -316,7 +316,7 @@
         - windows-x86_64
         - shell
         - vs2022
-        - msvc-14.42
+        - msvc-14.43
         - nonconcurrent
 
 .windows_x86_64_tags_nonconcurrent_vs2022_arm64:
@@ -325,7 +325,7 @@
         - windows-x86_64
         - shell
         - vs2022
-        - msvc-14.42-arm64
+        - msvc-14.43-arm64
         - nonconcurrent
 
 .windows_x86_64_tags_concurrent_vs2022:
@@ -334,7 +334,7 @@
         - windows-x86_64
         - shell
         - vs2022
-        - msvc-14.42
+        - msvc-14.43
         - concurrent
 
 .windows_x86_64_tags_concurrent_vs2022_android:
@@ -344,7 +344,7 @@
         - shell
         - vs2022
         - vs17-android
-        - msvc-14.42
+        - msvc-14.43
         - concurrent
 
 .windows_x86_64_tags_concurrent_vs2019_android:
@@ -370,7 +370,7 @@
         - windows-arm64
         - shell
         - vs2022
-        - msvc-14.42
+        - msvc-14.43
         - nonconcurrent
 
 .windows_arm64_tags_concurrent_vs2022:
@@ -379,7 +379,7 @@
         - windows-arm64
         - shell
         - vs2022
-        - msvc-14.42
+        - msvc-14.43
         - concurrent
 
 ## Windows-specific scripts
diff --git a/.typos.toml b/.typos.toml
index c494769..c06745a 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -11,6 +11,11 @@
   , "(?Rm)^.*(#|/(/|\\*)|\\.\\.)\\s*(NOQA|noqa):? spellcheck(: *|=| +)disable-line$"
   ]
 locale = "en-us"
+# ATTENTION If, for any reason, you want to add the
+# `extend-ignore-identifiers-re` to this section,
+# please also modify the `.gitlab/ci/typos.bash`
+# script accordingly.
+#extend-ignore-identifiers-re=["\\b[0-9a-f]{10}\\b"]
 
 # Add repo-wide false positives here in the form of `word = "word"`.
 # Check the manual for details.
diff --git a/Help/command/POLICY_VERSION.txt b/Help/command/POLICY_VERSION.txt
new file mode 100644
index 0000000..424849d
--- /dev/null
+++ b/Help/command/POLICY_VERSION.txt
@@ -0,0 +1,16 @@
+This specifies that the current CMake code is written for the given range of
+CMake versions, ``<min>[...<max>]``. It sets the "policy version" to:
+
+* the range's ``<max>`` version, if specified, or to
+* the ``<min>`` version, or to
+* the value of the :variable:`CMAKE_POLICY_VERSION_MINIMUM` variable
+  if it is higher than the other two versions.
+
+The policy version effectively requests behavior preferred as of a given CMake
+version and tells newer CMake versions to warn about their new policies.
+All policies known to the running version of CMake and introduced
+in that version or earlier will be set to use ``NEW`` behavior.
+All policies introduced in later versions will be unset (unless the
+:variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` variable sets a default).
+This effectively requests behavior preferred as of a given CMake
+version and tells newer CMake versions to warn about their new policies.
diff --git a/Help/command/cmake_minimum_required.rst b/Help/command/cmake_minimum_required.rst
index 2833ed7..47e8e6b 100644
--- a/Help/command/cmake_minimum_required.rst
+++ b/Help/command/cmake_minimum_required.rst
@@ -19,7 +19,7 @@
 If the running version of CMake is lower than the ``<min>`` required
 version it will stop processing the project and report an error.
 The optional ``<policy_max>`` version, if specified, must be at least the
-``<min>`` version and affects policy settings as described in `Policy Settings`_.
+``<min>`` version and sets the `Policy Version`_.
 If the running version of CMake is older than 3.12, the extra ``...``
 dots will be seen as version component separators, resulting in the
 ``...<max>`` part being ignored and preserving the pre-3.12 behavior
@@ -48,38 +48,18 @@
   calling scope, calling ``cmake_minimum_required()`` inside a function
   is generally discouraged.
 
-.. _`Policy Settings`:
+.. _`Policy Version`:
 
-Policy Settings
-^^^^^^^^^^^^^^^
+Policy Version
+^^^^^^^^^^^^^^
 
-The ``cmake_minimum_required(VERSION)`` command implicitly invokes the
-:command:`cmake_policy(VERSION)` command to specify that the current
-project code is written for the given range of CMake versions.
-All policies known to the running version of CMake and introduced
-in the ``<min>`` (or ``<max>``, if specified) version or earlier will
-be set to use ``NEW`` behavior.  All policies introduced in later
-versions will be unset (unless the
-:variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` variable sets a default).
-This effectively requests behavior preferred as of a given CMake
-version and tells newer CMake versions to warn about their new policies.
-
-When a ``<min>`` version higher than 2.4 is specified the command
-implicitly invokes
+``cmake_minimum_required(VERSION <min>[...<max>])`` implicitly invokes
 
 .. code-block:: cmake
 
   cmake_policy(VERSION <min>[...<max>])
 
-which sets CMake policies based on the range of versions specified.
-When a ``<min>`` version 2.4 or lower is given the command implicitly
-invokes
-
-.. code-block:: cmake
-
-  cmake_policy(VERSION 2.4[...<max>])
-
-which enables compatibility features for CMake 2.4 and lower.
+.. include:: POLICY_VERSION.txt
 
 .. include:: DEPRECATED_POLICY_VERSIONS.txt
 
diff --git a/Help/command/cmake_policy.rst b/Help/command/cmake_policy.rst
index 4a08c01..276e160 100644
--- a/Help/command/cmake_policy.rst
+++ b/Help/command/cmake_policy.rst
@@ -39,14 +39,7 @@
 component separators, resulting in the ``...<max>`` part being ignored and
 preserving the pre-3.12 behavior of basing policies on ``<min>``.
 
-This specifies that the current CMake code is written for the given
-range of CMake versions.  All policies known to the running version of CMake
-and introduced in the ``<min>`` (or ``<max>``, if specified) version
-or earlier will be set to use ``NEW`` behavior.  All policies
-introduced in later versions will be unset (unless the
-:variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` variable sets a default).
-This effectively requests behavior preferred as of a given CMake
-version and tells newer CMake versions to warn about their new policies.
+.. include:: POLICY_VERSION.txt
 
 Note that the :command:`cmake_minimum_required(VERSION)`
 command implicitly calls ``cmake_policy(VERSION)`` too.
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index 1b6a555..cae7150 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -79,18 +79,14 @@
   version files are used).
 
   .. note::
-
     If the experimental ``CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES`` is enabled,
     files named ``<PackageName>.cps`` and ``<lowercasePackageName>.cps`` are
     also considered.  These files provide package information according to the
     |CPS|_ (CPS), which is more portable than CMake script.  Aside from any
     explicitly noted exceptions, any references to "config files", "config
     mode", "package configuration files", and so forth refer equally to both
-    CPS and CMake-script files.  However, some features of ``find_package``
-    are not supported at this time when a CPS file is found.  In particular,
-    if a ``VERSION`` requirement is specified, only ``.cps`` files which do not
-    provide version information will be rejected.  (We expect to implement
-    proper version validation in the near future.)
+    CPS and CMake-script files.  This functionality is a work in progress, and
+    some features may be missing.
 
     Search is implemented in a manner that will tend to prefer |CPS| files
     over CMake-script config files in most cases.  Specifying ``CONFIGS``
@@ -211,15 +207,20 @@
 * A single version with the format ``major[.minor[.patch[.tweak]]]``, where
   each component is a numeric value.
 * A version range with the format ``versionMin...[<]versionMax`` where
-  ``versionMin`` and ``versionMax`` have the same format and constraints
-  on components being integers as the single version.  By default, both end
-  points are included.  By specifying ``<``, the upper end point will be
-  excluded. Version ranges are only supported with CMake 3.19 or later.
-  Note that it is not possible to extend the compatibility range specified
-  by the package's version file.  For example, if the package version file
-  specifies compatibility within a minor version, it is not possible to
-  extend the compatibility to several minor versions by specifying a
-  version range.
+  ``versionMin`` and ``versionMax`` have the same format and constraints on
+  components being integers as the single version.  By default, both end points
+  are included.  By specifying ``<``, the upper end point will be excluded.
+  Version ranges are only supported with CMake 3.19 or later.
+
+.. note::
+  With the exception of CPS packages, version support is currently provided
+  only on a package-by-package basis.  When a version range is specified but
+  the package is only designed to expect a single version, the package will
+  ignore the upper end point of the range and only take the single version at
+  the lower end of the range into account.  Non-CPS packages that do support
+  version ranges do so in a manner that is determined by the individual
+  package.  See the `Version Selection`_ section below for details and
+  important caveats.
 
 The ``EXACT`` option requests that the version be matched exactly. This option
 is incompatible with the specification of a version range.
@@ -227,11 +228,7 @@
 If no ``[version]`` and/or component list is given to a recursive invocation
 inside a find-module, the corresponding arguments are forwarded
 automatically from the outer call (including the ``EXACT`` flag for
-``[version]``).  Version support is currently provided only on a
-package-by-package basis (see the `Version Selection`_ section below).
-When a version range is specified but the package is only designed to expect
-a single version, the package will ignore the upper end point of the range and
-only take the single version at the lower end of the range into account.
+``[version]``).
 
 See the :command:`cmake_policy` command documentation for discussion
 of the ``NO_POLICY_SCOPE`` option.
@@ -749,7 +746,7 @@
 These variables are checked by the ``find_package`` command to determine
 whether the configuration file provides an acceptable version.  They
 are not available after the ``find_package`` call returns.  If the version
-is acceptable the following variables are set:
+is acceptable, the following variables are set:
 
 ``<PackageName>_VERSION``
   Full provided version string
@@ -766,12 +763,80 @@
 
 and the corresponding package configuration file is loaded.
 
+.. note::
+  While the exact behavior of version matching is determined by the individual
+  package, many packages use :command:`write_basic_package_version_file` to
+  supply this logic.  The version check scripts this produces have some notable
+  caveats with respect to version ranges:
+
+  * The upper end of a version range acts as a hard limit on what versions will
+    be accepted.  Thus, while a request for version ``1.4.0`` might be
+    satisfied by a package whose version is ``1.6.0`` and which advertises
+    'same major version' compatibility, the same package will be rejected if
+    the requested version range is ``1.4.0...1.5.0``.
+
+  * Both ends of the version range must match the package's advertised
+    compatibility level. For example, if a package advertises 'same major and
+    minor version' compatibility, requesting the version range
+    ``1.4.0...<1.5.5`` or ``1.4.0...1.5.0`` will result in that package being
+    rejected, even if the package version is ``1.4.1``.
+
+  As a result, it is not possible to use a version range to extend the range
+  of compatible package versions that will be accepted.
+
 |CPS|
 """""
 
-For |CPS| package configuration files, no version checking is performed at
-this time.  However, packages using the ``simple`` version schema will set
-the following variables:
+For |CPS| package configuration files, package version numbers are checked by
+CMake according to the set of recognized version schemas. At present, the
+following schemas are recognized:
+
+  ``simple``
+    Version numbers are a tuple of integers followed by an optional trailing
+    segment which is ignored with respect to version comparisons.
+
+  ``custom``
+    The mechanism for interpreting version numbers is unspecified.  The version
+    strings must match exactly for the package to be accepted.
+
+Refer to |cps-version_schema|_ for a more detailed explanation of each schema
+and how comparisons for each are performed.  Note that the specification may
+include schemas that are not supported by CMake.
+
+In addition to the package's ``version``, CPS allows packages to optionally
+specify a |cps-compat_version|_, which is the oldest version for which the
+package provides compatibility.  That is, the package warrants that a consumer
+expecting the ``compat_version`` should be able to use the package, even if the
+package's actual version is newer.  If not specified, the ``compat_version``
+is implicitly equal to the package version, i.e. no backwards compatibility is
+provided.
+
+When a package uses a recognized schema, CMake will determine the package's
+acceptability according to the following rules:
+
+* If ``EXACT`` was specified, or if the package does not supply a
+  ``compat_version``, the package's ``version`` must equal the requested
+  version.
+
+* Otherwise:
+
+  * The package's ``version`` must be greater than or equal to the requested
+    (minimum) version, and
+
+  * the package's ``compat_version`` must be less than or equal to the
+    requested (minimum) version, and
+
+  * if a requested maximum version was given, it must be greater than (or equal
+    to, depending on whether the maximum version is specified as inclusive or
+    exclusive) the package's ``version``.
+
+.. note::
+  This implementation of range matching was chosen in order to most closely
+  match the behavior of :command:`write_basic_package_version_file`, albeit
+  without the case where an overly broad range matches nothing.
+
+For packages using the ``simple`` version schema, if the version is acceptable,
+the following variables are set:
 
 ``<PackageName>_VERSION``
   Full provided version string
@@ -878,3 +943,9 @@
 
 .. _CPS: https://cps-org.github.io/cps/
 .. |CPS| replace:: Common Package Specification
+
+.. _cps-compat_version: https://cps-org.github.io/cps/schema.html#compat-version
+.. |cps-compat_version| replace:: ``compat_version``
+
+.. _cps-version_schema: https://cps-org.github.io/cps/schema.html#version-schema
+.. |cps-version_schema| replace:: ``version_schema``
diff --git a/Help/command/list.rst b/Help/command/list.rst
index 0110021..abae8d2 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -246,6 +246,11 @@
                                       <replace_expression> ...)
         :target: TRANSFORM_REPLACE
 
+      .. versionchanged:: 4.1
+        The ``^`` anchor now matches only at the beginning of the input
+        element instead of the beginning of each repeated search.
+        See policy :policy:`CMP0186`.
+
   ``<SELECTOR>`` determines which elements of the list will be transformed.
   Only one type of selector can be specified at a time.
   When given, ``<SELECTOR>`` must be one of the following:
diff --git a/Help/command/string.rst b/Help/command/string.rst
index e9de32e..c510ff4 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -117,6 +117,11 @@
   two backslashes (``\\1``) are required in CMake code to get a backslash
   through argument parsing.
 
+.. versionchanged:: 4.1
+  The ``^`` anchor now matches only at the beginning of the input
+  string instead of the beginning of each repeated search.
+  See policy :policy:`CMP0186`.
+
 .. _`Regex Specification`:
 
 Regex Specification
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst
index d363485..4fa878e 100644
--- a/Help/manual/cmake-buildsystem.7.rst
+++ b/Help/manual/cmake-buildsystem.7.rst
@@ -1513,7 +1513,7 @@
 
   add_library(Eigen INTERFACE)
 
-  target_sources(Eigen INTERFACE
+  target_sources(Eigen PUBLIC
     FILE_SET HEADERS
       BASE_DIRS src
       FILES src/eigen.h src/vector.h src/matrix.h
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 271d7a9..f434e55 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -532,6 +532,11 @@
 
         $<LIST:TRANSFORM,list,REPLACE,regular_expression,replace_expression[,SELECTOR]>
 
+      .. versionchanged:: 4.1
+        The ``^`` anchor now matches only at the beginning of the input
+        element instead of the beginning of each repeated search.
+        See policy :policy:`CMP0186`.
+
   ``SELECTOR`` determines which items of the list will be transformed.
   Only one type of selector can be specified at a time. When given,
   ``SELECTOR`` must be one of the following:
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index bf0933a..9f3de09 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -92,6 +92,14 @@
 
 The following policies are supported.
 
+Policies Introduced by CMake 4.1
+--------------------------------
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0186: Regular expressions match ^ at most once in repeated searches. </policy/CMP0186>
+
 Policies Introduced by CMake 4.0
 --------------------------------
 
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 200fc18..cbf8a92 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -254,6 +254,7 @@
    /variable/CMAKE_MFC_FLAG
    /variable/CMAKE_MODULE_PATH
    /variable/CMAKE_POLICY_DEFAULT_CMPNNNN
+   /variable/CMAKE_POLICY_VERSION_MINIMUM
    /variable/CMAKE_POLICY_WARNING_CMPNNNN
    /variable/CMAKE_PREFIX_PATH
    /variable/CMAKE_PROGRAM_PATH
diff --git a/Help/policy/CMP0186.rst b/Help/policy/CMP0186.rst
new file mode 100644
index 0000000..747bce2
--- /dev/null
+++ b/Help/policy/CMP0186.rst
@@ -0,0 +1,43 @@
+CMP0186
+-------
+
+.. versionadded:: 4.1
+
+Regular expressions match ``^`` at most once in repeated searches.
+
+This policy affects commands that perform multiple regular expression
+searches:
+
+* :command:`string(REGEX MATCHALL)`
+* :command:`string(REGEX REPLACE)`
+* :command:`list(TRANSFORM REPLACE)`
+
+and the generator expression :genex:`$<LIST:TRANSFORM,list,REPLACE>`.
+
+CMake 4.0 and below match the ``^`` anchor at the start of every
+successive search, leading to multiple matches:
+
+.. code-block:: cmake
+
+  string(REGEX REPLACE "^a" "b" result "aaaa") # result="bbbb"
+  string(REGEX MATCHALL "^a" result "aaaa")    # result="a;a;a;a"
+
+CMake 4.1 and above prefer to match the ``^`` anchor at most once,
+at the start of the input string:
+
+.. code-block:: cmake
+
+  string(REGEX REPLACE "^a" "b" result "aaaa") # result="abbb"
+  string(REGEX MATCHALL "^a" result "aaaa")    # result="a"
+
+This policy provides compatibility for projects that have not been updated.
+
+The ``OLD`` behavior for this policy is to match ``^`` multiple times,
+at the start of each search.  The ``NEW`` behavior for this policy is
+to match ``^`` at most once, at the start of the input string.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/4.0.rst b/Help/release/4.0.rst
index 80a55bf..b1102d5 100644
--- a/Help/release/4.0.rst
+++ b/Help/release/4.0.rst
@@ -72,6 +72,10 @@
   to select runtime checks for compilers targeting the MSVC ABI.
   See policy :policy:`CMP0184`.
 
+* The :variable:`CMAKE_POLICY_VERSION_MINIMUM` variable was added to
+  help pacakgers and end users try to configure existing projects that
+  have not been updated to work with supported CMake versions.
+
 * The :variable:`CMAKE_XCODE_SCHEME_LLDB_INIT_FILE` variable and corresponding
   :prop_tgt:`XCODE_SCHEME_LLDB_INIT_FILE` target property were added to tell
   the :generator:`Xcode` generator what to put in the scheme's "LLDB Init File"
diff --git a/Help/release/dev/diab-compiler-support.rst b/Help/release/dev/diab-compiler-support.rst
new file mode 100644
index 0000000..2cf0bd6
--- /dev/null
+++ b/Help/release/dev/diab-compiler-support.rst
@@ -0,0 +1,8 @@
+diab-compiler-support
+---------------------
+
+* `Diab compilers from Wind River Systems`_, versions 5.9.x+, are now
+  supported with :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``Diab``
+  for languages ``ASM``, ``C``, and ``CXX``.
+
+.. _`Diab compilers from Wind River Systems`: https://www.windriver.com/resource/wind-river-diab-compiler-product-overview
diff --git a/Help/release/dev/regex-fixes.rst b/Help/release/dev/regex-fixes.rst
new file mode 100644
index 0000000..e979c03
--- /dev/null
+++ b/Help/release/dev/regex-fixes.rst
@@ -0,0 +1,5 @@
+regex-fixes
+-----------
+
+* Regular expressions match the ``^`` anchor at most once in repeated
+  searches, at the start of the input.  See policy :policy:`CMP0186`.
diff --git a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.rst b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.rst
index f8f553d..c4f5b98 100644
--- a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.rst
+++ b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.rst
@@ -39,5 +39,6 @@
   endfunction()
 
 See also :variable:`CMAKE_CURRENT_FUNCTION`,
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE` and
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE`.
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE` and
+:variable:`CMAKE_CURRENT_LIST_DIR`.
diff --git a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_FILE.rst b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_FILE.rst
index 437dfec..d533441 100644
--- a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_FILE.rst
+++ b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_FILE.rst
@@ -7,5 +7,6 @@
 contains the full path to the listfile that defined the current function.
 
 See also :variable:`CMAKE_CURRENT_FUNCTION`,
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR` and
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE`.
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE` and
+:variable:`CMAKE_CURRENT_LIST_FILE`.
diff --git a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_LINE.rst b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_LINE.rst
index 2fc7012..2b1f472 100644
--- a/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_LINE.rst
+++ b/Help/variable/CMAKE_CURRENT_FUNCTION_LIST_LINE.rst
@@ -8,5 +8,6 @@
 was defined.
 
 See also :variable:`CMAKE_CURRENT_FUNCTION`,
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR` and
-:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE`.
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE` and
+:variable:`CMAKE_CURRENT_LIST_LINE`.
diff --git a/Help/variable/CMAKE_CURRENT_LIST_DIR.rst b/Help/variable/CMAKE_CURRENT_LIST_DIR.rst
index ebc3ab9..b55bc37 100644
--- a/Help/variable/CMAKE_CURRENT_LIST_DIR.rst
+++ b/Help/variable/CMAKE_CURRENT_LIST_DIR.rst
@@ -14,4 +14,5 @@
 on the call stack, not the directory of the file containing the macro
 or function definition.
 
-See also :variable:`CMAKE_CURRENT_LIST_FILE`.
+See also :variable:`CMAKE_CURRENT_LIST_FILE` and
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR`.
diff --git a/Help/variable/CMAKE_CURRENT_LIST_FILE.rst b/Help/variable/CMAKE_CURRENT_LIST_FILE.rst
index 84b0eee..c94933d 100644
--- a/Help/variable/CMAKE_CURRENT_LIST_FILE.rst
+++ b/Help/variable/CMAKE_CURRENT_LIST_FILE.rst
@@ -12,4 +12,5 @@
 is the file invoking the bottom-most entry on the call stack, not the
 file containing the macro or function definition.
 
-See also :variable:`CMAKE_PARENT_LIST_FILE`.
+See also :variable:`CMAKE_PARENT_LIST_FILE` and
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE`.
diff --git a/Help/variable/CMAKE_CURRENT_LIST_LINE.rst b/Help/variable/CMAKE_CURRENT_LIST_LINE.rst
index 7f839c2..f23ba2f 100644
--- a/Help/variable/CMAKE_CURRENT_LIST_LINE.rst
+++ b/Help/variable/CMAKE_CURRENT_LIST_LINE.rst
@@ -9,3 +9,5 @@
 If CMake is currently processing deferred calls scheduled by
 the :command:`cmake_language(DEFER)` command, this variable
 evaluates to ``DEFERRED`` instead of a specific line number.
+
+See also :variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE`.
diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
index a4f899e..f46ac73 100644
--- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst
+++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
@@ -19,6 +19,7 @@
 ``Clang``                       `LLVM Clang`_
 ``Cray``                        Cray Compiler
 ``CrayClang``                   Cray Clang-based Compiler
+``Diab``                        `Wind River Systems Diab Compiler`_
 ``Embarcadero``, ``Borland``    `Embarcadero`_
 ``Flang``                       `Classic Flang Fortran Compiler`_
 ``LLVMFlang``                   `LLVM Flang Fortran Compiler`_
@@ -71,3 +72,4 @@
 .. _Tiny C Compiler: https://bellard.org/tcc
 .. _Tasking Compiler Toolsets: https://www.tasking.com
 .. _Texas Instruments Clang-based Compilers: https://www.ti.com/tool/download/ARM-CGT-CLANG
+.. _Wind River Systems Diab Compiler: https://www.windriver.com/resource/wind-river-diab-compiler-product-overview
diff --git a/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
index d643fb8..5f47d4e 100644
--- a/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
+++ b/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
@@ -22,3 +22,6 @@
 * Projects may set this variable before a call to :command:`add_subdirectory`
   that adds a third-party project in order to set its policies without
   modifying third-party code.
+
+See :variable:`CMAKE_POLICY_VERSION_MINIMUM` set policies to ``NEW``
+based on the version of CMake that introduced them.
diff --git a/Help/variable/CMAKE_POLICY_VERSION_MINIMUM.rst b/Help/variable/CMAKE_POLICY_VERSION_MINIMUM.rst
new file mode 100644
index 0000000..cf48ecf
--- /dev/null
+++ b/Help/variable/CMAKE_POLICY_VERSION_MINIMUM.rst
@@ -0,0 +1,23 @@
+CMAKE_POLICY_VERSION_MINIMUM
+----------------------------
+
+.. versionadded:: 4.0
+
+Specify a minimum :ref:`Policy Version` for a project without modifying
+its calls to :command:`cmake_minimum_required(VERSION)` and
+:command:`cmake_policy(VERSION)`.
+
+This variable should not be set by a project in CMake code as a way to
+set its own policy version.  Use :command:`cmake_minimum_required(VERSION)`
+and/or :command:`cmake_policy(VERSION)` for that.  This variable is meant
+to externally set policies for which a project has not itself been updated:
+
+* Users running CMake may set this variable in the cache, e.g.,
+  ``-DCMAKE_POLICY_VERSION_MINIMUM=3.5``, to try configuring a project
+  that has not been updated to set at least that policy version itself.
+
+* Projects may set this variable before a call to :command:`add_subdirectory`
+  that adds a third-party project in order to set its policy version without
+  modifying third-party code.
+
+See :variable:`CMAKE_POLICY_DEFAULT_CMP<NNNN>` to set individual policies.
diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake
index 2817d37..ad73373 100644
--- a/Modules/CMakeCompilerIdDetection.cmake
+++ b/Modules/CMakeCompilerIdDetection.cmake
@@ -89,6 +89,7 @@
       MSVC
       ADSP
       IAR
+      Diab
     )
     if ("x${lang}" STREQUAL "xC")
       list(APPEND ordered_compilers
diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake
index 412fe36..d3b425a 100644
--- a/Modules/CMakeDetermineASMCompiler.cmake
+++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -110,6 +110,10 @@
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_IAR )
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_IAR "IAR Assembler")
 
+  list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS Diab)
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_Diab "-V" )
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_Diab "Wind River Systems")
+
   list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS ARMCC)
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_ARMCC )
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_ARMCC "(ARM Compiler)|(ARM Assembler)|(Arm Compiler)")
diff --git a/Modules/Compiler/Diab-ASM.cmake b/Modules/Compiler/Diab-ASM.cmake
new file mode 100644
index 0000000..ccc8cb6
--- /dev/null
+++ b/Modules/Compiler/Diab-ASM.cmake
@@ -0,0 +1,3 @@
+include(Compiler/Diab)
+
+__compiler_diab(ASM)
diff --git a/Modules/Compiler/Diab-C.cmake b/Modules/Compiler/Diab-C.cmake
new file mode 100644
index 0000000..0fd5345
--- /dev/null
+++ b/Modules/Compiler/Diab-C.cmake
@@ -0,0 +1,9 @@
+include(Compiler/Diab)
+
+__compiler_diab(C)
+
+# c89/90 is both -Xdialect-c89
+set(CMAKE_C89_STANDARD_COMPILE_OPTION "-Xdialect-c89")
+set(CMAKE_C90_STANDARD_COMPILE_OPTION "-Xdialect-c89")
+set(CMAKE_C99_STANDARD_COMPILE_OPTION "-Xdialect-c99")
+set(CMAKE_C11_STANDARD_COMPILE_OPTION "-Xdialect-c11")
diff --git a/Modules/Compiler/Diab-CXX.cmake b/Modules/Compiler/Diab-CXX.cmake
new file mode 100644
index 0000000..a8a17c5
--- /dev/null
+++ b/Modules/Compiler/Diab-CXX.cmake
@@ -0,0 +1,12 @@
+include(Compiler/Diab)
+
+__compiler_diab(CXX)
+
+# Diab C++98 is named as -Xdialect-c++03
+set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "-Xdialect-c++03")
+set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-Xdialect-c++11")
+set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-Xdialect-c++14")
+set(CMAKE_CXX17_STANDARD_COMPILE_OPTION "-Xdialect-c++17")
+set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-Xdialect-c++20")
+
+__compiler_check_default_language_standard(CXX 4.0 98 5.0 11)
diff --git a/Modules/Compiler/Diab-DetermineCompiler.cmake b/Modules/Compiler/Diab-DetermineCompiler.cmake
new file mode 100644
index 0000000..86518f5
--- /dev/null
+++ b/Modules/Compiler/Diab-DetermineCompiler.cmake
@@ -0,0 +1,10 @@
+# Diab Toolchain. Works only for versions 5.9.x or higher.
+set(_compiler_id_pp_test "defined(__DCC__) && defined(_DIAB_TOOL)")
+
+set(_compiler_id_version_compute "
+  # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__VERSION_MAJOR_NUMBER__)
+  # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__VERSION_MINOR_NUMBER__)
+  # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__VERSION_ARCH_FEATURE_NUMBER__)
+  # define @PREFIX@COMPILER_VERSION_TWEAK @MACRO_DEC@(__VERSION_BUG_FIX_NUMBER__)
+"
+)
diff --git a/Modules/Compiler/Diab-FindBinUtils.cmake b/Modules/Compiler/Diab-FindBinUtils.cmake
new file mode 100644
index 0000000..7ca00cb
--- /dev/null
+++ b/Modules/Compiler/Diab-FindBinUtils.cmake
@@ -0,0 +1,15 @@
+# Find the archiver for the compiler architecture, which is always in the same
+# directory as the compiler.
+if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL "")
+  message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set")
+endif()
+
+get_filename_component(__diab_path "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY)
+
+find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR
+  NAMES dar
+  HINTS ${__diab_path}
+  NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
+  DOC "Diab Archiver"
+)
+mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
diff --git a/Modules/Compiler/Diab.cmake b/Modules/Compiler/Diab.cmake
new file mode 100644
index 0000000..cf32139
--- /dev/null
+++ b/Modules/Compiler/Diab.cmake
@@ -0,0 +1,33 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# This module is shared by multiple languages; use include blocker.
+if(__COMPILER_Diab)
+  return()
+endif()
+set(__COMPILER_Diab 1)
+
+include(Compiler/CMakeCommonCompilerMacros)
+
+macro(__compiler_diab lang)
+  set(CMAKE_${lang}_VERBOSE_FLAG "-#")
+  set(CMAKE_${lang}_OUTPUT_EXTENSION ".o")
+
+  string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
+  string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
+  string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O -Xsize-opt")
+  string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -XO")
+  string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -XO -g3")
+
+  set(__DIAB_AR "${CMAKE_${lang}_COMPILER_AR}")
+  set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${__DIAB_AR}\"  -r <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_CREATE "\"${__DIAB_AR}\" -r <TARGET> <LINK_FLAGS> <OBJECTS>")
+
+  set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
+  set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
+  set(CMAKE_${lang}_COMPILE_OPTIONS_IPO -XO -Xwhole-program-optim)
+endmacro()
+
+set(CMAKE_EXECUTABLE_SUFFIX "")
+set(CMAKE_LIBRARY_PATH_TERMINATOR "")
+set(CMAKE_LIBRARY_PATH_FLAG "")
diff --git a/Modules/FindGettext.cmake b/Modules/FindGettext.cmake
index 940d2c0..b982bb9 100644
--- a/Modules/FindGettext.cmake
+++ b/Modules/FindGettext.cmake
@@ -35,10 +35,11 @@
 
   .. code-block:: cmake
 
-    gettext_create_translations(<mofile> [ALL] <file>...)
+    gettext_create_translations(<potfile> [ALL] <file>...)
 
-  This will create a target "translations" which will convert the
-  given input .po files into the binary output .mo file. Options:
+  This function creates a custom target "translations" which processes the
+  given .pot file to .mo files. The generated binary files will be installed
+  into ``share/locale/`` directory. Options:
 
   ``ALL``
     The translations will be created when building the default target.
@@ -77,7 +78,7 @@
 
   ``INSTALL_DESTINATION``
     Install the results into the given directory (``share/locale/`` by
-    default). The language subdirectory will be taken into account .
+    default). The language subdirectory will be taken into account.
 
 .. versionadded:: 3.2
   If you wish to use the Gettext runtime library (libintl), use
@@ -95,7 +96,7 @@
                   OUTPUT_STRIP_TRAILING_WHITESPACE)
   get_filename_component(msgmerge_name ${GETTEXT_MSGMERGE_EXECUTABLE} NAME)
   get_filename_component(msgmerge_namewe ${GETTEXT_MSGMERGE_EXECUTABLE} NAME_WE)
-  if (gettext_version MATCHES "^(${msgmerge_name}|${msgmerge_namewe}) \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)")
+  if(gettext_version MATCHES "^(${msgmerge_name}|${msgmerge_namewe}) \\([^\\)]*\\) ([0-9\\.]+[^ \n]*)")
     set(GETTEXT_VERSION_STRING "${CMAKE_MATCH_2}")
   endif()
   unset(gettext_version)
@@ -116,7 +117,7 @@
   endif()
   set(${_unique_name} "${_name}_${currentCounter}" PARENT_SCOPE)
   math(EXPR currentCounter "${currentCounter} + 1")
-  set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter} )
+  set_property(GLOBAL PROPERTY ${propertyName} ${currentCounter})
 endfunction()
 
 macro(GETTEXT_CREATE_TRANSLATIONS _potFile _firstPoFileArg)
@@ -134,7 +135,7 @@
     set(_firstPoFile)
   endif()
 
-  foreach (_currentPoFile ${_firstPoFile} ${ARGN})
+  foreach(_currentPoFile ${_firstPoFile} ${ARGN})
     get_filename_component(_absFile ${_currentPoFile} ABSOLUTE)
     get_filename_component(_abs_PATH ${_absFile} PATH)
     get_filename_component(_lang ${_absFile} NAME_WE)
@@ -150,7 +151,7 @@
     install(FILES ${_gmoFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
     set(_gmoFiles ${_gmoFiles} ${_gmoFile})
 
-  endforeach ()
+  endforeach()
 
   if(NOT TARGET translations)
     add_custom_target(translations)
@@ -177,7 +178,7 @@
   string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _potBasename ${_potName})
   get_filename_component(_absPotFile ${_potFile} ABSOLUTE)
 
-  foreach (_lang ${_parsedArguments_LANGUAGES})
+  foreach(_lang ${_parsedArguments_LANGUAGES})
     set(_poFile  "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.po")
     set(_gmoFile "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo")
 
@@ -197,7 +198,7 @@
       install(FILES ${_gmoFile} DESTINATION ${_parsedArguments_INSTALL_DESTINATION}/${_lang}/LC_MESSAGES RENAME ${_potBasename}.mo)
     endif()
     list(APPEND _gmoFiles ${_gmoFile})
-  endforeach ()
+  endforeach()
 
   if(NOT TARGET potfiles)
     add_custom_target(potfiles)
@@ -245,7 +246,7 @@
     add_custom_target(pofiles)
   endif()
 
-  _GETTEXT_GET_UNIQUE_TARGET_NAME( pofiles uniqueTargetName)
+  _GETTEXT_GET_UNIQUE_TARGET_NAME(pofiles uniqueTargetName)
 
   if(_parsedArguments_ALL)
     add_custom_target(${uniqueTargetName} ALL DEPENDS ${_gmoFiles})
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 93521cf..1c5641e 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -47,9 +47,10 @@
     endif ()
   endif()
 
+  set (${_PYTHON_BASE}_FOUND FALSE)
   set (${_PYTHON_PREFIX}_FOUND FALSE)
-  string (TOUPPER "${_PYTHON_PREFIX}" _${_PYTHON_PREFIX}_UPPER_PREFIX)
-  set (${_PYTHON_UPPER_PREFIX}_FOUND FALSE)
+  string (TOUPPER "${_PYTHON_BASE}" _${_PYTHON_BASE}_UPPER_PREFIX)
+  set (${_${_PYTHON_BASE}_UPPER_PREFIX}_FOUND FALSE)
 endmacro()
 
 
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index bf50f8c..080e99f 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 0)
-set(CMake_VERSION_PATCH 20250213)
+set(CMake_VERSION_PATCH 20250215)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake
index 09d8250..dba9d50 100644
--- a/Source/Modules/CMakeBuildUtilities.cmake
+++ b/Source/Modules/CMakeBuildUtilities.cmake
@@ -159,6 +159,7 @@
     set(CURL_CA_BUNDLE "" CACHE FILEPATH "Path to SSL CA Certificate Bundle")
     set(CURL_CA_PATH "" CACHE PATH "Path to SSL CA Certificate Directory")
     mark_as_advanced(CURL_CA_BUNDLE CURL_CA_PATH)
+    find_package(OpenSSL)
   endif()
   if(NOT CMAKE_USE_SYSTEM_NGHTTP2)
     # Tell curl's FindNGHTTP2 module to use our library.
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 63a313c..7720485 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -59,6 +59,7 @@
 namespace {
 
 using pdt = cmFindPackageCommand::PackageDescriptionType;
+using ParsedVersion = cmPackageInfoReader::Pep440Version;
 
 template <template <typename> class Op>
 struct StrverscmpOp
@@ -2717,12 +2718,61 @@
     std::unique_ptr<cmPackageInfoReader> reader =
       cmPackageInfoReader::Read(config_file);
     if (reader && reader->GetName() == this->Name) {
+      // Read version information.
       cm::optional<std::string> cpsVersion = reader->GetVersion();
-      if (cpsVersion) {
-        // TODO: Implement version check for CPS
-        result = true;
-      } else {
-        result = this->Version.empty();
+      cm::optional<ParsedVersion> const& parsedVersion =
+        reader->ParseVersion(cpsVersion);
+      bool const hasVersion = cpsVersion.has_value();
+
+      // Test for version compatibility.
+      result = this->Version.empty();
+      if (hasVersion) {
+        version = std::move(*cpsVersion);
+
+        if (!this->Version.empty()) {
+          if (!parsedVersion) {
+            // If we don't understand the version, compare the exact versions
+            // using full string comparison. This is the correct behavior for
+            // the "custom" schema, and the best we can do otherwise.
+            result = (this->Version == version);
+          } else if (this->VersionExact) {
+            // If EXACT is specified, the version must be exactly the requested
+            // version.
+            result =
+              cmSystemTools::VersionCompareEqual(this->Version, version);
+          } else {
+            // Do we have a compat_version?
+            cm::optional<std::string> const& compatVersion =
+              reader->GetCompatVersion();
+            if (reader->ParseVersion(compatVersion)) {
+              // If yes, the initial result is whether the requested version is
+              // between the actual version and the compat version, inclusive.
+              result = cmSystemTools::VersionCompareGreaterEq(version,
+                                                              this->Version) &&
+                cmSystemTools::VersionCompareGreaterEq(this->Version,
+                                                       *compatVersion);
+
+              if (result && !this->VersionMax.empty()) {
+                // We must also check that the version is less than the version
+                // limit.
+                if (this->VersionRangeMax == VERSION_ENDPOINT_EXCLUDED) {
+                  result = cmSystemTools::VersionCompareGreater(
+                    this->VersionMax, version);
+                } else {
+                  result = cmSystemTools::VersionCompareGreaterEq(
+                    this->VersionMax, version);
+                }
+              }
+            } else {
+              // If no, compat_version is assumed to be exactly the actual
+              // version, so the result is whether the requested version is
+              // exactly the actual version, and we can ignore the version
+              // limit.
+              result =
+                cmSystemTools::VersionCompareEqual(this->Version, version);
+            }
+          }
+        }
       }
 
       if (result) {
@@ -2752,26 +2802,33 @@
           result = false;
         }
 
-        if (result && cpsVersion) {
-          this->VersionFound = (version = std::move(*cpsVersion));
+        if (result && hasVersion) {
+          this->VersionFound = version;
 
-          std::vector<unsigned> const& versionParts = reader->ParseVersion();
-          this->VersionFoundCount = static_cast<unsigned>(versionParts.size());
-          switch (this->VersionFoundCount) {
-            case 4:
-              this->VersionFoundTweak = versionParts[3];
-              CM_FALLTHROUGH;
-            case 3:
-              this->VersionFoundPatch = versionParts[2];
-              CM_FALLTHROUGH;
-            case 2:
-              this->VersionFoundMinor = versionParts[1];
-              CM_FALLTHROUGH;
-            case 1:
-              this->VersionFoundMajor = versionParts[0];
-              CM_FALLTHROUGH;
-            default:
-              break;
+          if (parsedVersion) {
+            std::vector<unsigned> const& versionParts =
+              parsedVersion->ReleaseComponents;
+
+            this->VersionFoundCount =
+              static_cast<unsigned>(versionParts.size());
+            switch (std::min(this->VersionFoundCount, 4u)) {
+              case 4:
+                this->VersionFoundTweak = versionParts[3];
+                CM_FALLTHROUGH;
+              case 3:
+                this->VersionFoundPatch = versionParts[2];
+                CM_FALLTHROUGH;
+              case 2:
+                this->VersionFoundMinor = versionParts[1];
+                CM_FALLTHROUGH;
+              case 1:
+                this->VersionFoundMajor = versionParts[0];
+                CM_FALLTHROUGH;
+              default:
+                break;
+            }
+          } else {
+            this->VersionFoundCount = 0;
           }
         }
         this->CpsReader = std::move(reader);
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 3f38b73..c67ce01 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1706,6 +1706,11 @@
                     return std::string{};
                   }
 
+                  if (!selector) {
+                    selector = cmList::TransformSelector::New();
+                  }
+                  selector->Makefile = ctx->LG->GetMakefile();
+
                   return list
                     .transform(descriptor->Action, arguments,
                                std::move(selector))
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
index 074e06f..529b2bb 100644
--- a/Source/cmList.cxx
+++ b/Source/cmList.cxx
@@ -523,8 +523,8 @@
                   std::string const& replace) override
   {
     TransformAction::Initialize(selector);
-    this->ReplaceHelper =
-      cm::make_unique<cmStringReplaceHelper>(regex, replace);
+    this->ReplaceHelper = cm::make_unique<cmStringReplaceHelper>(
+      regex, replace, selector->Makefile);
 
     if (!this->ReplaceHelper->IsRegularExpressionValid()) {
       throw transform_error(
@@ -643,6 +643,11 @@
 }
 }
 
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::New()
+{
+  return cm::make_unique<TransformNoSelector>();
+}
+
 std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
   std::initializer_list<index_type> indexes)
 {
diff --git a/Source/cmList.h b/Source/cmList.h
index b5be0b9..68772b9 100644
--- a/Source/cmList.h
+++ b/Source/cmList.h
@@ -23,6 +23,7 @@
 
 template <typename T>
 class BT;
+class cmMakefile;
 
 /**
  * CMake lists management
@@ -893,6 +894,7 @@
     // cmList::TransformSelector::New<AT>({1, 2, 5, 6});
     //  or
     // cmList::TransformSelector::New<REGEX>("^XX.*");
+    static std::unique_ptr<TransformSelector> New();
     template <typename Type>
     static std::unique_ptr<TransformSelector> New(
       std::initializer_list<index_type>);
@@ -907,6 +909,8 @@
     template <typename Type>
     static std::unique_ptr<TransformSelector> New(std::string&&);
 
+    cmMakefile* Makefile = nullptr;
+
   private:
     static std::unique_ptr<TransformSelector> NewAT(
       std::initializer_list<index_type> init);
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 7e046e0..eb6147b 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -678,6 +678,11 @@
       return true;
     }
 
+    if (!selector) {
+      selector = cmList::TransformSelector::New();
+    }
+    selector->Makefile = &status.GetMakefile();
+
     list->transform(descriptor->Action, arguments, std::move(selector));
     status.GetMakefile().AddDefinition(outputName, list->to_string());
     return true;
diff --git a/Source/cmPackageInfoReader.cxx b/Source/cmPackageInfoReader.cxx
index 82f2cb8..40ed54e 100644
--- a/Source/cmPackageInfoReader.cxx
+++ b/Source/cmPackageInfoReader.cxx
@@ -366,6 +366,62 @@
   }
 }
 
+cm::optional<cmPackageInfoReader::Pep440Version> ParseSimpleVersion(
+  std::string const& version)
+{
+  if (version.empty()) {
+    return cm::nullopt;
+  }
+
+  cmPackageInfoReader::Pep440Version result;
+  result.Simple = true;
+
+  cm::string_view remnant{ version };
+  for (;;) {
+    // Find the next part separator.
+    std::string::size_type const n = remnant.find_first_of(".+-"_s);
+    if (n == 0) {
+      // The part is an empty string.
+      return cm::nullopt;
+    }
+
+    // Extract the part as a number.
+    cm::string_view const part = remnant.substr(0, n);
+    std::string::size_type const l = part.size();
+    std::string::size_type p;
+    unsigned long const value = std::stoul(std::string{ part }, &p);
+    if (p != l || value > std::numeric_limits<unsigned>::max()) {
+      // The part was not a valid number or is too big.
+      return cm::nullopt;
+    }
+    result.ReleaseComponents.push_back(static_cast<unsigned>(value));
+
+    // Have we consumed the entire input?
+    if (n == std::string::npos) {
+      return { std::move(result) };
+    }
+
+    // Lop off the current part.
+    char const sep = remnant[n];
+    remnant = remnant.substr(n + 1);
+    if (sep == '+' || sep == '-') {
+      // If we hit the local label, we're done.
+      result.LocalLabel = remnant;
+      return { std::move(result) };
+    }
+
+    // We just consumed a '.'; check that there's more.
+    if (remnant.empty()) {
+      // A trailing part separator is not allowed.
+      return cm::nullopt;
+    }
+
+    // Continue with the remaining input.
+  }
+
+  // Unreachable.
+}
+
 } // namespace
 
 std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
@@ -428,40 +484,31 @@
   return cm::nullopt;
 }
 
-std::vector<unsigned> cmPackageInfoReader::ParseVersion() const
+cm::optional<std::string> cmPackageInfoReader::GetCompatVersion() const
+{
+  Json::Value const& version = this->Data["compat_version"];
+  if (version.isString()) {
+    return version.asString();
+  }
+  return cm::nullopt;
+}
+
+cm::optional<cmPackageInfoReader::Pep440Version>
+cmPackageInfoReader::ParseVersion(
+  cm::optional<std::string> const& version) const
 {
   // Check that we have a version.
-  cm::optional<std::string> const& version = this->GetVersion();
   if (!version) {
-    return {};
+    return cm::nullopt;
   }
 
-  std::vector<unsigned> result;
-  cm::string_view remnant{ *version };
-
   // Check if we know how to parse the version.
   Json::Value const& schema = this->Data["version_schema"];
   if (schema.isNull() || cmStrCaseEq(schema.asString(), "simple"_s)) {
-    // Keep going until we run out of parts.
-    while (!remnant.empty()) {
-      std::string::size_type n = remnant.find('.');
-      cm::string_view part = remnant.substr(0, n);
-      if (n == std::string::npos) {
-        remnant = {};
-      } else {
-        remnant = remnant.substr(n + 1);
-      }
-
-      unsigned long const value = std::stoul(std::string{ part }, &n);
-      if (n == 0 || value > std::numeric_limits<unsigned>::max()) {
-        // The part was not a valid number or is too big.
-        return {};
-      }
-      result.push_back(static_cast<unsigned>(value));
-    }
+    return ParseSimpleVersion(*version);
   }
 
-  return result;
+  return cm::nullopt;
 }
 
 std::vector<cmPackageRequirement> cmPackageInfoReader::GetRequirements() const
diff --git a/Source/cmPackageInfoReader.h b/Source/cmPackageInfoReader.h
index d31cee9..26eb807 100644
--- a/Source/cmPackageInfoReader.h
+++ b/Source/cmPackageInfoReader.h
@@ -43,11 +43,34 @@
 
   std::string GetName() const;
   cm::optional<std::string> GetVersion() const;
+  cm::optional<std::string> GetCompatVersion() const;
 
-  /// If the package uses the 'simple' version scheme, obtain the version as
-  /// a numeric tuple.  Returns an empty vector for other schemes or if no
-  /// version is specified.
-  std::vector<unsigned> ParseVersion() const;
+  // NOTE: The eventual intent is for CPS to support multiple version schemas,
+  // and in particular, we expect to want to support "simple", "custom", "rpm",
+  // "dpkg" and "pep440". Additionally, we desire to be able to parse each of
+  // these to the maximum extent possible; in particular, we want to be able
+  // to decompose "simple" and "pep440" versions into components represented
+  // as numeric types rather than strings, which is not possible with the "rpm"
+  // and "dpkg" schemas. Therefore, we require different data structures to
+  // represent different version schemas.
+
+  struct Pep440Version
+  {
+    // NOTE: This structure is currently incomplete as we only support the
+    // "simple" schema at this time.
+    bool Simple; // "simple" can be represented as a subset of "pep440"
+    std::vector<unsigned> ReleaseComponents;
+    cm::optional<std::string> LocalLabel;
+  };
+
+  // FIXME: Return a sum type (e.g. {cm,std}::variant) of possible versions
+  // when we support more than just the "simple" (and possibly "pep440")
+  // schema(s).
+  /// If the package uses the 'simple' version scheme, parse the provided
+  /// version string as a numeric tuple and optional trailing string.  Returns
+  /// a disengaged optional for other schemes or if no version is specified.
+  cm::optional<Pep440Version> ParseVersion(
+    cm::optional<std::string> const& version) const;
 
   std::vector<cmPackageRequirement> GetRequirements() const;
   std::vector<std::string> GetComponentNames() const;
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index 9de978e..908a786 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -15,6 +15,7 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 
 static bool stringToId(char const* input, cmPolicies::PolicyID& pid)
@@ -294,6 +295,30 @@
                                     unsigned int patchVer,
                                     WarnCompat warnCompat)
 {
+  cmValue varVer = mf->GetDefinition("CMAKE_POLICY_VERSION_MINIMUM");
+  if (!varVer.IsEmpty()) {
+    unsigned int varMajor = 0;
+    unsigned int varMinor = 0;
+    unsigned int varPatch = 0;
+    unsigned int varTweak = 0;
+    if (sscanf(varVer.GetCStr(), "%u.%u.%u.%u", &varMajor, &varMinor,
+               &varPatch, &varTweak) < 2) {
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Invalid CMAKE_POLICY_VERSION_MINIMUM value \"", varVer,
+                 "\".  "
+                 "A numeric major.minor[.patch[.tweak]] must be given."));
+      return false;
+    }
+    if (varMajor > majorVer || (varMajor == majorVer && varMinor > minorVer) ||
+        (varMajor == majorVer && varMinor == minorVer &&
+         varPatch > patchVer)) {
+      majorVer = varMajor;
+      minorVer = varMinor;
+      patchVer = varPatch;
+    }
+  }
+
   // Error on policy versions for which support has been removed.
   if (majorVer < 3 || (majorVer == 3 && minorVer < 5)) {
     if (IsFromLegacyInstallEXPORT(mf, majorVer, minorVer, patchVer)) {
@@ -305,7 +330,9 @@
     } else {
       mf->IssueMessage(MessageType::FATAL_ERROR,
                        "Compatibility with CMake < 3.5 has been removed "
-                       "from CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT);
+                       "from CMake.\n" ADVICE_UPDATE_VERSION_ARGUMENT "\n"
+                       "Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try "
+                       "configuring anyway.");
       cmSystemTools::SetFatalErrorOccurred();
       return false;
     }
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index d8a63a6..5f5f383 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -555,7 +555,10 @@
          WARN)                                                                \
   SELECT(POLICY, CMP0185,                                                     \
          "FindRuby no longer provides upper-case RUBY_* variables.", 4, 0, 0, \
-         WARN)
+         WARN)                                                                \
+  SELECT(POLICY, CMP0186,                                                     \
+         "Regular expressions match ^ at most once in repeated searches.", 4, \
+         1, 0, WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 72c9405..152a68b 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -1451,8 +1451,11 @@
     if (this->AutogenTarget.DependOrigin) {
       // add_dependencies/addUtility do not support generator expressions.
       // We depend only on the libraries found in all configs therefore.
-      std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
+      std::map<cmGeneratorTarget const*, std::size_t> targetsPartOfAllConfigs;
       for (std::string const& config : this->ConfigsList) {
+        // The same target might appear multiple times in a config, but we
+        // should only count it once.
+        std::set<cmGeneratorTarget const*> seenTargets;
         cmLinkImplementationLibraries const* libs =
           this->GenTarget->GetLinkImplementationLibraries(
             config, cmGeneratorTarget::UseTo::Link);
@@ -1460,14 +1463,15 @@
           for (cmLinkItem const& item : libs->Libraries) {
             cmGeneratorTarget const* libTarget = item.Target;
             if (libTarget &&
-                !StaticLibraryCycle(this->GenTarget, libTarget, config)) {
+                !StaticLibraryCycle(this->GenTarget, libTarget, config) &&
+                seenTargets.insert(libTarget).second) {
               // Increment target config count
-              commonTargets[libTarget]++;
+              targetsPartOfAllConfigs[libTarget]++;
             }
           }
         }
       }
-      for (auto const& item : commonTargets) {
+      for (auto const& item : targetsPartOfAllConfigs) {
         if (item.second == this->ConfigsList.size()) {
           this->AutogenTarget.DependTargets.insert(item.first->Target);
         }
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 33930d8..f923ba6 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -29,6 +29,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
@@ -288,10 +289,16 @@
   // Concatenate all the last arguments together.
   std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
 
+  unsigned optAnchor = 0;
+  if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0186) !=
+      cmPolicies::NEW) {
+    optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
+  }
+
   // Scan through the input for all matches.
   std::string output;
-  char const* p = input.c_str();
-  while (re.find(p)) {
+  std::string::size_type base = 0;
+  while (re.find(input, base, optAnchor)) {
     status.GetMakefile().ClearMatches();
     status.GetMakefile().StoreMatches(re);
     std::string::size_type l = re.start();
@@ -305,8 +312,8 @@
     if (!output.empty()) {
       output += ";";
     }
-    output += std::string(p + l, r - l);
-    p += r;
+    output += re.match();
+    base = r;
   }
 
   // Store the output in the provided variable.
diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx
index 0715f0b..5cd159e 100644
--- a/Source/cmStringReplaceHelper.cxx
+++ b/Source/cmStringReplaceHelper.cxx
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "cmMakefile.h"
+#include "cmPolicies.h"
 
 cmStringReplaceHelper::cmStringReplaceHelper(std::string const& regex,
                                              std::string replace_expr,
@@ -24,9 +25,16 @@
 {
   output.clear();
 
+  unsigned optAnchor = 0;
+  if (this->Makefile &&
+      this->Makefile->GetPolicyStatus(cmPolicies::CMP0186) !=
+        cmPolicies::NEW) {
+    optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
+  }
+
   // Scan through the input for all matches.
   std::string::size_type base = 0;
-  while (this->RegularExpression.find(input.c_str() + base)) {
+  while (this->RegularExpression.find(input, base, optAnchor)) {
     if (this->Makefile) {
       this->Makefile->ClearMatches();
       this->Makefile->StoreMatches(this->RegularExpression);
@@ -35,7 +43,7 @@
     auto r = this->RegularExpression.end();
 
     // Concatenate the part of the input that was not matched.
-    output += input.substr(base, l2);
+    output += input.substr(base, l2 - base);
 
     // Make sure the match had some text.
     if (r - l2 == 0) {
@@ -54,11 +62,8 @@
         // Replace with part of the match.
         auto n = replacement.Number;
         auto start = this->RegularExpression.start(n);
-        auto end = this->RegularExpression.end(n);
-        auto len = input.length() - base;
-        if ((start != std::string::npos) && (end != std::string::npos) &&
-            (start <= len) && (end <= len)) {
-          output += input.substr(base + start, end - start);
+        if (start != std::string::npos) {
+          output += this->RegularExpression.match(n);
         } else {
           std::ostringstream error;
           error << "replace expression \"" << this->ReplaceExpression
@@ -71,11 +76,11 @@
     }
 
     // Move past the match.
-    base += r;
+    base = r;
   }
 
   // Concatenate the text after the last match.
-  output += input.substr(base, input.length() - base);
+  output += input.substr(base);
 
   return true;
 }
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 57ba964..88b3e9a 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -4590,6 +4590,13 @@
     }
     linkOptions.AddFlag("ProgramDataBaseFile", pdb);
 
+    // Add image version
+    int major, minor;
+    this->GeneratorTarget->GetTargetVersion(major, minor);
+    if (major || minor) {
+      linkOptions.AddFlag("Version", cmStrCat(major, '.', minor));
+    }
+
     // A Windows Runtime component uses internal .NET metadata,
     // so does not have an import library.
     if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT") &&
diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx
index aa1a21e..aade305 100644
--- a/Tests/CMakeLib/testList.cxx
+++ b/Tests/CMakeLib/testList.cxx
@@ -740,7 +740,7 @@
     cmList list({ "ABC", "BBCB", "BCCCBC", "BCBCDD", "EBCBCEBC" });
 
     list.transform(cmList::TransformAction::REPLACE, "^BC|BC$", "X");
-    if (list.to_string() != "AX;BBCB;XCCX;XXDD;EBCBCEX") {
+    if (list.to_string() != "AX;BBCB;XCCX;XBCDD;EBCBCEX") {
       result = false;
     }
   }
diff --git a/Tests/FindPackageCpsTest/CMakeLists.txt b/Tests/FindPackageCpsTest/CMakeLists.txt
index 58de268..73b76da 100644
--- a/Tests/FindPackageCpsTest/CMakeLists.txt
+++ b/Tests/FindPackageCpsTest/CMakeLists.txt
@@ -22,23 +22,52 @@
 add_executable(FindPackageCpsTest FindPackageTest.cxx)
 
 ###############################################################################
+
+function(expect PACKAGE VAR OP VALUE WHAT)
+  if(NOT ${PACKAGE}_${VAR} ${OP} ${VALUE})
+    message(SEND_ERROR "${PACKAGE} wrong ${WHAT} ${${PACKAGE}_${VAR}} !")
+  endif()
+endfunction()
+
+function(test_version PACKAGE LITERAL COUNT MAJOR MINOR PATCH TWEAK)
+  if(NOT ${PACKAGE}_FOUND)
+    message(SEND_ERROR "${PACKAGE} not found !")
+  else()
+    expect(${PACKAGE} VERSION STREQUAL "${LITERAL}" "version")
+    expect(${PACKAGE} VERSION_COUNT EQUAL ${COUNT} "version count")
+    expect(${PACKAGE} VERSION_MAJOR EQUAL ${MAJOR} "major version")
+    expect(${PACKAGE} VERSION_MINOR EQUAL ${MINOR} "minor version")
+    expect(${PACKAGE} VERSION_PATCH EQUAL ${PATCH} "patch version")
+    expect(${PACKAGE} VERSION_TWEAK EQUAL ${TWEAK} "tweak version")
+  endif()
+endfunction()
+
+function(test_unparsed_version PACKAGE VERSION)
+  find_package(${PACKAGE} CONFIG)
+  test_version(${PACKAGE} "${VERSION}" 0 0 0 0 0)
+endfunction()
+
+###############################################################################
 # Test a basic package search.
 # It should NOT find the package's CMake package file.
 
 find_package(Sample CONFIG)
-if(NOT Sample_FOUND)
-  message(SEND_ERROR "Sample not found !")
-elseif(NOT Sample_VERSION STREQUAL "2.10.11")
-  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
-elseif(NOT Sample_VERSION_MAJOR EQUAL 2)
-  message(SEND_ERROR "Sample wrong major version ${Sample_VERSION_MAJOR} !")
-elseif(NOT Sample_VERSION_MINOR EQUAL 10)
-  message(SEND_ERROR "Sample wrong minor version ${Sample_VERSION_MINOR} !")
-elseif(NOT Sample_VERSION_PATCH EQUAL 11)
-  message(SEND_ERROR "Sample wrong patch version ${Sample_VERSION_PATCH} !")
-elseif(NOT Sample_VERSION_TWEAK EQUAL 0)
-  message(SEND_ERROR "Sample wrong tweak version ${Sample_VERSION_TWEAK} !")
-endif()
+test_version(Sample "2.10.11" 3 2 10 11 0)
+
+###############################################################################
+# Test some more complicated version parsing.
+
+find_package(LongVersion CONFIG)
+test_version(LongVersion "1.1.2.3.5.8+fibonacci" 6 1 1 2 3)
+
+find_package(EmptyMarker CONFIG)
+test_version(EmptyMarker "1.1+" 2 1 1 0 0)
+
+test_unparsed_version(BadVersion1 "1..1")
+test_unparsed_version(BadVersion2 "1.1a.0")
+test_unparsed_version(BadVersion3 "1.1.")
+test_unparsed_version(BadVersion4 "+42")
+test_unparsed_version(CustomVersion "VII")
 
 ###############################################################################
 # Test glob sorting.
@@ -62,13 +91,12 @@
 set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE)
 unset(SortLib_VERSION)
 
-# TODO Add this test once CPS version checking is implemented.
-# set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE)
-# FIND_PACKAGE(SortLib 4.0 CONFIG)
-# IF (NOT "${SortLib_VERSION}" STREQUAL "4.0.0")
-#   message(SEND_ERROR "FIND_PACKAGE_SORT_ORDER gave up too soon! ${SortLib_VERSION}")
-# endif()
-# unset(SortLib_VERSION)
+set(SortLib_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE)
+FIND_PACKAGE(SortLib 4.0 CONFIG)
+IF (NOT "${SortLib_VERSION}" STREQUAL "4.0.0")
+  message(SEND_ERROR "FIND_PACKAGE_SORT_ORDER gave up too soon! ${SortLib_VERSION}")
+endif()
+unset(SortLib_VERSION)
 
 set(SortFramework_DIR "" CACHE FILEPATH "Wipe out find results for testing." FORCE)
 set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
diff --git a/Tests/FindPackageCpsTest/cps/badversion1.cps b/Tests/FindPackageCpsTest/cps/badversion1.cps
new file mode 100644
index 0000000..05acfa1
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/badversion1.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "BadVersion1",
+  "version": "1..1",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/badversion2.cps b/Tests/FindPackageCpsTest/cps/badversion2.cps
new file mode 100644
index 0000000..99e683d
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/badversion2.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "BadVersion2",
+  "version": "1.1a.0",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/badversion3.cps b/Tests/FindPackageCpsTest/cps/badversion3.cps
new file mode 100644
index 0000000..97e670d
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/badversion3.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "BadVersion3",
+  "version": "1.1.",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/badversion4.cps b/Tests/FindPackageCpsTest/cps/badversion4.cps
new file mode 100644
index 0000000..2574866
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/badversion4.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "BadVersion4",
+  "version": "+42",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/customversion.cps b/Tests/FindPackageCpsTest/cps/customversion.cps
new file mode 100644
index 0000000..30aa29d
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/customversion.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "CustomVersion",
+  "version": "VII",
+  "version_schema": "roman",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/emptymarker.cps b/Tests/FindPackageCpsTest/cps/emptymarker.cps
new file mode 100644
index 0000000..2099c92
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/emptymarker.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "EmptyMarker",
+  "version": "1.1+",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/cps/longversion.cps b/Tests/FindPackageCpsTest/cps/longversion.cps
new file mode 100644
index 0000000..300952d
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/longversion.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "LongVersion",
+  "version": "1.1.2.3.5.8+fibonacci",
+  "version_schema": "simple",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/FindPackageCpsTest/share/cps/SortLib/SortLib.cps b/Tests/FindPackageCpsTest/share/cps/SortLib/SortLib.cps
new file mode 100644
index 0000000..fdc99dc
--- /dev/null
+++ b/Tests/FindPackageCpsTest/share/cps/SortLib/SortLib.cps
@@ -0,0 +1,7 @@
+{
+  "cps_version": "0.13",
+  "name": "SortLib",
+  "version": "4.0.0",
+  "cps_path": "@prefix@/cps/SortLib",
+  "components": {}
+}
diff --git a/Tests/RunCMake/Autogen_1/AutogenDuplicateDependency.cmake b/Tests/RunCMake/Autogen_1/AutogenDuplicateDependency.cmake
new file mode 100644
index 0000000..f6968d4
--- /dev/null
+++ b/Tests/RunCMake/Autogen_1/AutogenDuplicateDependency.cmake
@@ -0,0 +1,38 @@
+enable_language(CXX)
+
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core)
+
+set(CMAKE_AUTOMOC ON)
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/lib.cpp"
+     CONTENT "void foo() {}")
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/main.cpp"
+     CONTENT "int main() {return 0;}")
+
+# Test case.
+# App depends on Lib, which means App_autogen depends on Lib_autogen by default.
+# Which means building App_autogen should trigger the generation of the
+# fancy_generated.txt file, which is a dependency of Lib_autogen.
+
+# Create a shared library and an executable.
+add_library(Lib SHARED "${CMAKE_CURRENT_BINARY_DIR}/lib.cpp")
+add_executable(App "${CMAKE_CURRENT_BINARY_DIR}/main.cpp")
+
+# Link Lib into App more than once. Previously this was not properly handled by AUTOGEN,
+# which discarded the Lib_autogen dependency from App_autogen entirely, and the
+# file was not generated.
+foreach(i RANGE 1 ${LIB_LINK_COUNT})
+  target_link_libraries(App PRIVATE Lib)
+endforeach()
+
+# Add a custom target that generates a file.
+set(generated_file_path "${CMAKE_CURRENT_BINARY_DIR}/fancy_generated.txt")
+add_custom_command(
+    OUTPUT "${generated_file_path}"
+    COMMAND ${CMAKE_COMMAND} -E touch "${generated_file_path}"
+)
+add_custom_target(generate_file DEPENDS "${generated_file_path}")
+
+# Make sure the file is generated as part of building the Lib_autogen target.
+set_target_properties(Lib PROPERTIES
+    AUTOGEN_TARGET_DEPENDS generate_file)
diff --git a/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake b/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake
index 2cf60e4..c49f09c 100644
--- a/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Autogen_1/RunCMakeTest.cmake
@@ -131,4 +131,19 @@
       endblock()
     endif()
   endif()
+  block()
+    foreach(LIB_LINK_COUNT RANGE 1 4)
+      set(RunCMake_TEST_VARIANT_DESCRIPTION "-link-count-${LIB_LINK_COUNT}")
+      set(RunCMake_TEST_BINARY_DIR
+        "${RunCMake_BINARY_DIR}/AutogenDuplicateDependency-build-${LIB_LINK_COUNT}")
+      run_cmake_with_options(AutogenDuplicateDependency ${RunCMake_TEST_OPTIONS} -DLIB_LINK_COUNT=${LIB_LINK_COUNT})
+      set(RunCMake_TEST_NO_CLEAN 1)
+      set(RunCMake_TEST_EXPECT_stdout "fancy_generated.txt")
+      run_cmake_command("AutogenDuplicateDependency-build"
+        ${CMAKE_COMMAND} --build . --target App_autogen --verbose)
+      unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+      unset(RunCMake_TEST_EXPECT_stdout)
+      unset(RunCMake_TEST_NO_CLEAN)
+    endforeach()
+  endblock()
 endif ()
diff --git a/Tests/RunCMake/CPack/ArchiveCommon/common_helpers.cmake b/Tests/RunCMake/CPack/ArchiveCommon/common_helpers.cmake
index 948c6ab..34c8abf 100644
--- a/Tests/RunCMake/CPack/ArchiveCommon/common_helpers.cmake
+++ b/Tests/RunCMake/CPack/ArchiveCommon/common_helpers.cmake
@@ -60,7 +60,7 @@
 
   unset(filtered_)
   foreach(part_ IN LISTS prepared_)
-    string(REGEX REPLACE "^/" "" part_ "${part_}")
+    string(REGEX REPLACE "^/+" "" part_ "${part_}")
 
     if(part_)
       list(APPEND filtered_ "${prefix_}${part_}")
diff --git a/Tests/RunCMake/GenEx-LIST/CMP0186-NEW-check.cmake b/Tests/RunCMake/GenEx-LIST/CMP0186-NEW-check.cmake
new file mode 100644
index 0000000..0a1ef60
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/CMP0186-NEW-check.cmake
@@ -0,0 +1,11 @@
+set(expected "
+  000;1001;002
+  x000;1001;x002
+  x000;x01;x002
+")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/generated.txt" generated)
+
+if(NOT generated STREQUAL expected)
+  set(RunCMake_TEST_FAILED "generated:${generated}\nexpected:${expected}")
+endif()
diff --git a/Tests/RunCMake/GenEx-LIST/CMP0186-NEW.cmake b/Tests/RunCMake/GenEx-LIST/CMP0186-NEW.cmake
new file mode 100644
index 0000000..6f97614
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/CMP0186-NEW.cmake
@@ -0,0 +1,5 @@
+file(GENERATE OUTPUT "generated.txt" CONTENT "
+  $<LIST:TRANSFORM,0000;1001;0002,REPLACE,^0,>
+  $<LIST:TRANSFORM,0000;1001;0002,REPLACE,^(a|0),x>
+  $<LIST:TRANSFORM,0000;1001;0002,REPLACE,(1|^)0,x>
+")
diff --git a/Tests/RunCMake/GenEx-LIST/CMP0186-OLD-check.cmake b/Tests/RunCMake/GenEx-LIST/CMP0186-OLD-check.cmake
new file mode 100644
index 0000000..e44ab3b
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/CMP0186-OLD-check.cmake
@@ -0,0 +1,11 @@
+set(expected "
+  ;1001;2
+  xxxx;1001;xxx2
+  xxxx;xx1;xxx2
+")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/generated.txt" generated)
+
+if(NOT generated STREQUAL expected)
+  set(RunCMake_TEST_FAILED "generated:${generated}\nexpected:${expected}")
+endif()
diff --git a/Tests/RunCMake/GenEx-LIST/CMP0186-OLD.cmake b/Tests/RunCMake/GenEx-LIST/CMP0186-OLD.cmake
new file mode 100644
index 0000000..845005a
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LIST/CMP0186-OLD.cmake
@@ -0,0 +1 @@
+include(CMP0186-NEW.cmake)
diff --git a/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake
index 1946e84..0b0177a 100644
--- a/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-LIST/RunCMakeTest.cmake
@@ -128,3 +128,6 @@
 check_list_execution (TRANSFORM-REPLACE)
 check_list_execution (REVERSE)
 check_list_execution (SORT)
+
+run_cmake_with_options(CMP0186-OLD -DCMAKE_POLICY_DEFAULT_CMP0186=OLD)
+run_cmake_with_options(CMP0186-NEW -DCMAKE_POLICY_DEFAULT_CMP0186=NEW)
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index 37f780c..232e637 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -49,12 +49,14 @@
 run_cmake(VsPlatformToolset)
 run_cmake(VsControlFlowGuardLinkSetting)
 run_cmake(VsToolOverride)
+run_cmake(VsImageVersion)
 
 run_cmake(VsWinRTByDefault)
 
-set(RunCMake_GENERATOR_TOOLSET "VCTargetsPath=$(VCTargetsPath)")
-run_cmake(VsVCTargetsPath)
-unset(RunCMake_GENERATOR_TOOLSET)
+block()
+  set(RunCMake_GENERATOR_TOOLSET "VCTargetsPath=$(VCTargetsPath)")
+  run_cmake(VsVCTargetsPath)
+endblock()
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   run_cmake(VsJustMyCode)
@@ -65,9 +67,8 @@
 endif()
 
 # Visual Studio 2017 has toolset version 141
-string(REPLACE "v" "" generator_toolset "${RunCMake_GENERATOR_TOOLSET}")
 if (RunCMake_GENERATOR MATCHES "Visual Studio 1[0-4] 201[0-5]" OR
-   (RunCMake_GENERATOR_TOOLSET AND generator_toolset VERSION_LESS "141"))
+   (RunCMake_GENERATOR_TOOLSET MATCHES "^v([0-9]+)" AND CMAKE_MATCH_1 LESS 141))
   run_cmake(UnityBuildPre2017)
 else()
   run_cmake(UnityBuildNative)
diff --git a/Tests/RunCMake/VS10Project/VsImageVersion-check.cmake b/Tests/RunCMake/VS10Project/VsImageVersion-check.cmake
new file mode 100644
index 0000000..f0eeecd
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsImageVersion-check.cmake
@@ -0,0 +1,28 @@
+macro(ensure_link_version projectFile expected)
+  if(NOT EXISTS "${projectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${projectFile} does not exist.")
+    return()
+  endif()
+
+  file(STRINGS "${projectFile}" lines)
+  set(version "")
+  foreach(line IN LISTS lines)
+    if(line MATCHES "<Link>")
+      set(in_link TRUE)
+    elseif(line MATCHES "</Link>")
+      if(NOT version STREQUAL "${expected}")
+        set(RunCMake_TEST_FAILED "<Version> not found or incorrect: ${version} vs ${expected}")
+        return()
+      endif()
+      set(in_link FALSE)
+      set(version "")
+    elseif(in_link AND line MATCHES "<Version>([^<]+)</Version>")
+      set(version ${CMAKE_MATCH_1})
+    endif()
+  endforeach()
+endmacro()
+
+ensure_link_version("${RunCMake_TEST_BINARY_DIR}/app-C.vcxproj" 0.1)
+ensure_link_version("${RunCMake_TEST_BINARY_DIR}/app-CXX.vcxproj" 1.0)
+ensure_link_version("${RunCMake_TEST_BINARY_DIR}/lib-C.vcxproj" 65535.65535)
+ensure_link_version("${RunCMake_TEST_BINARY_DIR}/lib-CXX.vcxproj" "")
diff --git a/Tests/RunCMake/VS10Project/VsImageVersion.cmake b/Tests/RunCMake/VS10Project/VsImageVersion.cmake
new file mode 100644
index 0000000..80e49a6
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsImageVersion.cmake
@@ -0,0 +1,11 @@
+enable_language(C CXX)
+
+add_executable(app-C empty.c)
+add_executable(app-CXX empty.cxx)
+add_library(lib-C SHARED empty.c)
+add_library(lib-CXX SHARED empty.cxx)
+
+set_property(TARGET app-C PROPERTY VERSION 0.1)
+set_property(TARGET app-CXX PROPERTY VERSION 1.0)
+set_property(TARGET lib-C PROPERTY VERSION 65535.65535)
+set_property(TARGET lib-CXX PROPERTY VERSION "")
diff --git a/Tests/RunCMake/cmake_minimum_required/BeforeVersionRemoved-stderr.txt b/Tests/RunCMake/cmake_minimum_required/BeforeVersionRemoved-stderr.txt
index 62145e6..f2f41af 100644
--- a/Tests/RunCMake/cmake_minimum_required/BeforeVersionRemoved-stderr.txt
+++ b/Tests/RunCMake/cmake_minimum_required/BeforeVersionRemoved-stderr.txt
@@ -4,5 +4,7 @@
   Update the VERSION argument <min> value\.  Or, use the <min>\.\.\.<max> syntax
   to tell CMake that the project requires at least <min> but has been updated
   to work with policies introduced by <max> or earlier\.
+
+  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3\.5 to try configuring anyway\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyBeforeVersionRemoved-stderr.txt b/Tests/RunCMake/cmake_minimum_required/PolicyBeforeVersionRemoved-stderr.txt
index c9eba0f..bf4035a 100644
--- a/Tests/RunCMake/cmake_minimum_required/PolicyBeforeVersionRemoved-stderr.txt
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyBeforeVersionRemoved-stderr.txt
@@ -4,5 +4,7 @@
   Update the VERSION argument <min> value\.  Or, use the <min>\.\.\.<max> syntax
   to tell CMake that the project requires at least <min> but has been updated
   to work with policies introduced by <max> or earlier\.
+
+  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3\.5 to try configuring anyway\.
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable-stderr.txt b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable-stderr.txt
new file mode 100644
index 0000000..4f161bf
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable-stderr.txt
@@ -0,0 +1,3 @@
+^CMAKE_MINIMUM_REQUIRED_VERSION='3\.1'
+CMP0071='NEW'
+CMP0072=''$
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable.cmake b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable.cmake
new file mode 100644
index 0000000..553fc94
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariable.cmake
@@ -0,0 +1,7 @@
+set(CMAKE_POLICY_VERSION_MINIMUM 3.10)
+cmake_minimum_required(VERSION 3.1...3.4)
+message("CMAKE_MINIMUM_REQUIRED_VERSION='${CMAKE_MINIMUM_REQUIRED_VERSION}'")
+foreach(policy CMP0071 CMP0072)
+  cmake_policy(GET ${policy} status)
+  message("${policy}='${status}'")
+endforeach()
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-result.txt b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-stderr.txt b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-stderr.txt
new file mode 100644
index 0000000..3a59bb7
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at PolicyVersionVariableBad\.cmake:2 \(cmake_minimum_required\):
+  Invalid CMAKE_POLICY_VERSION_MINIMUM value "\.\.\.3\.10"\.  A numeric
+  major\.minor\[\.patch\[\.tweak\]\] must be given\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad.cmake b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad.cmake
new file mode 100644
index 0000000..763997b
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/PolicyVersionVariableBad.cmake
@@ -0,0 +1,2 @@
+set(CMAKE_POLICY_VERSION_MINIMUM ...3.10)
+cmake_minimum_required(VERSION 3.1...3.4)
diff --git a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
index cd63093..d91f171 100644
--- a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
@@ -7,3 +7,5 @@
 run_cmake(Range)
 run_cmake(RangeBad)
 run_cmake(Unknown)
+run_cmake(PolicyVersionVariable)
+run_cmake(PolicyVersionVariableBad)
diff --git a/Tests/RunCMake/find_package-CPS/CompatVersion.cmake b/Tests/RunCMake/find_package-CPS/CompatVersion.cmake
new file mode 100644
index 0000000..a8a26e1
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/CompatVersion.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package that is version-compatible with our request.
+find_package(Sample 1.2.0 REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.2.3+clarke")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/CustomVersion.cmake b/Tests/RunCMake/find_package-CPS/CustomVersion.cmake
new file mode 100644
index 0000000..5ec5f85
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/CustomVersion.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION ASC)
+
+###############################################################################
+# Test finding a package that uses the "custom" version schema.
+find_package(CustomVersion 42 REQUIRED)
+if(NOT CustomVersion_VERSION EQUAL 42)
+  message(SEND_ERROR "CustomVersion wrong version ${CustomVersion_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/ExactVersion.cmake b/Tests/RunCMake/find_package-CPS/ExactVersion.cmake
new file mode 100644
index 0000000..6fbf614
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/ExactVersion.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions (exact match).
+find_package(Sample 1.1.0 EXACT REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.1.0+asimov")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/MissingComponent-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingComponent-stderr.txt
index d617cfd..14f1f0a 100644
--- a/Tests/RunCMake/find_package-CPS/MissingComponent-stderr.txt
+++ b/Tests/RunCMake/find_package-CPS/MissingComponent-stderr.txt
@@ -5,7 +5,7 @@
   The following configuration files were considered but not accepted:
 (
     [^
-]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: unknown)+
+]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+
 
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt
index c102911..50c4afe 100644
--- a/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt
+++ b/Tests/RunCMake/find_package-CPS/MissingTransitiveComponent-stderr.txt
@@ -5,7 +5,7 @@
   The following configuration files were considered but not accepted:
 (
     [^
-]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: unknown)+
+]*/Tests/RunCMake/find_package-CPS/cps/[Cc]omponent[Tt]est\.cps, version: 1\.0)+
 
 Call Stack \(most recent call first\):
   MissingTransitiveComponent\.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion1-result.txt b/Tests/RunCMake/find_package-CPS/MissingVersion1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion1-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingVersion1-stderr.txt
new file mode 100644
index 0000000..76879a2
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion1-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at MissingVersion1\.cmake:[0-9]+ \(find_package\):
+  Could not find a configuration file for package "Sample" that is compatible
+  with requested version "1\.2\.7"\.
+
+  The following configuration files were considered but not accepted:
+
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/sample\.cps, version: 1\.5\.0\+niven
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/sample\.cps, version: 1\.4\.2\+adams
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/sample\.cps, version: 1\.2\.3\+clarke
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/sample\.cps, version: 1\.1\.0\+asimov
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion1.cmake b/Tests/RunCMake/find_package-CPS/MissingVersion1.cmake
new file mode 100644
index 0000000..1a01bb9
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion1.cmake
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with no suitable version matches.
+find_package(Sample 1.2.7 REQUIRED)
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion2-result.txt b/Tests/RunCMake/find_package-CPS/MissingVersion2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion2-stderr.txt b/Tests/RunCMake/find_package-CPS/MissingVersion2-stderr.txt
new file mode 100644
index 0000000..c984ce7
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion2-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at MissingVersion2\.cmake:[0-9]+ \(find_package\):
+  Could not find a configuration file for package "Sample" that is compatible
+  with requested version "1\.4\.9"\.
+
+  The following configuration files were considered but not accepted:
+
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/sample\.cps, version: 1\.5\.0\+niven
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/sample\.cps, version: 1\.4\.2\+adams
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/sample\.cps, version: 1\.2\.3\+clarke
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/sample\.cps, version: 1\.1\.0\+asimov
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package-CPS/MissingVersion2.cmake b/Tests/RunCMake/find_package-CPS/MissingVersion2.cmake
new file mode 100644
index 0000000..2e8e737
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MissingVersion2.cmake
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with no suitable version matches.
+find_package(Sample 1.4.9 REQUIRED)
diff --git a/Tests/RunCMake/find_package-CPS/MultipleVersions.cmake b/Tests/RunCMake/find_package-CPS/MultipleVersions.cmake
new file mode 100644
index 0000000..6fe1691
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/MultipleVersions.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions (permissive match).
+find_package(Sample 1.1.0 REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.2.3+clarke")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake b/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake
index 67de955..8df4f60 100644
--- a/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package-CPS/RunCMakeTest.cmake
@@ -8,6 +8,22 @@
   "-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
   )
 
+# Version-matching tests
+run_cmake(ExactVersion)
+run_cmake(CompatVersion)
+run_cmake(MultipleVersions)
+run_cmake(VersionLimit1)
+run_cmake(VersionLimit2)
+run_cmake(TransitiveVersion)
+run_cmake(CustomVersion)
+
+# Version-matching failure tests
+run_cmake(MissingVersion1)
+run_cmake(MissingVersion2)
+run_cmake(VersionLimit3)
+run_cmake(VersionLimit4)
+
+# Component-related failure tests
 run_cmake(MissingTransitiveDependency)
 run_cmake(MissingComponent)
 run_cmake(MissingComponentDependency)
diff --git a/Tests/RunCMake/find_package-CPS/TransitiveVersion.cmake b/Tests/RunCMake/find_package-CPS/TransitiveVersion.cmake
new file mode 100644
index 0000000..d4172ab
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/TransitiveVersion.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION ASC)
+
+###############################################################################
+# Test finding a package with a versioned dependency.
+find_package(TransitiveVersion REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.2.3+clarke")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit1.cmake b/Tests/RunCMake/find_package-CPS/VersionLimit1.cmake
new file mode 100644
index 0000000..877746d
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit1.cmake
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions when a version limit
+# is present but not restricting the matches.
+find_package(Sample 1.1.0...1.2.5 REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.2.3+clarke")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit2.cmake b/Tests/RunCMake/find_package-CPS/VersionLimit2.cmake
new file mode 100644
index 0000000..3d614b4
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit2.cmake
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions when a version limit
+# is present and restricting the matches.
+find_package(Sample 1.1.0...<1.2.0 REQUIRED)
+if(NOT Sample_VERSION STREQUAL "1.1.0+asimov")
+  message(SEND_ERROR "Sample wrong version ${Sample_VERSION} !")
+endif()
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit3-result.txt b/Tests/RunCMake/find_package-CPS/VersionLimit3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit3-stderr.txt b/Tests/RunCMake/find_package-CPS/VersionLimit3-stderr.txt
new file mode 100644
index 0000000..3b1c5ef
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit3-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at VersionLimit3\.cmake:[0-9]+ \(find_package\):
+  Could not find a configuration file for package "Sample" that is compatible
+  with requested version range "1\.2\.0\.\.\.1\.2\.2"\.
+
+  The following configuration files were considered but not accepted:
+
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/sample\.cps, version: 1\.5\.0\+niven
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/sample\.cps, version: 1\.4\.2\+adams
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/sample\.cps, version: 1\.2\.3\+clarke
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/sample\.cps, version: 1\.1\.0\+asimov
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit3.cmake b/Tests/RunCMake/find_package-CPS/VersionLimit3.cmake
new file mode 100644
index 0000000..a7101f2
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit3.cmake
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions when a version limit
+# is excluding a match.
+find_package(Sample 1.2.0...1.2.2 REQUIRED)
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit4-result.txt b/Tests/RunCMake/find_package-CPS/VersionLimit4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit4-stderr.txt b/Tests/RunCMake/find_package-CPS/VersionLimit4-stderr.txt
new file mode 100644
index 0000000..67cef15
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit4-stderr.txt
@@ -0,0 +1,13 @@
+CMake Error at VersionLimit4\.cmake:[0-9]+ \(find_package\):
+  Could not find a configuration file for package "Sample" that is compatible
+  with requested version range "1\.2\.0\.\.\.<1\.2\.3"\.
+
+  The following configuration files were considered but not accepted:
+
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.5\.0/sample\.cps, version: 1\.5\.0\+niven
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.4\.2/sample\.cps, version: 1\.4\.2\+adams
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.2\.3/sample\.cps, version: 1\.2\.3\+clarke
+    .*/Tests/RunCMake/find_package-CPS/cps/sample/1\.1\.0/sample\.cps, version: 1\.1\.0\+asimov
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package-CPS/VersionLimit4.cmake b/Tests/RunCMake/find_package-CPS/VersionLimit4.cmake
new file mode 100644
index 0000000..ade7bfb
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/VersionLimit4.cmake
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 4.0)
+
+include(Setup.cmake)
+
+set(CMAKE_FIND_PACKAGE_SORT_ORDER NAME)
+set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+###############################################################################
+# Test finding a package with multiple suitable versions when a version limit
+# is excluding a match.
+find_package(Sample 1.2.0...<1.2.3 REQUIRED)
diff --git a/Tests/RunCMake/find_package-CPS/cps/CustomVersion/37/CustomVersion.cps b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/37/CustomVersion.cps
new file mode 100644
index 0000000..805694b
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/37/CustomVersion.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "CustomVersion",
+  "version": "37",
+  "version_schema": "custom",
+  "cps_path": "@prefix@/cps/CustomVersion/37",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/CustomVersion/42/CustomVersion.cps b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/42/CustomVersion.cps
new file mode 100644
index 0000000..973900e
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/42/CustomVersion.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "CustomVersion",
+  "version": "42",
+  "version_schema": "custom",
+  "cps_path": "@prefix@/cps/CustomVersion/42",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/CustomVersion/55/CustomVersion.cps b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/55/CustomVersion.cps
new file mode 100644
index 0000000..23226ac
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/CustomVersion/55/CustomVersion.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "CustomVersion",
+  "version": "55",
+  "version_schema": "custom",
+  "cps_path": "@prefix@/cps/CustomVersion/55",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/componenttest.cps b/Tests/RunCMake/find_package-CPS/cps/componenttest.cps
index ef49af4..dbbe095 100644
--- a/Tests/RunCMake/find_package-CPS/cps/componenttest.cps
+++ b/Tests/RunCMake/find_package-CPS/cps/componenttest.cps
@@ -1,6 +1,7 @@
 {
   "cps_version": "0.13",
   "name": "ComponentTest",
+  "version": "1.0",
   "cps_path": "@prefix@/cps",
   "components": {}
 }
diff --git a/Tests/RunCMake/find_package-CPS/cps/sample/1.1.0/sample.cps b/Tests/RunCMake/find_package-CPS/cps/sample/1.1.0/sample.cps
new file mode 100644
index 0000000..5716b9b
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/sample/1.1.0/sample.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "Sample",
+  "version": "1.1.0+asimov",
+  "compat_version": "1.0.0",
+  "cps_path": "@prefix@/cps/sample/1.1.0",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/sample/1.2.3/sample.cps b/Tests/RunCMake/find_package-CPS/cps/sample/1.2.3/sample.cps
new file mode 100644
index 0000000..e748528
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/sample/1.2.3/sample.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "Sample",
+  "version": "1.2.3+clarke",
+  "compat_version": "1.0.0",
+  "cps_path": "@prefix@/cps/sample/1.2.3",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/sample/1.4.2/sample.cps b/Tests/RunCMake/find_package-CPS/cps/sample/1.4.2/sample.cps
new file mode 100644
index 0000000..8b017bc
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/sample/1.4.2/sample.cps
@@ -0,0 +1,8 @@
+{
+  "cps_version": "0.13",
+  "name": "Sample",
+  "version": "1.4.2+adams",
+  "compat_version": "1.3.0",
+  "cps_path": "@prefix@/cps/sample/1.4.2",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/sample/1.5.0/sample.cps b/Tests/RunCMake/find_package-CPS/cps/sample/1.5.0/sample.cps
new file mode 100644
index 0000000..bf9a375
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/sample/1.5.0/sample.cps
@@ -0,0 +1,7 @@
+{
+  "cps_version": "0.13",
+  "name": "Sample",
+  "version": "1.5.0+niven",
+  "cps_path": "@prefix@/cps/sample/1.5.0",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package-CPS/cps/transitiveversion.cps b/Tests/RunCMake/find_package-CPS/cps/transitiveversion.cps
new file mode 100644
index 0000000..71b2f03
--- /dev/null
+++ b/Tests/RunCMake/find_package-CPS/cps/transitiveversion.cps
@@ -0,0 +1,11 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveVersion",
+  "cps_path": "@prefix@/cps",
+  "requires": {
+    "Sample": {
+      "version": "1.2.0"
+    }
+  },
+  "components": {}
+}
diff --git a/Tests/RunCMake/list/CMP0186.cmake b/Tests/RunCMake/list/CMP0186.cmake
new file mode 100644
index 0000000..088b03e
--- /dev/null
+++ b/Tests/RunCMake/list/CMP0186.cmake
@@ -0,0 +1,43 @@
+set(mylist 0000 1001 0002)
+
+# OLD
+cmake_policy(SET CMP0186 OLD)
+
+unset(output)
+list(TRANSFORM mylist REPLACE "^0" "" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL ";1001;2")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \";1001;2\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "^(a|0)" "x" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "xxxx;1001;xxx2")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"xxxx;1001;xxx2\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(1|^)0" "x" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "xxxx;xx1;xxx2")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"xxxx;xx1;xxx2\"")
+endif()
+
+# NEW, same cases as above
+cmake_policy(SET CMP0186 NEW)
+
+unset(output)
+list(TRANSFORM mylist REPLACE "^0" "" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "000;1001;002")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"000;1001;002\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "^(a|0)" "x" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "x000;1001;x002")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"x000;1001;x002\"")
+endif()
+
+unset(output)
+list(TRANSFORM mylist REPLACE "(1|^)0" "x" OUTPUT_VARIABLE output)
+if (NOT output STREQUAL "x000;x01;x002")
+  message(FATAL_ERROR "TRANSFORM(REPLACE) is \"${output}\", expected is \"x000;xx1;x002\"")
+endif()
diff --git a/Tests/RunCMake/list/RunCMakeTest.cmake b/Tests/RunCMake/list/RunCMakeTest.cmake
index 1b67635..e428cd0 100644
--- a/Tests/RunCMake/list/RunCMakeTest.cmake
+++ b/Tests/RunCMake/list/RunCMakeTest.cmake
@@ -90,6 +90,7 @@
 run_cmake(TRANSFORM-APPEND)
 run_cmake(TRANSFORM-PREPEND)
 run_cmake(TRANSFORM-REPLACE)
+run_cmake(CMP0186)
 
 # argument tests
 run_cmake(SORT-WrongOption)
diff --git a/Tests/RunCMake/string/CMP0186.cmake b/Tests/RunCMake/string/CMP0186.cmake
new file mode 100644
index 0000000..bc9dcf8
--- /dev/null
+++ b/Tests/RunCMake/string/CMP0186.cmake
@@ -0,0 +1,90 @@
+function(check_output name expected)
+  set(output "${${name}}")
+  if(NOT output STREQUAL expected)
+    message(FATAL_ERROR "\"string(REGEX)\" set ${name} to \"${output}\", expected \"${expected}\"")
+  endif()
+endfunction()
+
+# OLD
+cmake_policy(SET CMP0186 OLD)
+
+string(REGEX MATCHALL "^0" out "0000")
+check_output(out "0;0;0;0")
+
+string(REGEX MATCHALL "^0+" out "0000")
+check_output(out "0000")
+
+string(REGEX MATCHALL "^(0|a)" out "0000" )
+check_output(out "0;0;0;0")
+
+string(REGEX MATCHALL "^(0|a)" out "aaaa")
+check_output(out "a;a;a;a")
+
+string(REGEX MATCHALL "^(0|a)" out "a0a0")
+check_output(out "a;0;a;0")
+
+string(REGEX MATCHALL "(^|a)0" out "00a0")
+check_output(out "0;0;a0")
+
+string(REGEX REPLACE "^0" "" out "0000")
+check_output(out "")
+
+string(REGEX REPLACE "^0" "x" out "0000")
+check_output(out "xxxx")
+
+string(REGEX REPLACE "^0+" "x" out "0000")
+check_output(out "x")
+
+string(REGEX REPLACE "^(0|a)" "x" out "0000")
+check_output(out "xxxx")
+
+string(REGEX REPLACE "^(0|a)" "x" out "aaaa")
+check_output(out "xxxx")
+
+string(REGEX REPLACE "^(0|a)" "x" out "a0a0")
+check_output(out "xxxx")
+
+string(REGEX REPLACE "(^|a)0" "x" out "00a0")
+check_output(out "xxx")
+
+# NEW, same cases as above
+cmake_policy(SET CMP0186 NEW)
+
+string(REGEX MATCHALL "^0" out "0000")
+check_output(out "0")
+
+string(REGEX MATCHALL "^0+" out "0000")
+check_output(out "0000")
+
+string(REGEX MATCHALL "^(0|a)" out "0000")
+check_output(out "0")
+
+string(REGEX MATCHALL "^(0|a)" out "aaaa")
+check_output(out "a")
+
+string(REGEX MATCHALL "^(0|a)" out "a0a0")
+check_output(out "a")
+
+string(REGEX MATCHALL "(^|a)0" out "00a0")
+check_output(out "0;a0")
+
+string(REGEX REPLACE "^0" "" out "0000")
+check_output(out "000")
+
+string(REGEX REPLACE "^0" "x" out "0000")
+check_output(out "x000")
+
+string(REGEX REPLACE "^0+" "x" out "0000")
+check_output(out "x")
+
+string(REGEX REPLACE "^(0|a)" "x" out "0000")
+check_output(out "x000")
+
+string(REGEX REPLACE "^(0|a)" "x" out "aaaa")
+check_output(out "xaaa")
+
+string(REGEX REPLACE "^(0|a)" "x" out "a0a0")
+check_output(out "x0a0")
+
+string(REGEX REPLACE "(^|a)0" "x" out "00a0")
+check_output(out "x0x")
diff --git a/Tests/RunCMake/string/RegexMultiMatchClear-stderr.txt b/Tests/RunCMake/string/RegexMultiMatchClear-stderr.txt
index 4360d79..9ebf49b 100644
--- a/Tests/RunCMake/string/RegexMultiMatchClear-stderr.txt
+++ b/Tests/RunCMake/string/RegexMultiMatchClear-stderr.txt
@@ -1,12 +1,12 @@
-^matches: Some::;Scope
+^matches: Some::
 results from: string\(REGEX MATCHALL\)
-CMAKE_MATCH_0: -->Scope<--
-CMAKE_MATCH_1: -->Scope<--
-CMAKE_MATCH_2: --><--
-CMAKE_MATCH_COUNT: -->1<--
-replace: \[Some\]\[Scope\]
+CMAKE_MATCH_0: -->Some::<--
+CMAKE_MATCH_1: -->Some<--
+CMAKE_MATCH_2: -->::<--
+CMAKE_MATCH_COUNT: -->2<--
+replace: \[Some\]Scope
 results from: string\(REGEX REPLACE\)
-CMAKE_MATCH_0: -->Scope<--
-CMAKE_MATCH_1: -->Scope<--
-CMAKE_MATCH_2: --><--
-CMAKE_MATCH_COUNT: -->1<--$
+CMAKE_MATCH_0: -->Some::<--
+CMAKE_MATCH_1: -->Some<--
+CMAKE_MATCH_2: -->::<--
+CMAKE_MATCH_COUNT: -->2<--$
diff --git a/Tests/RunCMake/string/RegexMultiMatchClear.cmake b/Tests/RunCMake/string/RegexMultiMatchClear.cmake
index 788ba5e..99d4646 100644
--- a/Tests/RunCMake/string/RegexMultiMatchClear.cmake
+++ b/Tests/RunCMake/string/RegexMultiMatchClear.cmake
@@ -1,3 +1,5 @@
+cmake_policy(SET CMP0186 NEW)
+
 function (output_results msg)
   message("results from: ${msg}")
   message("CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--")
diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake
index ff0bb51..e352fcb 100644
--- a/Tests/RunCMake/string/RunCMakeTest.cmake
+++ b/Tests/RunCMake/string/RunCMakeTest.cmake
@@ -35,6 +35,7 @@
 
 run_cmake(RegexClear)
 run_cmake(RegexMultiMatchClear)
+run_cmake(CMP0186)
 
 run_cmake(UTF-16BE)
 run_cmake(UTF-16LE)
diff --git a/Utilities/Release/linux/aarch64/cache.txt b/Utilities/Release/linux/aarch64/cache.txt
index 9b13440..3cc9712 100644
--- a/Utilities/Release/linux/aarch64/cache.txt
+++ b/Utilities/Release/linux/aarch64/cache.txt
@@ -8,7 +8,7 @@
 
 # Enable ssl support in curl
 CMAKE_USE_OPENSSL:BOOL=ON
-OPENSSL_CRYPTO_LIBRARY:STRING=/opt/openssl/lib/libcrypto.a;-pthread
+OPENSSL_CRYPTO_LIBRARY:STRING=/opt/openssl/lib/libcrypto.a
 OPENSSL_INCLUDE_DIR:PATH=/opt/openssl/include
 OPENSSL_SSL_LIBRARY:FILEPATH=/opt/openssl/lib/libssl.a
 
diff --git a/Utilities/Release/linux/x86_64/cache.txt b/Utilities/Release/linux/x86_64/cache.txt
index a40f8fb..46dc81a 100644
--- a/Utilities/Release/linux/x86_64/cache.txt
+++ b/Utilities/Release/linux/x86_64/cache.txt
@@ -8,7 +8,7 @@
 
 # Enable ssl support in curl
 CMAKE_USE_OPENSSL:BOOL=ON
-OPENSSL_CRYPTO_LIBRARY:STRING=/opt/openssl/lib/libcrypto.a;-pthread
+OPENSSL_CRYPTO_LIBRARY:STRING=/opt/openssl/lib/libcrypto.a
 OPENSSL_INCLUDE_DIR:PATH=/opt/openssl/include
 OPENSSL_SSL_LIBRARY:FILEPATH=/opt/openssl/lib/libssl.a
 
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index 82be5a3..a1c82d3 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_12_0"
+readonly tag="curl-8_12_1"
 readonly shortlog=false
 readonly paths="
   CMake/*
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 1428238..c5a5257 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -70,7 +70,7 @@
 #include <netdb.h>
 int main(void)
 {
-  char *address = "example.com";
+  const char *address = "example.com";
   int length = 0;
   int type = 0;
   struct hostent h;
@@ -167,7 +167,7 @@
 int main(void)
 {
   /* ioctlsocket source code */
-  int socket;
+  int socket = -1;
   unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
   ;
   return 0;
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake
index 8a692bd..b72f190 100644
--- a/Utilities/cmcurl/CMake/FindBrotli.cmake
+++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -51,6 +51,7 @@
 endif()
 
 if(BROTLI_FOUND AND BROTLIDEC_FOUND)
+  set(Brotli_FOUND TRUE)
   list(APPEND BROTLIDEC_LIBRARIES ${BROTLI_LIBRARIES})  # order is significant: brotlidec then brotlicommon
   list(REVERSE BROTLIDEC_LIBRARIES)
   list(REMOVE_DUPLICATES BROTLIDEC_LIBRARIES)
diff --git a/Utilities/cmcurl/CMake/FindCares.cmake b/Utilities/cmcurl/CMake/FindCares.cmake
index 67dec09..cc47e2d 100644
--- a/Utilities/cmcurl/CMake/FindCares.cmake
+++ b/Utilities/cmcurl/CMake/FindCares.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(CARES_FOUND)
+  set(Cares_FOUND TRUE)
   string(REPLACE ";" " " CARES_CFLAGS "${CARES_CFLAGS}")
   message(STATUS "Found Cares (via pkg-config): ${CARES_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibgsasl.cmake b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
index c0ce673..c726ce1 100644
--- a/Utilities/cmcurl/CMake/FindLibgsasl.cmake
+++ b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBGSASL_FOUND)
+  set(Libgsasl_FOUND TRUE)
   string(REPLACE ";" " " LIBGSASL_CFLAGS "${LIBGSASL_CFLAGS}")
   message(STATUS "Found Libgsasl (via pkg-config): ${LIBGSASL_INCLUDE_DIRS} (found version \"${LIBGSASL_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibidn2.cmake b/Utilities/cmcurl/CMake/FindLibidn2.cmake
index a8887e8..f8f00f0 100644
--- a/Utilities/cmcurl/CMake/FindLibidn2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibidn2.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBIDN2_FOUND)
+  set(Libidn2_FOUND TRUE)
   string(REPLACE ";" " " LIBIDN2_CFLAGS "${LIBIDN2_CFLAGS}")
   message(STATUS "Found Libidn2 (via pkg-config): ${LIBIDN2_INCLUDE_DIRS} (found version \"${LIBIDN2_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibpsl.cmake b/Utilities/cmcurl/CMake/FindLibpsl.cmake
index 0d96995..d6fde4b 100644
--- a/Utilities/cmcurl/CMake/FindLibpsl.cmake
+++ b/Utilities/cmcurl/CMake/FindLibpsl.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBPSL_FOUND AND LIBPSL_INCLUDE_DIRS)
+  set(Libpsl_FOUND TRUE)
   string(REPLACE ";" " " LIBPSL_CFLAGS "${LIBPSL_CFLAGS}")
   message(STATUS "Found Libpsl (via pkg-config): ${LIBPSL_INCLUDE_DIRS} (found version \"${LIBPSL_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibrtmp.cmake b/Utilities/cmcurl/CMake/FindLibrtmp.cmake
index dd3fb9e..50fc969 100644
--- a/Utilities/cmcurl/CMake/FindLibrtmp.cmake
+++ b/Utilities/cmcurl/CMake/FindLibrtmp.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBRTMP_FOUND AND LIBRTMP_INCLUDE_DIRS)
+  set(Librtmp_FOUND TRUE)
   string(REPLACE ";" " " LIBRTMP_CFLAGS "${LIBRTMP_CFLAGS}")
   message(STATUS "Found Librtmp (via pkg-config): ${LIBRTMP_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibssh.cmake b/Utilities/cmcurl/CMake/FindLibssh.cmake
index b8b4a51..e2b27d9 100644
--- a/Utilities/cmcurl/CMake/FindLibssh.cmake
+++ b/Utilities/cmcurl/CMake/FindLibssh.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBSSH_FOUND)
+  set(Libssh_FOUND TRUE)
   string(REPLACE ";" " " LIBSSH_CFLAGS "${LIBSSH_CFLAGS}")
   message(STATUS "Found Libssh (via pkg-config): ${LIBSSH_INCLUDE_DIRS} (found version \"${LIBSSH_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibssh2.cmake b/Utilities/cmcurl/CMake/FindLibssh2.cmake
index dfb0582..0b81ecb 100644
--- a/Utilities/cmcurl/CMake/FindLibssh2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibssh2.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBSSH2_FOUND AND LIBSSH2_INCLUDE_DIRS)
+  set(Libssh2_FOUND TRUE)
   string(REPLACE ";" " " LIBSSH2_CFLAGS "${LIBSSH2_CFLAGS}")
   message(STATUS "Found Libssh2 (via pkg-config): ${LIBSSH2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindLibuv.cmake b/Utilities/cmcurl/CMake/FindLibuv.cmake
index 8255eaa..b16b355 100644
--- a/Utilities/cmcurl/CMake/FindLibuv.cmake
+++ b/Utilities/cmcurl/CMake/FindLibuv.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(LIBUV_FOUND)
+  set(Libuv_FOUND TRUE)
   string(REPLACE ";" " " LIBUV_CFLAGS "${LIBUV_CFLAGS}")
   message(STATUS "Found Libuv (via pkg-config): ${LIBUV_INCLUDE_DIRS} (found version \"${LIBUV_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
index c3ba27d..83f1371 100644
--- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake
+++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -60,6 +60,7 @@
 endif()
 
 if(MBEDTLS_FOUND AND MBEDX509_FOUND AND MBEDCRYPTO_FOUND)
+  set(MbedTLS_FOUND TRUE)
   list(APPEND MBEDTLS_LIBRARIES ${MBEDX509_LIBRARIES} ${MBEDCRYPTO_LIBRARIES})
   list(REVERSE MBEDTLS_LIBRARIES)
   list(REMOVE_DUPLICATES MBEDTLS_LIBRARIES)
diff --git a/Utilities/cmcurl/CMake/FindNettle.cmake b/Utilities/cmcurl/CMake/FindNettle.cmake
index 753633a..c2decf6 100644
--- a/Utilities/cmcurl/CMake/FindNettle.cmake
+++ b/Utilities/cmcurl/CMake/FindNettle.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(NETTLE_FOUND)
+  set(Nettle_FOUND TRUE)
   string(REPLACE ";" " " NETTLE_CFLAGS "${NETTLE_CFLAGS}")
   message(STATUS "Found Nettle (via pkg-config): ${NETTLE_INCLUDE_DIRS} (found version \"${NETTLE_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindQuiche.cmake b/Utilities/cmcurl/CMake/FindQuiche.cmake
index 092aff8..6939c64 100644
--- a/Utilities/cmcurl/CMake/FindQuiche.cmake
+++ b/Utilities/cmcurl/CMake/FindQuiche.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(QUICHE_FOUND)
+  set(Quiche_FOUND TRUE)
   string(REPLACE ";" " " QUICHE_CFLAGS "${QUICHE_CFLAGS}")
   message(STATUS "Found Quiche (via pkg-config): ${QUICHE_INCLUDE_DIRS} (found version \"${QUICHE_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindRustls.cmake b/Utilities/cmcurl/CMake/FindRustls.cmake
index 51f4506..564b08c 100644
--- a/Utilities/cmcurl/CMake/FindRustls.cmake
+++ b/Utilities/cmcurl/CMake/FindRustls.cmake
@@ -48,6 +48,7 @@
 endif()
 
 if(RUSTLS_FOUND)
+  set(Rustls_FOUND TRUE)
   string(REPLACE ";" " " RUSTLS_CFLAGS "${RUSTLS_CFLAGS}")
   message(STATUS "Found Rustls (via pkg-config): ${RUSTLS_INCLUDE_DIRS} (found version \"${RUSTLS_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
index 3163fee..e33fef8 100644
--- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -57,6 +57,7 @@
 endif()
 
 if(WOLFSSL_FOUND)
+  set(WolfSSL_FOUND TRUE)
   string(REPLACE ";" " " WOLFSSL_CFLAGS "${WOLFSSL_CFLAGS}")
   message(STATUS "Found WolfSSL (via pkg-config): ${WOLFSSL_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake
index dbfede7..0ea1fef 100644
--- a/Utilities/cmcurl/CMake/FindZstd.cmake
+++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -57,6 +57,7 @@
 endif()
 
 if(ZSTD_FOUND)
+  set(Zstd_FOUND TRUE)
   string(REPLACE ";" " " ZSTD_CFLAGS "${ZSTD_CFLAGS}")
   message(STATUS "Found Zstd (via pkg-config): ${ZSTD_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")")
 else()
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake
index ad0df18..8653f36 100644
--- a/Utilities/cmcurl/CMake/Macros.cmake
+++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -34,11 +34,14 @@
   endif()
 endmacro()
 
+set(CURL_TEST_DEFINES "")  # Initialize global variable
+
 # For other curl specific tests, use this macro.
 # 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}")
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index 0188538..8a7faaf 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -65,6 +65,9 @@
 set(_source_epilogue "#undef inline")
 curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h")
 check_c_source_compiles("${_source_epilogue}
+  #ifdef _MSC_VER
+  #include <winsock2.h>
+  #endif
   #include <time.h>
   int main(void)
   {
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake
index 23341c4..15f771b 100644
--- a/Utilities/cmcurl/CMake/PickyWarnings.cmake
+++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -274,6 +274,6 @@
 
 if(_picky)
   string(REPLACE ";" " " _picky "${_picky}")
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_picky}")
+  string(APPEND CMAKE_C_FLAGS " ${_picky}")
   message(STATUS "Picky compiler options: ${_picky}")
 endif()
diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake
index 869d591..ff2173f 100644
--- a/Utilities/cmcurl/CMake/Utilities.cmake
+++ b/Utilities/cmcurl/CMake/Utilities.cmake
@@ -47,7 +47,7 @@
     if(_var_advanced)
       set(_var_advanced " [adv]")
     endif()
-    message("${_var}${_var_type}${_var_advanced} = ${${_var}}")
+    message("${_var}${_var_type}${_var_advanced} = '${${_var}}'")
   endforeach()
   message("::endgroup::")
 endfunction()
diff --git a/Utilities/cmcurl/CMake/win32-cache.cmake b/Utilities/cmcurl/CMake/win32-cache.cmake
index 9d46736..272f513 100644
--- a/Utilities/cmcurl/CMake/win32-cache.cmake
+++ b/Utilities/cmcurl/CMake/win32-cache.cmake
@@ -74,12 +74,12 @@
     set(HAVE_UNISTD_H 0)
     set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
     set(HAVE_STDATOMIC_H 0)
-    if(NOT MSVC_VERSION LESS 1600)
+    if(MSVC_VERSION GREATER_EQUAL 1600)
       set(HAVE_STDINT_H 1)  # detected by CMake internally in check_type_size()
     else()
       set(HAVE_STDINT_H 0)  # detected by CMake internally in check_type_size()
     endif()
-    if(NOT MSVC_VERSION LESS 1800)
+    if(MSVC_VERSION GREATER_EQUAL 1800)
       set(HAVE_STDBOOL_H 1)
       set(HAVE_STRTOLL 1)
     else()
@@ -87,7 +87,7 @@
       set(HAVE_STRTOLL 0)
     endif()
     set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
-    if(NOT MSVC_VERSION LESS 1900)
+    if(MSVC_VERSION GREATER_EQUAL 1900)
       set(HAVE_SNPRINTF 1)
     else()
       set(HAVE_SNPRINTF 0)
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 4d4166a..75b5102 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -1,4 +1,5 @@
 # Set curl options as needed for CMake build
+set(_CURL_QUICK_DETECT ON)
 set(BUILD_CURL_EXE OFF CACHE INTERNAL "No curl exe")
 set(BUILD_DASHBOARD_REPORTS OFF CACHE INTERNAL "No curl dashboard reports")
 set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs")
@@ -235,13 +236,13 @@
       else()
         set(_cache_var_type ":${_cache_var_type}")
       endif()
-      set(_cmake_args "${_cmake_args} -D${_cache_var}${_cache_var_type}=\"${_cache_var_value}\"")
+      string(APPEND _cmake_args " -D${_cache_var}${_cache_var_type}=\"${_cache_var_value}\"")
     endif()
   endforeach()
 endif()
 endif() # XXX(cmake): end
 
-set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
+set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
 include(Utilities)
 include(Macros)
 include(CMakeDependentOption)
@@ -289,49 +290,49 @@
 
 set(_target_flags "")
 if(APPLE)
-  set(_target_flags "${_target_flags} APPLE")
+  string(APPEND _target_flags " APPLE")
 endif()
 if(UNIX)
-  set(_target_flags "${_target_flags} UNIX")
+  string(APPEND _target_flags " UNIX")
 endif()
 if(BSD)
-  set(_target_flags "${_target_flags} BSD")
+  string(APPEND _target_flags " BSD")
 endif()
 if(ANDROID)
-  set(_target_flags "${_target_flags} ANDROID-${ANDROID_PLATFORM_LEVEL}")
+  string(APPEND _target_flags " ANDROID-${ANDROID_PLATFORM_LEVEL}")
 endif()
 if(WIN32)
-  set(_target_flags "${_target_flags} WIN32")
+  string(APPEND _target_flags " WIN32")
 endif()
 if(WINDOWS_STORE)
-  set(_target_flags "${_target_flags} UWP")
+  string(APPEND _target_flags " UWP")
 endif()
 if(CYGWIN)
-  set(_target_flags "${_target_flags} CYGWIN")
+  string(APPEND _target_flags " CYGWIN")
 endif()
 if(MSYS)
-  set(_target_flags "${_target_flags} MSYS")
+  string(APPEND _target_flags " MSYS")
 endif()
 if(DOS)
-  set(_target_flags "${_target_flags} DOS")
+  string(APPEND _target_flags " DOS")
 endif()
 if(AMIGA)
-  set(_target_flags "${_target_flags} AMIGA")
+  string(APPEND _target_flags " AMIGA")
 endif()
 if(CMAKE_COMPILER_IS_GNUCC)
-  set(_target_flags "${_target_flags} GCC")
+  string(APPEND _target_flags " GCC")
 endif()
 if(MINGW)
-  set(_target_flags "${_target_flags} MINGW")
+  string(APPEND _target_flags " MINGW")
 endif()
 if(MSVC)
-  set(_target_flags "${_target_flags} MSVC-${MSVC_VERSION}")
+  string(APPEND _target_flags " MSVC-${MSVC_VERSION}")
 endif()
 if(VCPKG_TOOLCHAIN)
-  set(_target_flags "${_target_flags} VCPKG")
+  string(APPEND _target_flags " VCPKG")
 endif()
 if(CMAKE_CROSSCOMPILING)
-  set(_target_flags "${_target_flags} CROSS")
+  string(APPEND _target_flags " CROSS")
 endif()
 if(0) # XXX(cmake): not needed for build within cmake
 message(STATUS "CMake platform flags:${_target_flags}")
@@ -379,8 +380,8 @@
   option(CURL_STATIC_CRT "Build libcurl with static CRT with MSVC (/MT)" OFF)
   if(CURL_STATIC_CRT AND MSVC)
     set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
-    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -MT")
-    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -MTd")
+    string(APPEND CMAKE_C_FLAGS_RELEASE " -MT")
+    string(APPEND CMAKE_C_FLAGS_DEBUG " -MTd")
   endif()
 
   option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF)
@@ -395,7 +396,11 @@
   endif()
 
   if(0) # XXX(cmake): not needed for build within cmake
-  list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN")  # Apply to all feature checks
+  # Apply to all feature checks
+  list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN")
+  if(MSVC)
+    list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_CRT_NONSTDC_NO_DEPRECATE")  # for strdup() detection
+  endif()
 
   set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string")
   if(CURL_TARGET_WINDOWS_VERSION)
@@ -446,7 +451,7 @@
 endif() # XXX(cmake): end
 
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")  # Required for sendmmsg()
+  string(APPEND CMAKE_C_FLAGS " -D_GNU_SOURCE")  # Required for sendmmsg()
 endif()
 
 option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OFF)
@@ -455,10 +460,6 @@
 endif()
 option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG})
 
-if(MSVC)
-  set(ENABLE_CURLDEBUG OFF)  # FIXME: TrackMemory + MSVC fails test 558 and 1330. Tested with static build, Debug mode.
-endif()
-
 if(ENABLE_DEBUG)
   set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "DEBUGBUILD")
 endif()
@@ -542,7 +543,7 @@
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES})
   link_directories(${CARES_LIBRARY_DIRS})
   if(CARES_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CARES_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${CARES_CFLAGS}")
   endif()
 endif()
 
@@ -679,35 +680,6 @@
   set(CURL_DISABLE_TELNET ON)  # telnet code needs fixing to compile for UWP.
 endif()
 
-option(ENABLE_IPV6 "Enable IPv6 support" ON)
-mark_as_advanced(ENABLE_IPV6)
-if(ENABLE_IPV6 AND NOT WIN32)
-  include(CheckStructHasMember)
-  check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR)
-  check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
-  if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR)
-    if(NOT DOS AND NOT AMIGA)
-      message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support")
-    endif()
-    # Force the feature off as this name is used as guard macro...
-    set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE)
-  endif()
-
-  if(APPLE AND NOT ENABLE_ARES)
-    set(_use_core_foundation_and_core_services ON)
-
-    find_library(SYSTEMCONFIGURATION_FRAMEWORK NAMES "SystemConfiguration")
-    mark_as_advanced(SYSTEMCONFIGURATION_FRAMEWORK)
-    if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
-      message(FATAL_ERROR "SystemConfiguration framework not found")
-    endif()
-    list(APPEND CURL_LIBS "-framework SystemConfiguration")
-  endif()
-endif()
-if(ENABLE_IPV6)
-  set(USE_IPV6 ON)
-endif()
-
 if(0) # XXX(cmake): not needed for build within cmake
 find_package(Perl)
 
@@ -740,7 +712,7 @@
 
 # Disable warnings on Borland to avoid changing 3rd party code.
 if(BORLAND)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w-")
+  string(APPEND CMAKE_C_FLAGS " -w-")
 endif()
 
 # If we are on AIX, do the _ALL_SOURCE magic
@@ -766,15 +738,18 @@
 include(CheckTypeSize)
 include(CheckCSourceCompiles)
 
-if(WIN32)
-  # Preload settings on Windows
-  include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake")
-elseif(APPLE)
-  # Fast-track predictable feature detections
-  set(HAVE_EVENTFD 0)
-  set(HAVE_GETPASS_R 0)
-  set(HAVE_SENDMMSG 0)
-elseif(AMIGA)
+option(_CURL_QUICK_DETECT "Fast-track known feature detection results (Windows, some Apple)" ON)
+if(_CURL_QUICK_DETECT)
+  if(WIN32)
+    include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake")
+  elseif(APPLE)
+    set(HAVE_EVENTFD 0)
+    set(HAVE_GETPASS_R 0)
+    set(HAVE_SENDMMSG 0)
+  endif()
+endif()
+
+if(AMIGA)
   set(HAVE_GETADDRINFO 0)  # Breaks the build when detected and used.
 endif()
 if(DOS OR AMIGA)
@@ -817,12 +792,40 @@
 elseif(NOT WIN32 AND NOT APPLE)
   check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
   if(HAVE_LIBSOCKET)
-    set(CURL_LIBS "socket;${CURL_LIBS}")
+    set(CURL_LIBS "socket" ${CURL_LIBS})
   endif()
 endif()
 
-if(WIN32)
-  list(APPEND CURL_LIBS "ws2_32" "bcrypt")
+option(ENABLE_IPV6 "Enable IPv6 support" ON)
+mark_as_advanced(ENABLE_IPV6)
+if(ENABLE_IPV6)
+  include(CheckStructHasMember)
+  if(WIN32)
+    check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "winsock2.h;ws2tcpip.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+  else()
+    check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR)
+    check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+    if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR)
+      if(NOT DOS AND NOT AMIGA)
+        message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support")
+      endif()
+      set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE)  # Force the feature off as we use this name as guard macro
+    endif()
+
+    if(APPLE AND NOT ENABLE_ARES)
+      set(_use_core_foundation_and_core_services ON)
+
+      find_library(SYSTEMCONFIGURATION_FRAMEWORK NAMES "SystemConfiguration")
+      mark_as_advanced(SYSTEMCONFIGURATION_FRAMEWORK)
+      if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
+        message(FATAL_ERROR "SystemConfiguration framework not found")
+      endif()
+      list(APPEND CURL_LIBS "-framework SystemConfiguration")
+    endif()
+  endif()
+endif()
+if(ENABLE_IPV6)
+  set(USE_IPV6 ON)
 endif()
 
 if(0) # XXX(cmake): not needed for build within cmake
@@ -944,8 +947,9 @@
   set(_ssl_enabled ON)
   set(USE_OPENSSL ON)
 
-  list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES})
-  include_directories(${OPENSSL_INCLUDE_DIR})
+  # Depend on OpenSSL via imported targets. This allows our dependents to
+  # get our dependencies transitively.
+  list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto)
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "openssl")
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl")
@@ -954,7 +958,7 @@
   set(_curl_ca_bundle_supported TRUE)
 
   cmake_push_check_state()
-  list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
+  list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
   if(NOT DEFINED HAVE_BORINGSSL)
     check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL)
   endif()
@@ -1010,7 +1014,7 @@
   include_directories(SYSTEM ${MBEDTLS_INCLUDE_DIRS})
   link_directories(${MBEDTLS_LIBRARY_DIRS})
   if(MBEDTLS_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MBEDTLS_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${MBEDTLS_CFLAGS}")
   endif()
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls")
@@ -1046,7 +1050,7 @@
   include_directories(SYSTEM ${WOLFSSL_INCLUDE_DIRS})
   link_directories(${WOLFSSL_LIBRARY_DIRS})
   if(WOLFSSL_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WOLFSSL_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${WOLFSSL_CFLAGS}")
   endif()
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl")
@@ -1061,6 +1065,10 @@
     pkg_check_modules(GNUTLS "gnutls")
     if(GNUTLS_FOUND)
       set(GNUTLS_LIBRARIES ${GNUTLS_LINK_LIBRARIES})
+      string(REPLACE ";" " " GNUTLS_CFLAGS "${GNUTLS_CFLAGS}")
+      if(GNUTLS_CFLAGS)
+        string(APPEND CMAKE_C_FLAGS " ${GNUTLS_CFLAGS}")
+      endif()
     endif()
   endif()
   if(NOT GNUTLS_FOUND)
@@ -1070,12 +1078,12 @@
   set(_ssl_enabled ON)
   set(USE_GNUTLS ON)
   list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES})
-  list(APPEND CURL_LIBDIRS ${NETTLE_LIBRARY_DIRS})
+  list(APPEND CURL_LIBDIRS ${GNUTLS_LIBRARY_DIRS} ${NETTLE_LIBRARY_DIRS})
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" ${NETTLE_PC_REQUIRES})
   include_directories(SYSTEM ${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS})
   link_directories(${NETTLE_LIBRARY_DIRS})
   if(NETTLE_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NETTLE_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${NETTLE_CFLAGS}")
   endif()
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls")
@@ -1102,7 +1110,7 @@
   include_directories(SYSTEM ${RUSTLS_INCLUDE_DIRS})
   link_directories(${RUSTLS_LIBRARY_DIRS})
   if(RUSTLS_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${RUSTLS_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${RUSTLS_CFLAGS}")
   endif()
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls")
@@ -1142,7 +1150,7 @@
   include_directories(SYSTEM ${BROTLI_INCLUDE_DIRS})
   link_directories(${BROTLI_LIBRARY_DIRS})
   if(BROTLI_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${BROTLI_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${BROTLI_CFLAGS}")
   endif()
 endif()
 
@@ -1157,7 +1165,7 @@
     include_directories(SYSTEM ${ZSTD_INCLUDE_DIRS})
     link_directories(${ZSTD_LIBRARY_DIRS})
     if(ZSTD_CFLAGS)
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZSTD_CFLAGS}")
+      string(APPEND CMAKE_C_FLAGS " ${ZSTD_CFLAGS}")
     endif()
   else()
     message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.")
@@ -1168,10 +1176,14 @@
 macro(curl_openssl_check_symbol_exists _symbol _files _variable)
   cmake_push_check_state()
   if(USE_OPENSSL)
-    list(APPEND CMAKE_REQUIRED_INCLUDES   "${OPENSSL_INCLUDE_DIR}")
-    list(APPEND CMAKE_REQUIRED_LIBRARIES  "${OPENSSL_LIBRARIES}")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
+    list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DOPENSSL_SUPPRESS_DEPRECATED")  # for SSL_CTX_set_srp_username deprecated since 3.0.0
     if(HAVE_LIBZ)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
+      if(CMAKE_USE_SYSTEM_ZLIB)
+        list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB)
+      else()
+        list(APPEND CMAKE_REQUIRED_LIBRARIES cmzlib)
+      endif()
     endif()
     if(WIN32)
       list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
@@ -1183,8 +1195,7 @@
     list(APPEND CMAKE_REQUIRED_LIBRARIES  "${WOLFSSL_LIBRARIES}")
     curl_required_libpaths("${WOLFSSL_LIBRARY_DIRS}")
     if(HAVE_LIBZ)
-      list(APPEND CMAKE_REQUIRED_INCLUDES  "${ZLIB_INCLUDE_DIRS}")  # Public wolfSSL headers require zlib headers
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
+      list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB)  # Public wolfSSL headers also require zlib headers
     endif()
     if(WIN32)
       list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32")
@@ -1276,7 +1287,7 @@
     include_directories(SYSTEM ${NGHTTP2_INCLUDE_DIRS})
     link_directories(${NGHTTP2_LIBRARY_DIRS})
     if(NGHTTP2_CFLAGS)
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGHTTP2_CFLAGS}")
+      string(APPEND CMAKE_C_FLAGS " ${NGHTTP2_CFLAGS}")
     endif()
   else()
     set(USE_NGHTTP2 OFF)
@@ -1308,7 +1319,7 @@
   include_directories(SYSTEM ${NGTCP2_INCLUDE_DIRS})
   link_directories(${NGTCP2_LIBRARY_DIRS})
   if(NGTCP2_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGTCP2_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${NGTCP2_CFLAGS}")
   endif()
 
   find_package(NGHTTP3 REQUIRED)
@@ -1319,7 +1330,7 @@
   include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS})
   link_directories(${NGHTTP3_LIBRARY_DIRS})
   if(NGHTTP3_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NGHTTP3_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${NGHTTP3_CFLAGS}")
   endif()
 endif()
 
@@ -1339,7 +1350,7 @@
   include_directories(SYSTEM ${QUICHE_INCLUDE_DIRS})
   link_directories(${QUICHE_LIBRARY_DIRS})
   if(QUICHE_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${QUICHE_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${QUICHE_CFLAGS}")
   endif()
   if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD)
     cmake_push_check_state()
@@ -1368,7 +1379,7 @@
   include_directories(SYSTEM ${MSH3_INCLUDE_DIRS})
   link_directories(${MSH3_LIBRARY_DIRS})
   if(MSH3_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MSH3_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${MSH3_CFLAGS}")
   endif()
 endif()
 
@@ -1409,20 +1420,20 @@
     # Check for LDAP
     cmake_push_check_state()
     if(USE_OPENSSL)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
+      list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
     endif()
     find_package(LDAP)
     if(LDAP_FOUND)
       set(HAVE_LBER_H 1)
-      set(CURL_LIBS "${LDAP_LIBRARIES};${CURL_LIBS}")
+      set(CURL_LIBS ${LDAP_LIBRARIES} ${CURL_LIBS})
       list(APPEND CURL_LIBDIRS ${LDAP_LIBRARY_DIRS})
       if(LDAP_PC_REQUIRES)
-        set(LIBCURL_PC_REQUIRES_PRIVATE "${LDAP_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}")
+        set(LIBCURL_PC_REQUIRES_PRIVATE ${LDAP_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE})
       endif()
       include_directories(SYSTEM ${LDAP_INCLUDE_DIRS})
       link_directories(${LDAP_LIBRARY_DIRS})
       if(LDAP_CFLAGS)
-        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LDAP_CFLAGS}")
+        string(APPEND CMAKE_C_FLAGS " ${LDAP_CFLAGS}")
       endif()
 
       # LDAP feature checks
@@ -1438,7 +1449,6 @@
 
       if(HAVE_LDAP_INIT_FD)
         set(USE_OPENLDAP ON)
-        add_definitions("-DLDAP_DEPRECATED=1")
       endif()
       if(NOT CURL_DISABLE_LDAPS)
         set(HAVE_LDAP_SSL ON)
@@ -1492,13 +1502,13 @@
 if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN)
   find_package(Libidn2 QUIET)
   if(LIBIDN2_FOUND)
-    set(CURL_LIBS "${LIBIDN2_LIBRARIES};${CURL_LIBS}")
+    set(CURL_LIBS ${LIBIDN2_LIBRARIES} ${CURL_LIBS})
     list(APPEND CURL_LIBDIRS ${LIBIDN2_LIBRARY_DIRS})
-    set(LIBCURL_PC_REQUIRES_PRIVATE "${LIBIDN2_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}")
+    set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBIDN2_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE})
     include_directories(SYSTEM ${LIBIDN2_INCLUDE_DIRS})
     link_directories(${LIBIDN2_LIBRARY_DIRS})
     if(LIBIDN2_CFLAGS)
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBIDN2_CFLAGS}")
+      string(APPEND CMAKE_C_FLAGS " ${LIBIDN2_CFLAGS}")
     endif()
     set(HAVE_IDN2_H 1)
     set(HAVE_LIBIDN2 1)
@@ -1518,7 +1528,7 @@
   include_directories(SYSTEM ${LIBPSL_INCLUDE_DIRS})
   link_directories(${LIBPSL_LIBRARY_DIRS})
   if(LIBPSL_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBPSL_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${LIBPSL_CFLAGS}")
   endif()
   set(USE_LIBPSL ON)
 endif()
@@ -1531,13 +1541,13 @@
 if(CURL_USE_LIBSSH2)
   find_package(Libssh2)
   if(LIBSSH2_FOUND)
-    list(APPEND CURL_LIBS ${LIBSSH2_LIBRARIES})
+    set(CURL_LIBS ${LIBSSH2_LIBRARIES} ${CURL_LIBS})  # keep it before TLS-crypto, compression
     list(APPEND CURL_LIBDIRS ${LIBSSH2_LIBRARY_DIRS})
-    list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH2_PC_REQUIRES})
+    set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH2_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE})
     include_directories(SYSTEM ${LIBSSH2_INCLUDE_DIRS})
     link_directories(${LIBSSH2_LIBRARY_DIRS})
     if(LIBSSH2_CFLAGS)
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH2_CFLAGS}")
+      string(APPEND CMAKE_C_FLAGS " ${LIBSSH2_CFLAGS}")
     endif()
     set(USE_LIBSSH2 ON)
   endif()
@@ -1548,13 +1558,13 @@
 mark_as_advanced(CURL_USE_LIBSSH)
 if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH)
   find_package(Libssh REQUIRED)
-  list(APPEND CURL_LIBS ${LIBSSH_LIBRARIES})
+  set(CURL_LIBS ${LIBSSH_LIBRARIES} ${CURL_LIBS})  # keep it before TLS-crypto, compression
   list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS})
-  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH_PC_REQUIRES})
+  set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE})
   include_directories(SYSTEM ${LIBSSH_INCLUDE_DIRS})
   link_directories(${LIBSSH_LIBRARY_DIRS})
   if(LIBSSH_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBSSH_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${LIBSSH_CFLAGS}")
   endif()
   set(USE_LIBSSH ON)
 endif()
@@ -1567,7 +1577,7 @@
   if(USE_WOLFSSL)
     find_package(WolfSSH)
     if(WOLFSSH_FOUND)
-      list(APPEND CURL_LIBS ${WOLFSSH_LIBRARIES})
+      set(CURL_LIBS ${WOLFSSH_LIBRARIES} ${CURL_LIBS})  # keep it before TLS-crypto, compression
       include_directories(SYSTEM ${WOLFSSH_INCLUDE_DIRS})
       set(USE_WOLFSSH ON)
     endif()
@@ -1586,7 +1596,7 @@
   include_directories(SYSTEM ${LIBGSASL_INCLUDE_DIRS})
   link_directories(${LIBGSASL_LIBRARY_DIRS})
   if(LIBGSASL_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBGSASL_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${LIBGSASL_CFLAGS}")
   endif()
   set(USE_GSASL ON)
 endif()
@@ -1605,7 +1615,7 @@
     include_directories(SYSTEM ${GSS_INCLUDE_DIRS})
     link_directories(${GSS_LIBRARY_DIRS})
     if(GSS_CFLAGS)
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}")
+      string(APPEND CMAKE_C_FLAGS " ${GSS_CFLAGS}")
     endif()
 
     if(GSS_FLAVOUR STREQUAL "GNU")
@@ -1631,7 +1641,7 @@
         endif()
 
         if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE)
-          set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${GSS_CFLAGS}")
+          string(APPEND CMAKE_REQUIRED_FLAGS " ${GSS_CFLAGS}")
           list(APPEND CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES})
           curl_required_libpaths("${GSS_LIBRARY_DIRS}")
           check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" "${_include_list}" HAVE_GSS_C_NT_HOSTBASED_SERVICE)
@@ -1661,7 +1671,7 @@
   include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS})
   link_directories(${LIBUV_LIBRARY_DIRS})
   if(LIBUV_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBUV_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${LIBUV_CFLAGS}")
   endif()
   set(USE_LIBUV ON)
   set(HAVE_UV_H ON)
@@ -1676,7 +1686,7 @@
   include_directories(SYSTEM ${LIBRTMP_INCLUDE_DIRS})
   link_directories(${LIBRTMP_LIBRARY_DIRS})
   if(LIBRTMP_CFLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBRTMP_CFLAGS}")
+    string(APPEND CMAKE_C_FLAGS " ${LIBRTMP_CFLAGS}")
   endif()
 endif()
 
@@ -1875,7 +1885,7 @@
     HAVE_UNISTD_H
     )
   if(${_variable})
-    set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${_variable}")
+    string(APPEND CURL_TEST_DEFINES " -D${_variable}")
   endif()
 endforeach()
 
@@ -1946,14 +1956,15 @@
 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("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # winsock2.h net/if.h
 check_function_exists("getrlimit"       HAVE_GETRLIMIT)
 check_function_exists("setlocale"       HAVE_SETLOCALE)
 check_function_exists("setmode"         HAVE_SETMODE)
 check_function_exists("setrlimit"       HAVE_SETRLIMIT)
 
 if(NOT WIN32)
-  check_function_exists("sched_yield"   HAVE_SCHED_YIELD)
+  check_function_exists("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # iphlpapi.h (Windows non-UWP), net/if.h
+  check_function_exists("realpath"        HAVE_REALPATH)
+  check_function_exists("sched_yield"     HAVE_SCHED_YIELD)
   check_symbol_exists("strcasecmp"      "string.h" HAVE_STRCASECMP)
   check_symbol_exists("stricmp"         "string.h" HAVE_STRICMP)
   check_symbol_exists("strcmpi"         "string.h" HAVE_STRCMPI)
@@ -1971,15 +1982,16 @@
   check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM)
 endif()
 
-if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
-  # Earlier MSVC compilers had faulty snprintf implementations
-  check_function_exists("snprintf" HAVE_SNPRINTF)
+if(NOT MSVC)
+  check_function_exists("snprintf" HAVE_SNPRINTF)  # to match detection method in ./configure
+elseif(MSVC_VERSION GREATER_EQUAL 1900)  # Earlier MSVC compilers had faulty snprintf implementations
+  check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF)  # snprintf may be a compatibility macro, not an exported function
 endif()
 if(APPLE)
   check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME)
 endif()
-check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP)  # arpa/inet.h
-check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON)  # arpa/inet.h
+check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP)  # arpa/inet.h netinet/in.h sys/socket.h
+check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON)  # arpa/inet.h netinet/in.h sys/socket.h
 
 check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR)
 if(HAVE_FSETXATTR)
@@ -2124,7 +2136,7 @@
     # The Mac version of GCC warns about use of long double. Disable it.
     get_source_file_property(_mprintf_compile_flags "mprintf.c" COMPILE_FLAGS)
     if(_mprintf_compile_flags)
-      set(_mprintf_compile_flags "${_mprintf_compile_flags} -Wno-long-double")
+      string(APPEND _mprintf_compile_flags " -Wno-long-double")
     else()
       set(_mprintf_compile_flags "-Wno-long-double")
     endif()
@@ -2143,13 +2155,15 @@
 add_definitions("-DHAVE_CONFIG_H")
 
 if(WIN32)
+  list(APPEND CURL_LIBS "ws2_32" "bcrypt")
+
   # _fseeki64() requires VS2005
   if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1400))
     set(USE_WIN32_LARGE_FILES ON)
   endif()
 
   # Use the manifest embedded in the Windows Resource
-  set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DCURL_EMBED_MANIFEST")
+  string(APPEND CMAKE_RC_FLAGS " -DCURL_EMBED_MANIFEST")
 
   # We use crypto functions that are not available for UWP apps
   if(NOT WINDOWS_STORE)
@@ -2164,26 +2178,25 @@
 
 if(MSVC)
   # Disable default manifest added by CMake
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -MANIFEST:NO")
+  string(APPEND CMAKE_EXE_LINKER_FLAGS " -MANIFEST:NO")
 
   if(CMAKE_C_FLAGS MATCHES "[/-]W[0-4]")
     string(REGEX REPLACE "[/-]W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
   else()
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W4")
+    string(APPEND CMAKE_C_FLAGS " -W4")
   endif()
 
   # Use multithreaded compilation on VS2008+
   if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1500)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP")
+    string(APPEND CMAKE_C_FLAGS " -MP")
   endif()
 endif()
 
 if(CURL_WERROR)
   if(MSVC)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -WX")
+    string(APPEND CMAKE_C_FLAGS " -WX")
   else()
-    # This assumes clang or gcc style options
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
+    string(APPEND CMAKE_C_FLAGS " -Werror")  # This assumes clang or gcc style options
   endif()
 endif()
 
@@ -2352,7 +2365,7 @@
 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)
+curl_add_if("asyn-rr"       USE_ARES AND ENABLE_THREADED_RESOLVER AND USE_HTTPSRR)
 curl_add_if("IDN"           (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR
                             USE_WIN32_IDN OR
                             USE_APPLE_IDN)
@@ -2427,7 +2440,6 @@
 
   # curl-config needs the following options to be set.
   set(CC                      "${CMAKE_C_COMPILER}")
-  # TODO: probably put a -D... options here?
   set(CONFIGURE_OPTIONS       "")
   set(CURLVERSION             "${_curl_version}")
   set(VERSIONNUM              "${_curl_version_num}")
@@ -2471,18 +2483,18 @@
   endforeach()
 
   # Avoid getting unnecessary -L options for known system directories.
-  set(_sys_libdirs "")
+  set(_sys_libdirs "${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}")
   foreach(_libdir IN LISTS CMAKE_SYSTEM_PREFIX_PATH)
     if(_libdir MATCHES "/$")
-      set(_libdir "${_libdir}lib")
+      string(APPEND _libdir "lib")
     else()
-      set(_libdir "${_libdir}/lib")
+      string(APPEND _libdir "/lib")
     endif()
     if(IS_DIRECTORY "${_libdir}")
       list(APPEND _sys_libdirs "${_libdir}")
     endif()
     if(DEFINED CMAKE_LIBRARY_ARCHITECTURE)
-      set(_libdir "${_libdir}/${CMAKE_LIBRARY_ARCHITECTURE}")
+      string(APPEND _libdir "/${CMAKE_LIBRARY_ARCHITECTURE}")
       if(IS_DIRECTORY "${_libdir}")
         list(APPEND _sys_libdirs "${_libdir}")
       endif()
@@ -2490,6 +2502,9 @@
   endforeach()
 
   foreach(_libdir IN LISTS _custom_libdirs CURL_LIBDIRS)
+    if(NOT CMAKE_VERSION VERSION_LESS 3.20)
+      cmake_path(SET _libdir NORMALIZE "${_libdir}")
+    endif()
     list(FIND _sys_libdirs "${_libdir}" _libdir_index)
     if(_libdir_index LESS 0)
       list(APPEND _ldflags "-L${_libdir}")
@@ -2498,7 +2513,7 @@
 
   set(_implicit_libs "")
   if(NOT MINGW AND NOT UNIX)
-    set(_implicit_libs ${CMAKE_C_IMPLICIT_LINK_LIBRARIES})
+    set(_implicit_libs "${CMAKE_C_IMPLICIT_LINK_LIBRARIES}")
   endif()
 
   foreach(_lib IN LISTS _implicit_libs _custom_libs CURL_LIBS)
@@ -2524,6 +2539,9 @@
       get_filename_component(_libdir ${_lib} DIRECTORY)
       get_filename_component(_libname ${_lib} NAME_WE)
       if(_libname MATCHES "^lib")
+        if(NOT CMAKE_VERSION VERSION_LESS 3.20)
+          cmake_path(SET _libdir NORMALIZE "${_libdir}")
+        endif()
         list(FIND _sys_libdirs "${_libdir}" _libdir_index)
         if(_libdir_index LESS 0)
           list(APPEND _ldflags "-L${_libdir}")
@@ -2694,6 +2712,7 @@
 endif()
 
 # Save build info for test runner to pick up and log
+set(_cmake_sysroot "")
 if(CMAKE_OSX_SYSROOT)
   set(_cmake_sysroot ${CMAKE_OSX_SYSROOT})
 elseif(CMAKE_SYSROOT)
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index a7c1a08..1c00572 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,13 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.12.0"
+#define LIBCURL_VERSION "8.12.1"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
 #define LIBCURL_VERSION_MINOR 12
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_PATCH 1
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080c00
+#define LIBCURL_VERSION_NUM 0x080c01
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index ecdfe3b..ae5fa5a 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -94,6 +94,9 @@
   )
   target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB")
   target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+  # There is plenty of parallelism when building the testdeps target.
+  # Override the curlu batch size with the maximum to optimize performance.
+  set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0)
 endif()
 
 if(ENABLE_CURLDEBUG)
@@ -104,6 +107,13 @@
 
 ## Library definition
 
+if(NOT DEFINED IMPORT_LIB_SUFFIX)
+  set(IMPORT_LIB_SUFFIX "")
+endif()
+if(NOT DEFINED STATIC_LIB_SUFFIX)
+  set(STATIC_LIB_SUFFIX "")
+endif()
+
 # Add "_imp" as a suffix before the extension to avoid conflicting with
 # the statically linked "libcurl.lib" (typically with MSVC)
 if(WIN32 AND
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 38dc729..640c8a9 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -109,6 +109,9 @@
   int i;
   int num = 0;
 
+  if(!channel)
+    return 0;
+
   bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
 
   for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index b8c3049..f989415 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -138,14 +138,14 @@
   return Curl_resolver_init(easy, to);
 }
 
-static void destroy_async_data(struct Curl_async *);
+static void destroy_async_data(struct Curl_easy *);
 
 /*
  * Cancel all possibly still on-going resolves for this connection.
  */
 void Curl_resolver_cancel(struct Curl_easy *data)
 {
-  destroy_async_data(&data->state.async);
+  destroy_async_data(data);
 }
 
 /* This function is used to init a threaded resolve */
@@ -282,14 +282,6 @@
   struct thread_data *td = tsd->td;
   char service[12];
   int rc;
-#ifndef CURL_DISABLE_SOCKETPAIR
-#ifdef USE_EVENTFD
-  const void *buf;
-  const uint64_t val = 1;
-#else
-  char buf[1];
-#endif
-#endif
 
   msnprintf(service, sizeof(service), "%d", tsd->port);
 
@@ -315,9 +307,9 @@
 #ifndef CURL_DISABLE_SOCKETPAIR
     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
 #ifdef USE_EVENTFD
-      buf = &val;
+      const uint64_t buf[1] = { 1 };
 #else
-      buf[0] = 1;
+      const char buf[1] = { 1 };
 #endif
       /* DNS has been resolved, signal client task */
       if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
@@ -377,18 +369,22 @@
 /*
  * destroy_async_data() cleans up async resolver data and thread handle.
  */
-static void destroy_async_data(struct Curl_async *async)
+static void destroy_async_data(struct Curl_easy *data)
 {
+  struct Curl_async *async;
+  DEBUGASSERT(data);
+  async = &data->state.async;
+  DEBUGASSERT(async);
   if(async->tdata) {
     struct thread_data *td = async->tdata;
     bool done;
 #ifndef CURL_DISABLE_SOCKETPAIR
     curl_socket_t sock_rd = td->tsd.sock_pair[0];
-    struct Curl_easy *data = td->tsd.data;
 #endif
 
 #ifdef USE_HTTPSRR_ARES
-    ares_destroy(data->state.async.tdata->channel);
+    if(data->state.async.tdata->channel)
+      ares_destroy(data->state.async.tdata->channel);
 #endif
     /*
      * if the thread is still blocking in the resolve syscall, detach it and
@@ -495,12 +491,12 @@
   }
 #ifdef USE_HTTPSRR_ARES
   if(resolve_httpsrr(data, asp))
-    goto err_exit;
+    infof(data, "Failed HTTPS RR operation");
 #endif
   return TRUE;
 
 err_exit:
-  destroy_async_data(asp);
+  destroy_async_data(data);
 
 errno_exit:
   errno = err;
@@ -539,7 +535,7 @@
     /* a name was not resolved, report error */
     result = Curl_resolver_error(data);
 
-  destroy_async_data(&data->state.async);
+  destroy_async_data(data);
 
   if(!data->state.async.dns && report)
     connclose(data->conn, "asynch resolve failed");
@@ -617,7 +613,7 @@
 
     if(!data->state.async.dns) {
       CURLcode result = Curl_resolver_error(data);
-      destroy_async_data(&data->state.async);
+      destroy_async_data(data);
       return result;
     }
 #ifdef USE_HTTPSRR_ARES
@@ -625,13 +621,13 @@
       struct Curl_https_rrinfo *lhrr =
         Curl_memdup(&td->hinfo, sizeof(struct Curl_https_rrinfo));
       if(!lhrr) {
-        destroy_async_data(&data->state.async);
+        destroy_async_data(data);
         return CURLE_OUT_OF_MEMORY;
       }
       data->state.async.dns->hinfo = lhrr;
     }
 #endif
-    destroy_async_data(&data->state.async);
+    destroy_async_data(data);
     *entry = data->state.async.dns;
   }
   else {
@@ -665,15 +661,17 @@
   timediff_t milli;
   timediff_t ms;
   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-  int socketi = 0;
 #ifndef CURL_DISABLE_SOCKETPAIR
   struct thread_data *td = data->state.async.tdata;
+#endif
+#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
+  int socketi = 0;
 #else
   (void)socks;
 #endif
 
 #ifdef USE_HTTPSRR_ARES
-  if(data->state.async.tdata) {
+  if(data->state.async.tdata && data->state.async.tdata->channel) {
     ret_val = Curl_ares_getsock(data, data->state.async.tdata->channel, socks);
     for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
       if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
@@ -685,8 +683,7 @@
   if(td) {
     /* return read fd to client for polling the DNS resolution status */
     socks[socketi] = td->tsd.sock_pair[0];
-    td->tsd.data = data;
-    ret_val = GETSOCK_READSOCK(socketi);
+    ret_val |= GETSOCK_READSOCK(socketi);
   }
   else {
 #endif
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h
index c674541..5a21329 100644
--- a/Utilities/cmcurl/lib/asyn.h
+++ b/Utilities/cmcurl/lib/asyn.h
@@ -40,20 +40,19 @@
 /* Data for synchronization between resolver thread and its parent */
 struct thread_sync_data {
   curl_mutex_t *mtx;
-  bool done;
-  int port;
   char *hostname;        /* hostname to resolve, Curl_async.hostname
                             duplicate */
 #ifndef CURL_DISABLE_SOCKETPAIR
-  struct Curl_easy *data;
   curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
 #endif
-  int sock_error;
   struct Curl_addrinfo *res;
 #ifdef HAVE_GETADDRINFO
   struct addrinfo hints;
 #endif
   struct thread_data *td; /* for thread-self cleanup */
+  int port;
+  int sock_error;
+  bool done;
 };
 
 struct thread_data {
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c
index 2e13f39..9b75d08 100644
--- a/Utilities/cmcurl/lib/cf-h1-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -660,7 +660,6 @@
     cf->ctx = ts;
   }
 
-  /* TODO: can we do blocking? */
   /* We want "seamless" operations through HTTP proxy tunnel */
 
   result = H1_CONNECT(cf, data, ts);
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c
index 771ecc1..d8b9128 100644
--- a/Utilities/cmcurl/lib/cf-h2-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -1408,7 +1408,7 @@
   ssize_t nwritten;
   CURLcode result;
 
-  (void)eos; /* TODO, maybe useful for blocks? */
+  (void)eos;
   if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
     *err = CURLE_SEND_ERROR;
     return -1;
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c
index 2b39e98..f073647 100644
--- a/Utilities/cmcurl/lib/cf-https-connect.c
+++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -42,11 +42,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 typedef enum {
   CF_HC_INIT,
   CF_HC_CONNECT,
@@ -592,8 +587,8 @@
 
   DEBUGASSERT(alpnids);
   DEBUGASSERT(alpn_count);
-  DEBUGASSERT(alpn_count <= ARRAYSIZE(ctx->ballers));
-  if(!alpn_count || (alpn_count > ARRAYSIZE(ctx->ballers))) {
+  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);
     return CURLE_FAILED_INIT;
@@ -607,7 +602,7 @@
   ctx->remotehost = remotehost;
   for(i = 0; i < alpn_count; ++i)
     cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
-  for(; i < ARRAYSIZE(ctx->ballers); ++i)
+  for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
     ctx->ballers[i].alpn_id = ALPN_none;
   ctx->baller_count = alpn_count;
 
@@ -663,8 +658,8 @@
       if(conn->dns_entry && conn->dns_entry->hinfo &&
          !conn->dns_entry->hinfo->no_def_alpn) {
         size_t i, j;
-        for(i = 0; i < ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
-                   alpn_count < ARRAYSIZE(alpn_ids); ++i) {
+        for(i = 0; i < CURL_ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
+                   alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
           bool present = FALSE;
           enum alpnid alpn = conn->dns_entry->hinfo->alpns[i];
           for(j = 0; j < alpn_count; ++j) {
@@ -701,7 +696,6 @@
       break;
     case CURL_HTTP_VERSION_3:
       /* We assume that silently not even trying H3 is ok here */
-      /* TODO: should we fail instead? */
       if(Curl_conn_may_http3(data, conn) == CURLE_OK)
         alpn_ids[alpn_count++] = ALPN_h3;
       alpn_ids[alpn_count++] = ALPN_h2;
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
index edda348..a6f9888 100644
--- a/Utilities/cmcurl/lib/cf-socket.c
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -1325,7 +1325,6 @@
     return CURLE_OK;
   }
 
-  /* TODO: need to support blocking connect? */
   if(blocking)
     return CURLE_UNSUPPORTED_PROTOCOL;
 
@@ -1432,7 +1431,7 @@
     /* A listening socket filter needs to be connected before the accept
      * for some weird FTP interaction. This should be rewritten, so that
      * FTP no longer does the socket checks and accept calls and delegates
-     * all that to the filter. TODO. */
+     * all that to the filter. */
     if(ctx->listening) {
       Curl_pollset_set_in_only(data, ps, ctx->sock);
       CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
@@ -1850,7 +1849,7 @@
   /* QUIC needs a connected socket, nonblocking */
   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
 
-  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,  /* NOLINT FIXME */
+  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,  /* NOLINT */
                (curl_socklen_t)ctx->addr.addrlen);
   if(-1 == rc) {
     return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
@@ -2213,7 +2212,7 @@
   cf_tcp_accept_connect,
   cf_socket_close,
   cf_socket_shutdown,
-  cf_socket_get_host,              /* TODO: not accurate */
+  cf_socket_get_host,
   cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index 3be0840..6a894e8 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -41,10 +41,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 static void cf_cntrl_update_info(struct Curl_easy *data,
                                  struct connectdata *conn);
 
@@ -724,7 +720,7 @@
   CURLcode result = CURLE_OK;
   size_t i;
 
-  for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
     result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
                                 event, arg1, arg2);
     if(!ignore_result && result)
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index 85435bf..b8a0515 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -166,11 +166,11 @@
   if(!cpool->idata)
     return 1; /* bad */
   cpool->idata->state.internal = TRUE;
-  /* TODO: this is quirky. We need an internal handle for certain
-   * operations, but we do not add it to the multi (if there is one).
-   * But we give it the multi so that socket event operations can work.
-   * Probably better to have an internal handle owned by the multi that
-   * can be used for cpool operations. */
+  /* This is quirky. We need an internal handle for certain operations, but we
+   * do not add it to the multi (if there is one). We give it the multi so
+   * that socket event operations can work. Probably better to have an
+   * internal handle owned by the multi that can be used for cpool
+   * operations. */
   cpool->idata->multi = multi;
 #ifdef DEBUGBUILD
   if(getenv("CURL_DEBUG"))
@@ -1302,7 +1302,6 @@
                        void *param)
 {
   struct curltime *now = param;
-  /* TODO, shall we reap connections that return an error here? */
   Curl_conn_upkeep(data, conn, now);
   return 0; /* continue iteration */
 }
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index 05aabff..67cdb54 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -85,10 +85,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
 
 enum alpnid Curl_alpn2alpnid(char *name, size_t len)
@@ -644,7 +640,7 @@
   *connected = FALSE; /* a negative world view is best */
   now = Curl_now();
   ongoing = not_started = 0;
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     if(!baller || baller->is_done)
@@ -705,7 +701,7 @@
   if(not_started > 0) {
     int added = 0;
 
-    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
       struct eyeballer *baller = ctx->baller[i];
 
       if(!baller || baller->has_started)
@@ -739,7 +735,7 @@
   /* all ballers have failed to connect. */
   CURL_TRC_CF(data, cf, "all eyeballers failed");
   result = CURLE_COULDNT_CONNECT;
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
     if(!baller)
       continue;
@@ -884,7 +880,7 @@
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(data);
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     baller_free(ctx->baller[i], data);
     ctx->baller[i] = NULL;
   }
@@ -907,7 +903,7 @@
 
   /* shutdown all ballers that have not done so already. If one fails,
    * continue shutting down others until all are shutdown. */
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
     bool bdone = FALSE;
     if(!baller || !baller->cf || baller->shutdown)
@@ -918,12 +914,12 @@
   }
 
   *done = TRUE;
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     if(ctx->baller[i] && !ctx->baller[i]->shutdown)
       *done = FALSE;
   }
   if(*done) {
-    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
       if(ctx->baller[i] && ctx->baller[i]->result)
         result = ctx->baller[i]->result;
     }
@@ -940,7 +936,7 @@
   size_t i;
 
   if(!cf->connected) {
-    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
       struct eyeballer *baller = ctx->baller[i];
       if(!baller || !baller->cf)
         continue;
@@ -962,7 +958,7 @@
     return CURLE_OK;
   }
 
-  (void)blocking; /* TODO: do we want to support this? */
+  (void)blocking;
   DEBUGASSERT(ctx);
   *done = FALSE;
 
@@ -1037,7 +1033,7 @@
   if(cf->connected)
     return cf->next->cft->has_data_pending(cf->next, data);
 
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
     if(!baller || !baller->cf)
       continue;
@@ -1056,7 +1052,7 @@
   size_t i;
 
   memset(&tmax, 0, sizeof(tmax));
-  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     memset(&t, 0, sizeof(t));
@@ -1081,7 +1077,7 @@
       int reply_ms = -1;
       size_t i;
 
-      for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+      for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
         struct eyeballer *baller = ctx->baller[i];
         int breply_ms;
 
@@ -1215,7 +1211,7 @@
 static cf_ip_connect_create *get_cf_create(int transport)
 {
   size_t i;
-  for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
     if(transport == transport_providers[i].transport)
       return transport_providers[i].cf_create;
   }
@@ -1469,7 +1465,7 @@
                                        cf_ip_connect_create *cf_create)
 {
   size_t i;
-  for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
     if(transport == transport_providers[i].transport) {
       transport_providers[i].cf_create = cf_create;
       return;
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index 3111bed..e83a790 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -69,6 +69,10 @@
 
 #ifdef HAVE_LIBZ
 
+#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
+#error "requires zlib 1.2.0.4 or newer"
+#endif
+
 typedef enum {
   ZLIB_UNINIT,               /* uninitialized */
   ZLIB_INIT,                 /* initialized */
@@ -309,24 +313,15 @@
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
-  const char *v = zlibVersion();
 
   /* Initialize zlib */
   z->zalloc = (alloc_func) zalloc_cb;
   z->zfree = (free_func) zfree_cb;
 
-  if(strcmp(v, "1.2.0.4") >= 0) {
-    /* zlib version >= 1.2.0.4 supports transparent gzip decompressing */
-    if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
-      return process_zlib_error(data, z);
-    }
-    zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
-  }
-  else {
-    failf(data, "too old zlib version: %s", v);
-    return CURLE_FAILED_INIT;
-  }
+  if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)
+    return process_zlib_error(data, z);
 
+  zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
   return CURLE_OK;
 }
 
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index 6fe949b..9819768 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -879,7 +879,7 @@
       /*
        * flag: A TRUE/FALSE value indicating if all machines within a given
        * domain can access the variable. Set TRUE when the cookie says
-       * .domain.com and to false when the domain is complete www.domain.com
+       * .example.com and to false when the domain is complete www.example.com
        */
       co->tailmatch = !!strncasecompare(ptr, "TRUE", len);
       break;
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index 32f1d0c..2091065 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -428,6 +428,9 @@
 /* If you have poll */
 #cmakedefine HAVE_POLL 1
 
+/* If you have realpath */
+#cmakedefine HAVE_REALPATH 1
+
 /* Define to 1 if you have the <poll.h> header file. */
 #cmakedefine HAVE_POLL_H 1
 
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 1aac082..89500a1 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -948,6 +948,8 @@
    as their argument */
 #define STRCONST(x) x,sizeof(x)-1
 
+#define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+
 /* Some versions of the Android NDK is missing the declaration */
 #if defined(HAVE_GETPWUID_R) && \
   defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
@@ -998,10 +1000,16 @@
 #  endif
 #endif
 
+#ifdef USE_OPENSSL
 /* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no
    replacements (yet) so tell the compiler to not warn for them. */
-#ifdef USE_OPENSSL
-#define OPENSSL_SUPPRESS_DEPRECATED
+#  define OPENSSL_SUPPRESS_DEPRECATED
+#  ifdef _WIN32
+/* Silence LibreSSL warnings about wincrypt.h collision. Works in 3.8.2+ */
+#    ifndef LIBRESSL_DISABLE_OVERRIDE_WINCRYPT_DEFINES_WARNING
+#    define LIBRESSL_DISABLE_OVERRIDE_WINCRYPT_DEFINES_WARNING
+#    endif
+#  endif
 #endif
 
 #if defined(CURL_INLINE)
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c
index e773e4f..07137c1 100644
--- a/Utilities/cmcurl/lib/curl_trc.c
+++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -53,10 +53,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 void Curl_debug(struct Curl_easy *data, curl_infotype type,
                 char *ptr, size_t size)
 {
@@ -349,13 +345,13 @@
 {
   size_t i;
 
-  for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
     if(strcasecompare(token, trc_cfts[i].cft->name)) {
       trc_cfts[i].cft->log_level = lvl;
       break;
     }
   }
-  for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
     if(strcasecompare(token, trc_feats[i].feat->name)) {
       trc_feats[i].feat->log_level = lvl;
       break;
@@ -367,11 +363,11 @@
 {
   size_t i;
 
-  for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
     if(!category || (trc_cfts[i].category & category))
       trc_cfts[i].cft->log_level = lvl;
   }
-  for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
     if(!category || (trc_feats[i].category & category))
       trc_feats[i].feat->log_level = lvl;
   }
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 8e7ec5e..5733d69 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -4108,11 +4108,6 @@
   return CURLE_OK;
 }
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4706) /* assignment within conditional expression */
-#endif
-
 /***********************************************************************
  *
  * ftp_parse_url_path()
@@ -4203,7 +4198,8 @@
         }
 
         /* parse the URL path into separate path components */
-        while((slashPos = strchr(curPos, '/'))) {
+        /* !checksrc! disable EQUALSNULL 1 */
+        while((slashPos = strchr(curPos, '/')) != NULL) {
           size_t compLen = slashPos - curPos;
 
           /* path starts with a slash: add that as a directory */
@@ -4267,10 +4263,6 @@
   return CURLE_OK;
 }
 
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
 /* call this when the DO phase has completed */
 static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
 {
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 1fe0904..5bd2bd4 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -495,7 +495,6 @@
             ongoing_auth ? " send, " : "");
     /* We decided to abort the ongoing transfer */
     streamclose(conn, "Mid-auth HTTP and much data left to send");
-    /* FIXME: questionable manipulation here, can we do this differently? */
     data->req.size = 0; /* do not download any more than 0 bytes */
   }
   return CURLE_OK;
@@ -2477,7 +2476,7 @@
       }
       else if(data->state.resume_from) {
         /* This is because "resume" was selected */
-        /* TODO: not sure if we want to send this header during authentication
+        /* Not sure if we want to send this header during authentication
          * negotiation, but test1084 checks for it. In which case we have a
          * "null" client reader installed that gives an unexpected length. */
         curl_off_t total_len = data->req.authneg ?
@@ -3597,10 +3596,9 @@
       }
 #endif
       else {
-        /* We silently accept this as the final response.
-         * TODO: this looks, uhm, wrong. What are we switching to if we
-         * did not ask for an Upgrade? Maybe the application provided an
-         * `Upgrade: xxx` header? */
+        /* 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;
diff --git a/Utilities/cmcurl/lib/http1.c b/Utilities/cmcurl/lib/http1.c
index f135b20..9d2461e 100644
--- a/Utilities/cmcurl/lib/http1.c
+++ b/Utilities/cmcurl/lib/http1.c
@@ -167,8 +167,6 @@
   if(!target_len || !hv_len)
     goto out;
 
-  /* TODO: we do not check HTTP_VERSION for conformity, should
-   + do that when STRICT option is supplied. */
   (void)hv;
 
   /* The TARGET can be (rfc 9112, ch. 3.2):
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index e5ee401..49abaf4 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -193,19 +193,6 @@
 };
 
 
-#ifdef USE_SSL
-static void imap_to_imaps(struct connectdata *conn)
-{
-  /* Change the connection handler */
-  conn->handler = &Curl_handler_imaps;
-
-  /* Set the connection's upgraded to TLS flag */
-  conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define imap_to_imaps(x) Curl_nop_stmt
-#endif
-
 /***********************************************************************
  *
  * imap_matchresp()
@@ -474,6 +461,7 @@
 static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
                                          struct connectdata *conn)
 {
+#ifdef USE_SSL
   /* Start the SSL connection */
   struct imap_conn *imapc = &conn->proto.imapc;
   CURLcode result;
@@ -483,21 +471,27 @@
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;
+    /* Change the connection handler */
+    conn->handler = &Curl_handler_imaps;
+    conn->bits.tls_upgraded = TRUE;
   }
 
+  DEBUGASSERT(!imapc->ssldone);
   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-  if(!result) {
+  DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d",
+         result, ssldone));
+  if(!result && ssldone) {
     imapc->ssldone = ssldone;
-    if(imapc->state != IMAP_UPGRADETLS)
-      imap_state(data, IMAP_UPGRADETLS);
-
-    if(imapc->ssldone) {
-      imap_to_imaps(conn);
-      result = imap_perform_capability(data, conn);
-    }
+     /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */
+     result = imap_perform_capability(data, conn);
   }
 out:
   return result;
+#else
+  (void)data;
+  (void)conn;
+  return CURLE_NOT_BUILT_IN;
+#endif
 }
 
 /***********************************************************************
@@ -998,7 +992,7 @@
       result = imap_perform_authentication(data, conn);
   }
   else
-    result = imap_perform_upgrade_tls(data, conn);
+    imap_state(data, IMAP_UPGRADETLS);
 
   return result;
 }
@@ -1307,8 +1301,12 @@
   (void)data;
 
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
-  if(imapc->state == IMAP_UPGRADETLS)
-    return imap_perform_upgrade_tls(data, conn);
+upgrade_tls:
+  if(imapc->state == IMAP_UPGRADETLS) {
+    result = imap_perform_upgrade_tls(data, conn);
+    if(result || (imapc->state == IMAP_UPGRADETLS))
+      return result;
+  }
 
   /* Flush any data that needs to be sent */
   if(pp->sendleft)
@@ -1339,6 +1337,10 @@
 
     case IMAP_STARTTLS:
       result = imap_state_starttls_resp(data, imapcode, imapc->state);
+      /* During UPGRADETLS, leave the read loop as we need to connect
+       * (e.g. TLS handshake) before we continue sending/receiving. */
+      if(!result && (imapc->state == IMAP_UPGRADETLS))
+        goto upgrade_tls;
       break;
 
     case IMAP_AUTHENTICATE:
@@ -1392,14 +1394,6 @@
   struct connectdata *conn = data->conn;
   struct imap_conn *imapc = &conn->proto.imapc;
 
-  if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !imapc->ssldone) {
-    bool ssldone = FALSE;
-    result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-    imapc->ssldone = ssldone;
-    if(result || !ssldone)
-      return result;
-  }
-
   result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
   *done = (imapc->state == IMAP_STOP);
 
diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h
index 3b90ed3..6bc7e27 100644
--- a/Utilities/cmcurl/lib/inet_ntop.h
+++ b/Utilities/cmcurl/lib/inet_ntop.h
@@ -29,6 +29,12 @@
 char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
 
 #ifdef HAVE_INET_NTOP
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h
index 50bce61..915385f 100644
--- a/Utilities/cmcurl/lib/inet_pton.h
+++ b/Utilities/cmcurl/lib/inet_pton.h
@@ -29,6 +29,12 @@
 int Curl_inet_pton(int, const char *, void *);
 
 #ifdef HAVE_INET_PTON
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index 0fa304b..77bd9fb 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -389,55 +389,7 @@
 #else
     int ldap_option;
     char *ldap_ca = conn->ssl_config.CAfile;
-#if defined(CURL_HAS_NOVELL_LDAPSDK)
-    rc = ldapssl_client_init(NULL, NULL);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
-      result = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-    if(conn->ssl_config.verifypeer) {
-      /* Novell SDK supports DER or BASE64 files. */
-      int cert_type = LDAPSSL_CERT_FILETYPE_B64;
-      if((data->set.ssl.cert_type) &&
-         (strcasecompare(data->set.ssl.cert_type, "DER")))
-        cert_type = LDAPSSL_CERT_FILETYPE_DER;
-      if(!ldap_ca) {
-        failf(data, "LDAP local: ERROR %s CA cert not set",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
-        result = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      infof(data, "LDAP local: using %s CA cert '%s'",
-            (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-            ldap_ca);
-      rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
-      if(rc != LDAP_SUCCESS) {
-        failf(data, "LDAP local: ERROR setting %s CA cert: %s",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-              ldap_err2string(rc));
-        result = CURLE_SSL_CERTPROBLEM;
-        goto quit;
-      }
-      ldap_option = LDAPSSL_VERIFY_SERVER;
-    }
-    else
-      ldap_option = LDAPSSL_VERIFY_NONE;
-    rc = ldapssl_set_verify_mode(ldap_option);
-    if(rc != LDAP_SUCCESS) {
-      failf(data, "LDAP local: ERROR setting cert verify mode: %s",
-              ldap_err2string(rc));
-      result = CURLE_SSL_CERTPROBLEM;
-      goto quit;
-    }
-    server = ldapssl_init(host, conn->primary.remote_port, 1);
-    if(!server) {
-      failf(data, "LDAP local: Cannot connect to %s:%u",
-            conn->host.dispname, conn->primary.remote_port);
-      result = CURLE_COULDNT_CONNECT;
-      goto quit;
-    }
-#elif defined(LDAP_OPT_X_TLS)
+#ifdef LDAP_OPT_X_TLS
     if(conn->ssl_config.verifypeer) {
       /* OpenLDAP SDK supports BASE64 files. */
       if((data->set.ssl.cert_type) &&
@@ -758,10 +710,6 @@
     ldap_free_urldesc(ludp);
   if(server)
     ldap_unbind_s(server);
-#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
-  if(ldap_ssl)
-    ldapssl_client_deinit();
-#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
 
   FREE_ON_WINLDAP(host);
 
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index d448891..a90d170 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -1598,8 +1598,8 @@
 
   (void) size;   /* Always 1. */
 
-  /* TODO: this loop is broken. If `nitems` is <= 4, some encoders will
-   * return STOP_FILLING without adding any data and this loops infinitely. */
+  /* If `nitems` is <= 4, some encoders will return STOP_FILLING without
+   * adding any data and this loops infinitely. */
   do {
     hasread = FALSE;
     ret = readback_part(part, buffer, nitems, &hasread);
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index d4dd4a0..2b05e94 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -1538,15 +1538,6 @@
      Curl_multi struct that are constant */
   struct Curl_multi *multi = m;
 
-#if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK)
-#ifdef USE_EVENTFD
-  const void *buf;
-  const uint64_t val = 1;
-#else
-  char buf[1];
-#endif
-#endif
-
   /* GOOD_MULTI_HANDLE can be safely called */
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -1560,15 +1551,14 @@
      making it safe to access from another thread after the init part
      and before cleanup */
   if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
-#ifdef USE_EVENTFD
-    buf = &val;
-    /* eventfd has a stringent rule of requiring the 8-byte buffer when
-       calling write(2) on it, which makes the sizeof(buf) below fine since
-       this is only used on 64-bit systems and then the pointer is 64-bit */
-#else
-    buf[0] = 1;
-#endif
     while(1) {
+#ifdef USE_EVENTFD
+      /* eventfd has a stringent rule of requiring the 8-byte buffer when
+         calling write(2) on it */
+      const uint64_t buf[1] = { 1 };
+#else
+      const char buf[1] = { 1 };
+#endif
       /* swrite() is not thread-safe in general, because concurrent calls
          can have their messages interleaved, but in this case the content
          of the messages does not matter, which makes it ok to call.
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 7ad81ec..ba67089 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -59,23 +59,26 @@
 #define FOUND_LOGIN    1
 #define FOUND_PASSWORD 2
 
-#define NETRC_FILE_MISSING 1
-#define NETRC_FAILED -1
-#define NETRC_SUCCESS 0
-
 #define MAX_NETRC_LINE 16384
 #define MAX_NETRC_FILE (128*1024)
 #define MAX_NETRC_TOKEN 4096
 
-static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
+/* convert a dynbuf call CURLcode error to a NETRCcode error */
+#define curl2netrc(result)                     \
+  (((result) == CURLE_OUT_OF_MEMORY) ?         \
+   NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)
+
+static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
 {
-  CURLcode result = CURLE_OK;
+  NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
   FILE *file = fopen(filename, FOPEN_READTEXT);
   struct dynbuf linebuf;
   Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
 
   if(file) {
+    ret = NETRC_OK;
     while(Curl_get_line(&linebuf, file)) {
+      CURLcode result;
       const char *line = Curl_dyn_ptr(&linebuf);
       /* skip comments on load */
       while(ISBLANK(*line))
@@ -83,27 +86,29 @@
       if(*line == '#')
         continue;
       result = Curl_dyn_add(filebuf, line);
-      if(result)
+      if(result) {
+        ret = curl2netrc(result);
         goto done;
+      }
     }
   }
 done:
   Curl_dyn_free(&linebuf);
   if(file)
     fclose(file);
-  return result;
+  return ret;
 }
 
 /*
  * Returns zero on success.
  */
-static int parsenetrc(struct store_netrc *store,
-                      const char *host,
-                      char **loginp, /* might point to a username */
-                      char **passwordp,
-                      const char *netrcfile)
+static NETRCcode parsenetrc(struct store_netrc *store,
+                            const char *host,
+                            char **loginp, /* might point to a username */
+                            char **passwordp,
+                            const char *netrcfile)
 {
-  int retcode = NETRC_FILE_MISSING;
+  NETRCcode retcode = NETRC_NO_MATCH;
   char *login = *loginp;
   char *password = NULL;
   bool specific_login = !!login; /* points to something */
@@ -120,8 +125,9 @@
   Curl_dyn_init(&token, MAX_NETRC_TOKEN);
 
   if(!store->loaded) {
-    if(file2memory(netrcfile, filebuf))
-      return NETRC_FAILED;
+    NETRCcode ret = file2memory(netrcfile, filebuf);
+    if(ret)
+      return ret;
     store->loaded = TRUE;
   }
 
@@ -151,12 +157,18 @@
       tok_end = tok;
       if(!quoted) {
         size_t len = 0;
+        CURLcode result;
         while(!ISSPACE(*tok_end)) {
           tok_end++;
           len++;
         }
-        if(!len || Curl_dyn_addn(&token, tok, len)) {
-          retcode = NETRC_FAILED;
+        if(!len) {
+          retcode = NETRC_SYNTAX_ERROR;
+          goto out;
+        }
+        result = Curl_dyn_addn(&token, tok, len);
+        if(result) {
+          retcode = curl2netrc(result);
           goto out;
         }
       }
@@ -165,6 +177,7 @@
         bool endquote = FALSE;
         tok_end++; /* pass the leading quote */
         while(*tok_end) {
+          CURLcode result;
           char s = *tok_end;
           if(escape) {
             escape = FALSE;
@@ -190,15 +203,16 @@
             endquote = TRUE;
             break;
           }
-          if(Curl_dyn_addn(&token, &s, 1)) {
-            retcode = NETRC_FAILED;
+          result = Curl_dyn_addn(&token, &s, 1);
+          if(result) {
+            retcode = curl2netrc(result);
             goto out;
           }
           tok_end++;
         }
         if(escape || !endquote) {
           /* bad syntax, get out */
-          retcode = NETRC_FAILED;
+          retcode = NETRC_SYNTAX_ERROR;
           goto out;
         }
       }
@@ -226,7 +240,7 @@
         }
         else if(strcasecompare("default", tok)) {
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
         }
         break;
       case MACDEF:
@@ -237,7 +251,7 @@
         if(strcasecompare(host, tok)) {
           /* and yes, this is our host! */
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
         }
         else
           /* not our host */
@@ -253,7 +267,7 @@
             free(login);
             login = strdup(tok);
             if(!login) {
-              retcode = NETRC_FAILED; /* allocation failed */
+              retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
               goto out;
             }
           }
@@ -264,7 +278,7 @@
           free(password);
           password = strdup(tok);
           if(!password) {
-            retcode = NETRC_FAILED; /* allocation failed */
+            retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
             goto out;
           }
           if(!specific_login || our_login)
@@ -290,7 +304,7 @@
         }
         else if(strcasecompare("default", tok)) {
           state = HOSTVALID;
-          retcode = NETRC_SUCCESS; /* we did find our host */
+          retcode = NETRC_OK; /* we did find our host */
           Curl_safefree(password);
           if(!specific_login)
             Curl_safefree(login);
@@ -321,11 +335,11 @@
       /* success without a password, set a blank one */
       password = strdup("");
       if(!password)
-        retcode = 1; /* out of memory */
+        retcode = NETRC_OUT_OF_MEMORY; /* out of memory */
     }
     else if(!login && !password)
       /* a default with no credentials */
-      retcode = NETRC_FILE_MISSING;
+      retcode = NETRC_NO_MATCH;
   }
   if(!retcode) {
     /* success */
@@ -343,17 +357,34 @@
   return retcode;
 }
 
+const char *Curl_netrc_strerror(NETRCcode ret)
+{
+  switch(ret) {
+  default:
+    return ""; /* not a legit error */
+  case NETRC_FILE_MISSING:
+    return "no such file";
+  case NETRC_NO_MATCH:
+    return "no matching entry";
+  case NETRC_OUT_OF_MEMORY:
+    return "out of memory";
+  case NETRC_SYNTAX_ERROR:
+    return "syntax error";
+  }
+  /* never reached */
+}
+
 /*
  * @unittest: 1304
  *
  * *loginp and *passwordp MUST be allocated if they are not NULL when passed
  * in.
  */
-int Curl_parsenetrc(struct store_netrc *store, const char *host,
-                    char **loginp, char **passwordp,
-                    char *netrcfile)
+NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host,
+                          char **loginp, char **passwordp,
+                          char *netrcfile)
 {
-  int retcode = 1;
+  NETRCcode retcode = NETRC_OK;
   char *filealloc = NULL;
 
   if(!netrcfile) {
@@ -391,23 +422,23 @@
     }
 
     if(!home)
-      return retcode; /* no home directory found (or possibly out of
-                         memory) */
+      return NETRC_FILE_MISSING; /* no home directory found (or possibly out
+                                    of memory) */
 
     filealloc = aprintf("%s%s.netrc", home, DIR_CHAR);
     if(!filealloc) {
       free(homea);
-      return -1;
+      return NETRC_OUT_OF_MEMORY;
     }
     retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
     free(filealloc);
 #ifdef _WIN32
-    if((retcode == NETRC_FILE_MISSING) || (retcode == NETRC_FAILED)) {
+    if(retcode == NETRC_FILE_MISSING) {
       /* fallback to the old-style "_netrc" file */
       filealloc = aprintf("%s%s_netrc", home, DIR_CHAR);
       if(!filealloc) {
         free(homea);
-        return -1;
+        return NETRC_OUT_OF_MEMORY;
       }
       retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
       free(filealloc);
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h
index 0ef9ff7..ac0f886 100644
--- a/Utilities/cmcurl/lib/netrc.h
+++ b/Utilities/cmcurl/lib/netrc.h
@@ -34,12 +34,21 @@
   BIT(loaded);
 };
 
+typedef enum {
+  NETRC_OK,
+  NETRC_NO_MATCH,      /* no matching entry in the file */
+  NETRC_SYNTAX_ERROR,  /* in the netrc file */
+  NETRC_FILE_MISSING,  /* the netrc file does not exist */
+  NETRC_OUT_OF_MEMORY, /* while parsing netrc */
+  NETRC_LAST /* never used */
+} NETRCcode;
+
+const char *Curl_netrc_strerror(NETRCcode ret);
 void Curl_netrc_init(struct store_netrc *s);
 void Curl_netrc_cleanup(struct store_netrc *s);
 
-/* returns -1 on failure, 0 if the host is found, 1 is the host is not found */
-int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp,
-                    char **passwordp, char *filename);
+NETRCcode Curl_parsenetrc(struct store_netrc *s, const char *host,
+                          char **loginp, char **passwordp, char *filename);
   /* Assume: (*passwordp)[0]=0, host[0] != 0.
    * If (*loginp)[0] = 0, search for login and password within a machine
    * section in the netrc.
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index 86e9eca..07c7dba 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -83,10 +83,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 /* Local API functions */
 static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
 static CURLcode pop3_do(struct Curl_easy *data, bool *done);
@@ -192,19 +188,6 @@
   SASL_FLAG_BASE64      /* Configuration flags */
 };
 
-#ifdef USE_SSL
-static void pop3_to_pop3s(struct connectdata *conn)
-{
-  /* Change the connection handler */
-  conn->handler = &Curl_handler_pop3s;
-
-  /* Set the connection's upgraded to TLS flag */
-  conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define pop3_to_pop3s(x) Curl_nop_stmt
-#endif
-
 struct pop3_cmd {
   const char *name;
   unsigned short nlen;
@@ -239,7 +222,7 @@
 static bool pop3_is_multiline(const char *cmdline)
 {
   size_t i;
-  for(i = 0; i < ARRAYSIZE(pop3cmds); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) {
     if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) {
       if(!cmdline[pop3cmds[i].nlen])
         return pop3cmds[i].multiline;
@@ -425,6 +408,7 @@
 static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
                                          struct connectdata *conn)
 {
+#ifdef USE_SSL
   /* Start the SSL connection */
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   CURLcode result;
@@ -434,22 +418,27 @@
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;
+    /* Change the connection handler */
+    conn->handler = &Curl_handler_pop3s;
+    conn->bits.tls_upgraded = TRUE;
   }
 
+  DEBUGASSERT(!pop3c->ssldone);
   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-
-  if(!result) {
+  DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d",
+         result, ssldone));
+  if(!result && ssldone) {
     pop3c->ssldone = ssldone;
-    if(pop3c->state != POP3_UPGRADETLS)
-      pop3_state(data, POP3_UPGRADETLS);
-
-    if(pop3c->ssldone) {
-      pop3_to_pop3s(conn);
-      result = pop3_perform_capa(data, conn);
-    }
+     /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */
+    result = pop3_perform_capa(data, conn);
   }
 out:
   return result;
+#else
+  (void)data;
+  (void)conn;
+  return CURLE_NOT_BUILT_IN;
+#endif
 }
 
 /***********************************************************************
@@ -861,7 +850,7 @@
       result = pop3_perform_authentication(data, conn);
   }
   else
-    result = pop3_perform_upgrade_tls(data, conn);
+    pop3_state(data, POP3_UPGRADETLS);
 
   return result;
 }
@@ -1039,8 +1028,12 @@
   (void)data;
 
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
-  if(pop3c->state == POP3_UPGRADETLS)
-    return pop3_perform_upgrade_tls(data, conn);
+upgrade_tls:
+  if(pop3c->state == POP3_UPGRADETLS) {
+    result = pop3_perform_upgrade_tls(data, conn);
+    if(result || (pop3c->state == POP3_UPGRADETLS))
+      return result;
+  }
 
   /* Flush any data that needs to be sent */
   if(pp->sendleft)
@@ -1067,6 +1060,10 @@
 
     case POP3_STARTTLS:
       result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
+      /* During UPGRADETLS, leave the read loop as we need to connect
+       * (e.g. TLS handshake) before we continue sending/receiving. */
+      if(!result && (pop3c->state == POP3_UPGRADETLS))
+        goto upgrade_tls;
       break;
 
     case POP3_AUTH:
@@ -1112,17 +1109,6 @@
   struct connectdata *conn = data->conn;
   struct pop3_conn *pop3c = &conn->proto.pop3c;
 
-  /* Issue #16166, STLS seems to stall and time out. Revert to previous
-   * check, but it remains to find out why this is wrong. */
-  /* if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !pop3c->ssldone) { */
-  if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
-    bool ssldone = FALSE;
-    result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-    pop3c->ssldone = ssldone;
-    if(result || !pop3c->ssldone)
-      return result;
-  }
-
   result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
   *done = (pop3c->state == POP3_STOP);
 
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index e264c73..8a87d24 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -601,8 +601,8 @@
     switch(arg) {
     case CURL_HTTP_VERSION_NONE:
 #ifdef USE_HTTP2
-      /* TODO: this seems an undesirable quirk to force a behaviour on
-       * lower implementations that they should recognize independently? */
+      /* This seems an undesirable quirk to force a behaviour on lower
+       * implementations that they should recognize independently? */
       arg = CURL_HTTP_VERSION_2TLS;
 #endif
       /* accepted */
@@ -1584,10 +1584,6 @@
       if(data->share->hsts == data->hsts)
         data->hsts = NULL;
 #endif
-#ifdef USE_SSL
-      if(data->share->ssl_scache == data->state.ssl_scache)
-        data->state.ssl_scache = data->multi ? data->multi->ssl_scache : NULL;
-#endif
 #ifdef USE_LIBPSL
       if(data->psl == &data->share->psl)
         data->psl = data->multi ? &data->multi->psl : NULL;
@@ -1628,10 +1624,6 @@
         data->hsts = data->share->hsts;
       }
 #endif
-#ifdef USE_SSL
-      if(data->share->ssl_scache)
-        data->state.ssl_scache = data->share->ssl_scache;
-#endif
 #ifdef USE_LIBPSL
       if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
         data->psl = &data->share->psl;
@@ -2425,6 +2417,7 @@
      */
     return Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], ptr);
 
+#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
   case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
     /*
      * Option to allow for the MD5 of the host public key to be checked
@@ -2437,7 +2430,7 @@
      * Store the filename to read known hosts from.
      */
     return Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], ptr);
-
+#endif
   case CURLOPT_SSH_KEYDATA:
     /*
      * Custom client data to pass to the SSH keyfunc callback
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 752386d..d22030c 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -881,7 +881,16 @@
       return CURLE_COULDNT_CONNECT;
     }
     nrsp = msg;
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic push
+/* error: 'memcpy' offset [74, 80] from the object at '<unknown>' is out of
+   the bounds of referenced subobject 'bytes' with type 'char[1]' */
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
     memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif
     smbc->session_key = smb_swap32(nrsp->session_key);
     result = smb_send_setup(data);
     if(result) {
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 7c631a1..b763557 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -189,19 +189,6 @@
   SASL_FLAG_BASE64      /* Configuration flags */
 };
 
-#ifdef USE_SSL
-static void smtp_to_smtps(struct connectdata *conn)
-{
-  /* Change the connection handler */
-  conn->handler = &Curl_handler_smtps;
-
-  /* Set the connection's upgraded to TLS flag */
-  conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define smtp_to_smtps(x) Curl_nop_stmt
-#endif
-
 /***********************************************************************
  *
  * smtp_endofresp()
@@ -396,31 +383,38 @@
  */
 static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
 {
+#ifdef USE_SSL
   /* Start the SSL connection */
   struct connectdata *conn = data->conn;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   CURLcode result;
   bool ssldone = FALSE;
 
+  DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
   if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;
+    /* Change the connection handler and SMTP state */
+    conn->handler = &Curl_handler_smtps;
+    conn->bits.tls_upgraded = TRUE;
   }
 
+  DEBUGASSERT(!smtpc->ssldone);
   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-  if(!result) {
+  DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d",
+           result, ssldone));
+  if(!result && ssldone) {
     smtpc->ssldone = ssldone;
-    if(smtpc->state != SMTP_UPGRADETLS)
-      smtp_state(data, SMTP_UPGRADETLS);
-
-    if(smtpc->ssldone) {
-      smtp_to_smtps(conn);
-      result = smtp_perform_ehlo(data);
-    }
+    /* perform EHLO now, changes smpt->state out of SMTP_UPGRADETLS */
+    result = smtp_perform_ehlo(data);
   }
 out:
   return result;
+#else
+  (void)data;
+  return CURLE_NOT_BUILT_IN;
+#endif
 }
 
 /***********************************************************************
@@ -875,7 +869,7 @@
       result = smtp_perform_authentication(data);
   }
   else
-    result = smtp_perform_upgrade_tls(data);
+    smtp_state(data, SMTP_UPGRADETLS);
 
   return result;
 }
@@ -1204,8 +1198,11 @@
 
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
 upgrade_tls:
-  if(smtpc->state == SMTP_UPGRADETLS)
-    return smtp_perform_upgrade_tls(data);
+  if(smtpc->state == SMTP_UPGRADETLS) {
+    result = smtp_perform_upgrade_tls(data);
+    if(result || (smtpc->state == SMTP_UPGRADETLS))
+      return result;
+  }
 
   /* Flush any data that needs to be sent */
   if(pp->sendleft)
@@ -1288,14 +1285,6 @@
   struct connectdata *conn = data->conn;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
-  if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !smtpc->ssldone) {
-    bool ssldone = FALSE;
-    result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-    smtpc->ssldone = ssldone;
-    if(result || !smtpc->ssldone)
-      return result;
-  }
-
   result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
   *done = (smtpc->state == SMTP_STOP);
 
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 589f9da..e383917 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -777,22 +777,15 @@
   }
 }
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4706) /* assignment within conditional expression */
-#endif
 static bool str_is_nonascii(const char *str)
 {
   char c;
-  while((c = *str++))
+  while((c = *str++) != 0)
     if(c & 0x80)
       return TRUE;
 
   return FALSE;
 }
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
 
 static CURLcode check_telnet_options(struct Curl_easy *data)
 {
@@ -1559,10 +1552,9 @@
         /* returned not-zero, this an error */
         if(result) {
           keepon = FALSE;
-          /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
-           * Is this the telnet test server not shutting down the socket
-           * in a clean way? Seems to be timing related, happens more
-           * on slow debug build */
+          /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
+           * telnet test server not shutting down the socket in a clean way?
+           * Seems to be timing related, happens more on slow debug build */
           if(data->state.os_errno == ECONNRESET) {
             DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
           }
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index c4b23a8..742828a 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -567,12 +567,6 @@
 #endif
   data->state.httpreq = data->set.method;
 
-#ifdef USE_SSL
-  if(!data->state.ssl_scache)
-    /* There was no ssl session cache set via a share, use the multi one */
-    data->state.ssl_scache = data->multi->ssl_scache;
-#endif
-
   data->state.requests = 0;
   data->state.followlocation = 0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 0f3d100..516ee08 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -125,10 +125,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #ifdef USE_NGHTTP2
 static void data_priority_cleanup(struct Curl_easy *data);
 #else
@@ -566,7 +562,7 @@
 
   DEBUGASSERT(conn);
 
-  for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+  for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
     Curl_conn_cf_discard_all(data, conn, (int)i);
   }
 
@@ -2690,7 +2686,6 @@
   }
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
-    int ret;
     bool url_provided = FALSE;
 
     if(data->state.aptr.user &&
@@ -2702,17 +2697,19 @@
     }
 
     if(!*passwdp) {
-      ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
-                            userp, passwdp,
-                            data->set.str[STRING_NETRC_FILE]);
-      if(ret > 0) {
+      NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
+                                      userp, passwdp,
+                                      data->set.str[STRING_NETRC_FILE]);
+      if(ret && ((ret == NETRC_NO_MATCH) ||
+                 (data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
         infof(data, "Couldn't find host %s in the %s file; using defaults",
               conn->host.name,
               (data->set.str[STRING_NETRC_FILE] ?
                data->set.str[STRING_NETRC_FILE] : ".netrc"));
       }
-      else if(ret < 0) {
-        failf(data, ".netrc parser error");
+      else if(ret) {
+        const char *m = Curl_netrc_strerror(ret);
+        failf(data, ".netrc error: %s", m);
         return CURLE_READ_ERROR;
       }
       else {
@@ -3105,7 +3102,7 @@
     DEBUGF(infof(data, "check Alt-Svc for host %s", host));
     if(srcalpnid == ALPN_none) {
       /* scan all alt-svc protocol ids in order or relevance */
-      for(i = 0; !hit && (i < ARRAYSIZE(alpn_ids)); ++i) {
+      for(i = 0; !hit && (i < CURL_ARRAYSIZE(alpn_ids)); ++i) {
         srcalpnid = alpn_ids[i];
         hit = Curl_altsvc_lookup(data->asi,
                                  srcalpnid, host, conn->remote_port, /* from */
@@ -3215,7 +3212,7 @@
 #endif
 
   if(unix_path) {
-    /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
+    /* This only works if previous transport is TRNSPRT_TCP. Check it? */
     conn->transport = TRNSPRT_UNIX;
     return resolve_unix(data, conn, unix_path);
   }
@@ -3313,7 +3310,7 @@
    *   We want to reuse an existing conn to the remote endpoint.
    * Since connection reuse does not match on conn->host necessarily, we
    * switch `existing` conn to `temp` conn's host settings.
-   * TODO: is this correct in the case of TLS connections that have
+   *       Is this correct in the case of TLS connections that have
    *       used the original hostname in SNI to negotiate? Do we send
    *       requests for another host through the different SNI?
    */
@@ -3573,7 +3570,6 @@
   if(result)
     goto out;
 
-  /* FIXME: do we really want to run this every time we add a transfer? */
   Curl_cpool_prune_dead(data);
 
   /*************************************************************
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index d9b04e3..2368bd7 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -395,7 +395,7 @@
 
   /* We will now try to extract the
    * possible login information in a string like:
-   * ftp://user:password@ftp.my.site:8021/README */
+   * ftp://user:password@ftp.site.example:8021/README */
   ptr++;
 
   /* if this is a known scheme, get some details */
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index 33fa0d1..d9acb2b 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -1108,7 +1108,6 @@
 /**
  * Priority information for an easy handle in relation to others
  * on the same connection.
- * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218
  */
 struct Curl_data_priority {
 #ifdef USE_NGHTTP2
@@ -1199,7 +1198,6 @@
   curl_prot_t first_remote_protocol;
 
   int retrycount; /* number of retries on a new connection */
-  struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
   int os_errno;  /* filled in with errno whenever an error occurs */
   long followlocation; /* redirect counter */
   int requests; /* request counter: redirects + authentication retakes */
diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c
index 9e7cfbe..e0b5949 100644
--- a/Utilities/cmcurl/lib/vquic/curl_msh3.c
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -441,10 +441,10 @@
   CURLcode result;
   bool rv = FALSE;
 
-  /* TODO: we would like to limit the amount of data we are buffer here.
-   * There seems to be no mechanism in msh3 to adjust flow control and
-   * it is undocumented what happens if we return FALSE here or less
-   * length (buflen is an inout parameter).
+  /* We would like to limit the amount of data we are buffer here. There seems
+   * to be no mechanism in msh3 to adjust flow control and it is undocumented
+   * what happens if we return FALSE here or less length (buflen is an inout
+   * parameter).
    */
   (void)Request;
   if(!stream)
@@ -703,8 +703,8 @@
       goto out;
     }
 
-    /* TODO - msh3/msquic will hold onto this memory until the send complete
-       event. How do we make sure curl does not free it until then? */
+    /* msh3/msquic will hold onto this memory until the send complete event.
+       How do we make sure curl does not free it until then? */
     *err = CURLE_OK;
     nwritten = len;
   }
@@ -838,7 +838,7 @@
   MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
 
   if(verify && (conn_config->CAfile || conn_config->CApath)) {
-    /* TODO: need a way to provide trust anchors to MSH3 */
+    /* Need a way to provide trust anchors to MSH3 */
 #ifdef DEBUGBUILD
     /* we need this for our test cases to run */
     CURL_TRC_CF(data, cf, "non-standard CA not supported, "
@@ -1006,7 +1006,7 @@
 
   switch(query) {
   case CF_QUERY_MAX_CONCURRENT: {
-    /* TODO: we do not have access to this so far, fake it */
+    /* We do not have access to this so far, fake it */
     (void)ctx;
     *pres1 = 100;
     return CURLE_OK;
@@ -1091,7 +1091,7 @@
 
   (void)data;
   (void)conn;
-  (void)ai; /* TODO: msh3 resolves itself? */
+  (void)ai; /* msh3 resolves itself? */
   ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
index f2ceff2..cc9d560 100644
--- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -394,7 +394,7 @@
   struct Curl_cfilter *cf = user_data;
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
 
-  (void)ctx;  /* TODO: need an easy handle to infof() message */
+  (void)ctx;  /* need an easy handle to infof() message */
   va_list ap;
   va_start(ap, fmt);
   vfprintf(stderr, fmt, ap);
@@ -1591,7 +1591,7 @@
   if(ctx->tls_vrfy_result)
     return ctx->tls_vrfy_result;
 
-  (void)eos; /* TODO: use for stream EOF and block handling */
+  (void)eos; /* use for stream EOF and block handling */
   result = cf_progress_ingress(cf, data, &pktx);
   if(result) {
     *err = result;
@@ -1965,8 +1965,8 @@
                               struct Curl_easy *data,
                               bool pause)
 {
-  /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge
-   * the streams windows. As we do in HTTP/2. */
+  /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams
+   * windows. As we do in HTTP/2. */
   if(!pause) {
     h3_drain_stream(cf, data);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c
index 1b78497..4fd4fee 100644
--- a/Utilities/cmcurl/lib/vquic/curl_osslq.c
+++ b/Utilities/cmcurl/lib/vquic/curl_osslq.c
@@ -82,10 +82,6 @@
 #define H3_STREAM_SEND_CHUNKS \
           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
 typedef uint32_t sslerr_t;
 #else
@@ -458,7 +454,7 @@
   struct cf_osslq_ctx *ctx = cf->ctx;
   curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl);
 
-  if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
+  if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) {
     /* rejected, we are full */
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream",
                 stream_id);
@@ -1564,7 +1560,7 @@
     bool blocked = FALSE, eos_written = FALSE;
 
     n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
-                                   vec, ARRAYSIZE(vec));
+                                   vec, CURL_ARRAYSIZE(vec));
     if(n < 0) {
       failf(data, "nghttp3_conn_writev_stream returned error: %s",
             nghttp3_strerror((int)n));
@@ -1984,7 +1980,7 @@
   ssize_t nwritten;
   CURLcode result;
 
-  (void)eos; /* TODO: use to end stream */
+  (void)eos; /* use to end stream */
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
   DEBUGASSERT(ctx->tls.ossl.ssl);
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
index ec0dcce..679cba3 100644
--- a/Utilities/cmcurl/lib/vquic/curl_quiche.c
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -926,8 +926,8 @@
   nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
                                  (uint8_t *)buf, len, eos);
   if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
-    /* TODO: we seem to be blocked on flow control and should HOLD
-     * sending. But when do we open again? */
+    /* Blocked on flow control and should HOLD sending. But when do we open
+     * again? */
     if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
       CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
                   "-> window exhausted", stream->id, len);
@@ -1204,8 +1204,8 @@
                               struct Curl_easy *data,
                               bool pause)
 {
-  /* TODO: there seems right now no API in quiche to shrink/enlarge
-   * the streams windows. As we do in HTTP/2. */
+  /* There seems to exist no API in quiche to shrink/enlarge the streams
+   * windows. As we do in HTTP/2. */
   if(!pause) {
     h3_drain_stream(cf, data);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c
index 6e1ace2..ff2445d 100644
--- a/Utilities/cmcurl/lib/vquic/vquic-tls.c
+++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -58,10 +58,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #if defined(USE_WOLFSSL)
 
 #define QUIC_CIPHERS                                                          \
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index 9ac3518..69de5c1 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -163,7 +163,7 @@
     case EIO:
       if(pktlen > gsolen) {
         /* GSO failure */
-        failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
+        infof(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
               SOCKERRNO);
         qctx->no_gso = TRUE;
         return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index 8d42ddd..2390967 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -342,17 +342,11 @@
   struct curl_khkey *knownkeyp = NULL;
   curl_sshkeycallback func =
     data->set.ssh_keyfunc;
-
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
   struct ssh_knownhosts_entry *knownhostsentry = NULL;
   struct curl_khkey knownkey;
-#endif
 
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
   rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
-#else
-  rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
-#endif
+
   if(rc != SSH_OK)
     return rc;
 
@@ -388,7 +382,6 @@
 
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
 
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
     /* Get the known_key from the known hosts file */
     vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
                                                &knownhostsentry);
@@ -446,22 +439,6 @@
       break;
     }
 
-#else
-    vstate = ssh_is_server_known(sshc->ssh_session);
-    switch(vstate) {
-    case SSH_SERVER_KNOWN_OK:
-      keymatch = CURLKHMATCH_OK;
-      break;
-    case SSH_SERVER_FILE_NOT_FOUND:
-    case SSH_SERVER_NOT_KNOWN:
-      keymatch = CURLKHMATCH_MISSING;
-      break;
-    default:
-      keymatch = CURLKHMATCH_MISMATCH;
-      break;
-    }
-#endif
-
     if(func) { /* use callback to determine action */
       rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
       if(rc != SSH_OK)
@@ -478,18 +455,14 @@
         foundkey.keytype = CURLKHTYPE_RSA1;
         break;
       case SSH_KEYTYPE_ECDSA:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
       case SSH_KEYTYPE_ECDSA_P256:
       case SSH_KEYTYPE_ECDSA_P384:
       case SSH_KEYTYPE_ECDSA_P521:
-#endif
         foundkey.keytype = CURLKHTYPE_ECDSA;
         break;
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
       case SSH_KEYTYPE_ED25519:
         foundkey.keytype = CURLKHTYPE_ED25519;
         break;
-#endif
       case SSH_KEYTYPE_DSS:
         foundkey.keytype = CURLKHTYPE_DSS;
         break;
@@ -506,11 +479,7 @@
 
       switch(rc) {
       case CURLKHSTAT_FINE_ADD_TO_FILE:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
         rc = ssh_session_update_known_hosts(sshc->ssh_session);
-#else
-        rc = ssh_write_knownhost(sshc->ssh_session);
-#endif
         if(rc != SSH_OK) {
           goto cleanup;
         }
@@ -541,11 +510,9 @@
   if(hash)
     ssh_clean_pubkey_hash(&hash);
   ssh_key_free(pubkey);
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
   if(knownhostsentry) {
     ssh_knownhosts_entry_free(knownhostsentry);
   }
-#endif
   return rc;
 }
 
@@ -1848,7 +1815,7 @@
       }
 
       rc = ssh_scp_push_file(sshc->scp_session, protop->path,
-                             data->state.infilesize,
+                             (size_t)data->state.infilesize,
                              (int)data->set.new_file_perms);
       if(rc != SSH_OK) {
         err_msg = ssh_get_error(sshc->ssh_session);
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index edfadc8..429abac 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -83,15 +83,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if LIBSSH2_VERSION_NUM >= 0x010206
-/* libssh2_sftp_statvfs and friends were added in 1.2.6 */
-#define HAS_STATVFS_SUPPORT 1
-#endif
-
-#define sftp_libssh2_realpath(s,p,t,m)                          \
-  libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)),    \
-                          (t), (m), LIBSSH2_SFTP_REALPATH)
-
 /* Local functions: */
 static const char *sftp_libssh2_strerror(unsigned long err);
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
@@ -389,8 +380,6 @@
   sshc->state = nowstate;
 }
 
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
 static int sshkeycallback(CURL *easy,
                           const struct curl_khkey *knownkey, /* known */
                           const struct curl_khkey *foundkey, /* found */
@@ -405,37 +394,7 @@
   /* we only allow perfect matches, and we reject everything else */
   return (match != CURLKHMATCH_OK) ? CURLKHSTAT_REJECT : CURLKHSTAT_FINE;
 }
-#endif
 
-/*
- * Earlier libssh2 versions did not have the ability to seek to 64-bit
- * positions with 32-bit size_t.
- */
-#ifdef HAVE_LIBSSH2_SFTP_SEEK64
-#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
-#else
-#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
-#endif
-
-/*
- * Earlier libssh2 versions did not do SCP properly beyond 32-bit sizes on
- * 32-bit architectures so we check of the necessary function is present.
- */
-#ifndef HAVE_LIBSSH2_SCP_SEND64
-#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
-#else
-#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c),            \
-                                             (libssh2_int64_t)d, 0, 0)
-#endif
-
-/*
- * libssh2 1.2.8 fixed the problem with 32-bit ints used for sockets on win64.
- */
-#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
-#define session_startup(x,y) libssh2_session_handshake(x, y)
-#else
-#define session_startup(x,y) libssh2_session_startup(x, (int)y)
-#endif
 static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
 {
   enum curl_khtype keytype = CURLKHTYPE_UNKNOWN;
@@ -477,7 +436,6 @@
   int rc = 0;
   CURLcode result = CURLE_OK;
 
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
     /* we are asked to verify the host against a file */
     struct connectdata *conn = data->conn;
@@ -537,7 +495,6 @@
         /* no check means failure! */
         rc = CURLKHSTAT_REJECT;
       else {
-#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP
         keycheck = libssh2_knownhost_checkp(sshc->kh,
                                             conn->host.name,
                                             (conn->remote_port != PORT_SSH) ?
@@ -547,15 +504,6 @@
                                             LIBSSH2_KNOWNHOST_KEYENC_RAW|
                                             keybit,
                                             &host);
-#else
-        keycheck = libssh2_knownhost_check(sshc->kh,
-                                           conn->host.name,
-                                           remotekey, keylen,
-                                           LIBSSH2_KNOWNHOST_TYPE_PLAIN|
-                                           LIBSSH2_KNOWNHOST_KEYENC_RAW|
-                                           keybit,
-                                           &host);
-#endif
 
         infof(data, "SSH host check: %d, key: %s", keycheck,
               (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ?
@@ -639,9 +587,6 @@
       break;
     }
   }
-#else /* HAVE_LIBSSH2_KNOWNHOST_API */
-  (void)data;
-#endif
   return result;
 }
 
@@ -819,8 +764,6 @@
 {
   CURLcode result = CURLE_OK;
 
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-
 #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
   static const char * const hostkey_method_ssh_ed25519
     = "ssh-ed25519";
@@ -916,12 +859,10 @@
         break;
 #endif
       case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
-#ifdef HAVE_LIBSSH2_VERSION
         if(libssh2_version(0x010900))
           /* since 1.9.0 libssh2_session_method_pref() works as expected */
           hostkey_method = hostkey_method_ssh_rsa_all;
         else
-#endif
           /* old libssh2 which cannot correctly remove unsupported methods due
            * to bug in src/kex.c or does not support the new methods anyways.
            */
@@ -956,8 +897,6 @@
     }
   }
 
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-
   return result;
 }
 
@@ -1094,12 +1033,10 @@
     state(data, SSH_SFTP_QUOTE_UNLINK);
     return result;
   }
-#ifdef HAS_STATVFS_SUPPORT
   else if(strncasecompare(cmd, "statvfs ", 8)) {
     state(data, SSH_SFTP_QUOTE_STATVFS);
     return result;
   }
-#endif
 
   failf(data, "Unknown SFTP command");
   Curl_safefree(sshc->quote_path1);
@@ -1264,7 +1201,8 @@
       Curl_pgrsSetUploadSize(data, data->state.infilesize);
     }
 
-    SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+    libssh2_sftp_seek64(sshc->sftp_handle,
+                        (libssh2_uint64_t)data->state.resume_from);
   }
   if(data->state.infilesize > 0) {
     data->req.size = data->state.infilesize;
@@ -1565,7 +1503,7 @@
         size = to - from + 1;
       }
 
-      SFTP_SEEK(sshc->sftp_handle, from);
+      libssh2_sftp_seek64(sshc->sftp_handle, (libssh2_uint64_t)from);
     }
     data->req.size = size;
     data->req.maxdownload = size;
@@ -1598,7 +1536,8 @@
     data->req.maxdownload = attrs.filesize - data->state.resume_from;
     Curl_pgrsSetDownloadSize(data,
                              attrs.filesize - data->state.resume_from);
-    SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+    libssh2_sftp_seek64(sshc->sftp_handle,
+                        (libssh2_uint64_t)data->state.resume_from);
   }
 
   /* Setup the actual download */
@@ -1670,10 +1609,10 @@
       return result;
     }
   }
-  else if(rc == 0) {
+  else if(!rc) {
     state(data, SSH_SFTP_READDIR_DONE);
   }
-  else if(rc < 0) {
+  else {
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
     result = sftp_libssh2_error_to_CURLE(sftperr);
     sshc->actualcode = result ? result : CURLE_SSH;
@@ -1723,7 +1662,8 @@
       FALLTHROUGH();
 
     case SSH_S_STARTUP:
-      rc = session_startup(sshc->ssh_session, conn->sock[FIRSTSOCKET]);
+      rc = libssh2_session_handshake(sshc->ssh_session,
+                                     conn->sock[FIRSTSOCKET]);
       if(rc == LIBSSH2_ERROR_EAGAIN) {
         break;
       }
@@ -1878,7 +1818,6 @@
       break;
 
     case SSH_AUTH_AGENT_INIT:
-#ifdef HAVE_LIBSSH2_AGENT_API
       if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
          && (strstr(sshc->authlist, "publickey") != NULL)) {
 
@@ -1908,12 +1847,10 @@
         }
       }
       else
-#endif /* HAVE_LIBSSH2_AGENT_API */
         state(data, SSH_AUTH_KEY_INIT);
       break;
 
     case SSH_AUTH_AGENT_LIST:
-#ifdef HAVE_LIBSSH2_AGENT_API
       rc = libssh2_agent_list_identities(sshc->ssh_agent);
 
       if(rc == LIBSSH2_ERROR_EAGAIN)
@@ -1927,11 +1864,9 @@
         state(data, SSH_AUTH_AGENT);
         sshc->sshagent_prev_identity = NULL;
       }
-#endif
       break;
 
     case SSH_AUTH_AGENT:
-#ifdef HAVE_LIBSSH2_AGENT_API
       /* as prev_identity evolves only after an identity user auth finished we
          can safely request it again as long as EAGAIN is returned here or by
          libssh2_agent_userauth */
@@ -1968,7 +1903,6 @@
         state(data, SSH_AUTH_KEY_INIT);
         rc = 0; /* clear rc and continue */
       }
-#endif
       break;
 
     case SSH_AUTH_KEY_INIT:
@@ -2051,8 +1985,10 @@
       /*
        * Get the "home" directory
        */
-      rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
-                                 sshp->readdir_filename, CURL_PATH_MAX);
+      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) {
         break;
       }
@@ -2288,7 +2224,6 @@
       state(data, SSH_SFTP_NEXT_QUOTE);
       break;
 
-#ifdef HAS_STATVFS_SUPPORT
     case SSH_SFTP_QUOTE_STATVFS:
     {
       LIBSSH2_SFTP_STATVFS statvfs;
@@ -2351,7 +2286,7 @@
       state(data, SSH_SFTP_NEXT_QUOTE);
       break;
     }
-#endif
+
     case SSH_SFTP_GETINFO:
     {
       if(data->set.get_filetime) {
@@ -2684,8 +2619,9 @@
        * directory in the path.
        */
       sshc->ssh_channel =
-        SCP_SEND(sshc->ssh_session, sshp->path, data->set.new_file_perms,
-                 data->state.infilesize);
+        libssh2_scp_send64(sshc->ssh_session, sshp->path,
+                           (int)data->set.new_file_perms,
+                           (libssh2_int64_t)data->state.infilesize, 0, 0);
       if(!sshc->ssh_channel) {
         int ssh_err;
         char *err_msg = NULL;
@@ -2920,14 +2856,11 @@
       break;
 
     case SSH_SESSION_FREE:
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
       if(sshc->kh) {
         libssh2_knownhost_free(sshc->kh);
         sshc->kh = NULL;
       }
-#endif
 
-#ifdef HAVE_LIBSSH2_AGENT_API
       if(sshc->ssh_agent) {
         rc = libssh2_agent_disconnect(sshc->ssh_agent);
         if(rc == LIBSSH2_ERROR_EAGAIN) {
@@ -2948,7 +2881,6 @@
         sshc->sshagent_identity = NULL;
         sshc->sshagent_prev_identity = NULL;
       }
-#endif
 
       if(sshc->ssh_session) {
         rc = libssh2_session_free(sshc->ssh_session);
@@ -2970,12 +2902,8 @@
       DEBUGASSERT(sshc->ssh_channel == NULL);
       DEBUGASSERT(sshc->sftp_session == NULL);
       DEBUGASSERT(sshc->sftp_handle == NULL);
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
       DEBUGASSERT(sshc->kh == NULL);
-#endif
-#ifdef HAVE_LIBSSH2_AGENT_API
       DEBUGASSERT(sshc->ssh_agent == NULL);
-#endif
 
       Curl_safefree(sshc->rsa_pub);
       Curl_safefree(sshc->rsa);
@@ -3325,14 +3253,11 @@
     conn->send[FIRSTSOCKET] = sftp_send;
   }
 
-  if(data->set.ssh_compression) {
-#if LIBSSH2_VERSION_NUM >= 0x010208
-    if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0)
-#endif
-      infof(data, "Failed to enable compression for ssh session");
+  if(data->set.ssh_compression &&
+     libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) {
+    infof(data, "Failed to enable compression for ssh session");
   }
 
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
     int rc;
     sshc->kh = libssh2_knownhost_init(sshc->ssh_session);
@@ -3350,7 +3275,6 @@
       infof(data, "Failed to read known hosts from %s",
             data->set.str[STRING_SSH_KNOWNHOSTS]);
   }
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
 
 #ifdef CURL_LIBSSH2_DEBUG
   libssh2_trace(sshc->ssh_session, ~0);
@@ -3761,25 +3685,21 @@
 
 CURLcode Curl_ssh_init(void)
 {
-#ifdef HAVE_LIBSSH2_INIT
   if(libssh2_init(0)) {
     DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
     return CURLE_FAILED_INIT;
   }
-#endif
   return CURLE_OK;
 }
 
 void Curl_ssh_cleanup(void)
 {
-#ifdef HAVE_LIBSSH2_EXIT
   (void)libssh2_exit();
-#endif
 }
 
 void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION);
+  (void)msnprintf(buffer, buflen, "libssh2/%s", libssh2_version(0));
 }
 
 /* The SSH session is associated with the *CONNECTION* but the callback user
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h
index 8d8a9b3..bbbe95d 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -201,17 +201,10 @@
   Curl_send *tls_send;
 #endif
 
-#ifdef HAVE_LIBSSH2_AGENT_API
   LIBSSH2_AGENT *ssh_agent;     /* proxy to ssh-agent/pageant */
-  struct libssh2_agent_publickey *sshagent_identity,
-                                 *sshagent_prev_identity;
-#endif
-
-  /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h
-     header */
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+  struct libssh2_agent_publickey *sshagent_identity;
+  struct libssh2_agent_publickey *sshagent_prev_identity;
   LIBSSH2_KNOWNHOSTS *kh;
-#endif
 #elif defined(USE_WOLFSSH)
   WOLFSSH *ssh_session;
   WOLFSSH_CTX *ctx;
@@ -221,43 +214,20 @@
 #endif /* USE_LIBSSH */
 };
 
+#ifdef USE_LIBSSH
+#if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 9, 0)
+#  error "SCP/SFTP protocols require libssh 0.9.0 or later"
+#endif
+#endif
+
 #if defined(USE_LIBSSH2)
 
 /* Feature detection based on version numbers to better work with
    non-configure platforms */
 
-#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000)
-#  error "SCP/SFTP protocols require libssh2 0.16 or later"
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010000
-#define HAVE_LIBSSH2_SFTP_SEEK64 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010100
-#define HAVE_LIBSSH2_VERSION 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010205
-#define HAVE_LIBSSH2_INIT 1
-#define HAVE_LIBSSH2_EXIT 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010206
-#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1
-#define HAVE_LIBSSH2_SCP_SEND64 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010208
-#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1
-#endif
-
-#ifdef HAVE_LIBSSH2_VERSION
-/* get it runtime if possible */
-#define CURL_LIBSSH2_VERSION libssh2_version(0)
-#else
-/* use build-time if runtime not possible */
-#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
+#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208)
+#  error "SCP/SFTP protocols require libssh2 1.2.8 or later"
+/* 1.2.8 was released on April 5 2011 */
 #endif
 
 #endif /* USE_LIBSSH2 */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index d2c0172..a47d803 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -63,10 +63,6 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #define QUIC_PRIORITY \
   "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
   "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
@@ -1223,7 +1219,7 @@
     size_t i, alen = alpn_len;
     unsigned char *salpn = (unsigned char *)alpn;
     unsigned char slen;
-    for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
+    for(i = 0; (i < CURL_ARRAYSIZE(gtls_alpns)) && alen; ++i) {
       slen = salpn[0];
       if(slen >= alen)
         return CURLE_FAILED_INIT;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 456b561..13e44c7 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -79,11 +79,8 @@
 #include "memdebug.h"
 
 /* ALPN for http2 */
-#ifdef USE_HTTP2
-#  undef HAS_ALPN
-#  ifdef MBEDTLS_SSL_ALPN
-#    define HAS_ALPN
-#  endif
+#if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN)
+#  define HAS_ALPN_MBEDTLS
 #endif
 
 struct mbed_ssl_backend_data {
@@ -97,7 +94,7 @@
 #endif
   mbedtls_pk_context pk;
   mbedtls_ssl_config config;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
   const char *protocols[3];
 #endif
   int *ciphersuites;
@@ -931,7 +928,7 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
   if(connssl->alpn) {
     struct alpn_proto_buf proto;
     size_t i;
@@ -1109,7 +1106,7 @@
     }
   }
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
   if(connssl->alpn) {
     const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
 
@@ -1636,7 +1633,6 @@
                                   unsigned char *sha256sum,
                                   size_t sha256len UNUSED_PARAM)
 {
-  /* TODO: explain this for different mbedtls 2.x vs 3 version */
   (void)sha256len;
 #if MBEDTLS_VERSION_NUMBER < 0x02070000
   mbedtls_sha256(input, inputlen, sha256sum, 0);
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index ddb05f2..71f4b0d 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -116,10 +116,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
    renegotiations when built with BoringSSL. Renegotiating is non-compliant
    with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -1313,7 +1309,9 @@
   if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
      (file_type == SSL_FILETYPE_PROVIDER)) {
     SSL *ssl;
-    X509 *x509;
+    X509 *x509 = NULL;
+    EVP_PKEY *pri = NULL;
+    STACK_OF(X509) *ca = NULL;
     int cert_done = 0;
     int cert_use_result;
 
@@ -1502,8 +1500,6 @@
     {
       BIO *cert_bio = NULL;
       PKCS12 *p12 = NULL;
-      EVP_PKEY *pri;
-      STACK_OF(X509) *ca = NULL;
       if(cert_blob) {
         cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len));
         if(!cert_bio) {
@@ -1544,8 +1540,7 @@
 
       PKCS12_PBE_add();
 
-      if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
-                       &ca)) {
+      if(!PKCS12_parse(p12, key_passwd, &pri, &x509, &ca)) {
         failf(data,
               "could not parse PKCS12 file, check password, " OSSL_PACKAGE
               " error %s",
@@ -2067,8 +2062,6 @@
                         sizeof(error_buffer)));
     /* Do not attempt to load it again */
     data->state.provider_failed = TRUE;
-    /* FIXME not the right error but much less fuss than creating a new
-     * public one */
     return CURLE_SSL_ENGINE_NOTFOUND;
   }
   data->state.provider = TRUE;
@@ -2884,10 +2877,9 @@
 /* ====================================================== */
 
 /* Check for OpenSSL 1.0.2 which has ALPN support. */
-#undef HAS_ALPN
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L       \
   && !defined(OPENSSL_NO_TLSEXT)
-#  define HAS_ALPN 1
+#  define HAS_ALPN_OPENSSL
 #endif
 
 /* Check for OpenSSL 1.1.0 which has set_{min,max}_proto_version(). */
@@ -3381,7 +3373,7 @@
         "CA"      /* Intermediate Certification Authorities */
       };
       size_t i;
-      for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+      for(i = 0; i < CURL_ARRAYSIZE(storeNames); ++i) {
         bool imported = FALSE;
         result = import_windows_cert_store(data, storeNames[i], store,
                                            &imported);
@@ -3692,7 +3684,7 @@
   ctx_option_t ctx_options = 0;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  const long int ssl_version_min = conn_config->version;
+  unsigned int ssl_version_min = conn_config->version;
   char * const ssl_cert = ssl_config->primary.clientcert;
   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
   const char * const ssl_cert_type = ssl_config->cert_type;
@@ -3735,6 +3727,7 @@
     }
     break;
   case TRNSPRT_QUIC:
+    ssl_version_min = CURL_SSLVERSION_TLSv1_3;
     if(conn_config->version_max &&
        (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
       failf(data, "QUIC needs at least TLS version 1.3");
@@ -3875,7 +3868,7 @@
 #endif
 
   if(alpn && alpn_len) {
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
     if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
       failf(data, "Error setting ALPN");
       return CURLE_SSL_CONNECT_ERROR;
@@ -3898,7 +3891,7 @@
   ciphers = conn_config->cipher_list;
   if(!ciphers && (peer->transport != TRNSPRT_QUIC))
     ciphers = DEFAULT_CIPHER_SELECTION;
-  if(ciphers) {
+  if(ciphers && (ssl_version_min < CURL_SSLVERSION_TLSv1_3)) {
     if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
       failf(data, "failed setting cipher list: %s", ciphers);
       return CURLE_SSL_CIPHER;
@@ -3909,7 +3902,9 @@
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
   {
     const char *ciphers13 = conn_config->cipher_list13;
-    if(ciphers13) {
+    if(ciphers13 &&
+       (!conn_config->version_max ||
+        (conn_config->version_max >= CURL_SSLVERSION_MAX_TLSv1_3))) {
       if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
         failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
         return CURLE_SSL_CIPHER;
@@ -4213,7 +4208,7 @@
   DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
   DEBUGASSERT(octx);
   memset(&proto, 0, sizeof(proto));
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
   if(connssl->alpn) {
     result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
     if(result) {
@@ -4250,7 +4245,7 @@
   SSL_set_bio(octx->ssl, bio, bio);
 #endif
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
   if(connssl->alpn) {
     Curl_alpn_to_proto_str(&proto, connssl->alpn);
     infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
@@ -4307,7 +4302,6 @@
       servername_type = SSL_get_servername_type(ssl);
       inner = SSL_get_servername(ssl, servername_type);
       SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
-      /* TODO: get the inner from BoringSSL */
       infof(data, "ECH: retry_configs for %s from %s, %d %d",
             inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
 #endif
@@ -4562,7 +4556,7 @@
 # endif  /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
 #endif  /* USE_ECH_OPENSSL */
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
     /* Sets data and len to negotiated protocol, len is 0 if no protocol was
      * negotiated
      */
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 8441b46..2af29a4 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -77,7 +77,7 @@
    https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
 */
 #if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
-#  define HAS_ALPN 1
+#  define HAS_ALPN_SCHANNEL
 #endif
 
 #ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
@@ -888,7 +888,7 @@
   SecBufferDesc outbuf_desc;
   SecBuffer inbuf;
   SecBufferDesc inbuf_desc;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
   unsigned char alpn_buffer[128];
 #endif
   SECURITY_STATUS sspi_status = SEC_E_OK;
@@ -908,7 +908,7 @@
           "connect to some servers due to lack of SNI, algorithms, etc.");
   }
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
   /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
      Also it does not seem to be supported for WINE, see curl bug #983. */
   backend->use_alpn = connssl->alpn &&
@@ -991,7 +991,7 @@
     infof(data, "schannel: using IP address, SNI is not supported by OS.");
   }
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
   if(backend->use_alpn) {
     int cur = 0;
     int list_start_index = 0;
@@ -1039,7 +1039,7 @@
     InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
     InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
   }
-#else /* HAS_ALPN */
+#else /* HAS_ALPN_SCHANNEL */
   InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
   InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
 #endif
@@ -1533,7 +1533,7 @@
   CURLcode result = CURLE_OK;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CERT_CONTEXT *ccert_context = NULL;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
   SecPkgContext_ApplicationProtocol alpn_result;
 #endif
 
@@ -1562,7 +1562,7 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
   if(backend->use_alpn) {
     sspi_status =
       Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
@@ -2367,7 +2367,6 @@
       Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
       if(!result) {
         if(written < (ssize_t)outbuf.cbBuffer) {
-          /* TODO: handle partial sends */
           failf(data, "schannel: failed to send close msg: %s"
                 " (bytes written: %zd)", curl_easy_strerror(result), written);
           result = CURLE_SEND_ERROR;
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index c258b11..9c59b4c 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -915,7 +915,9 @@
 {
   if(multissl_setup(NULL))
     return 1;
-  return Curl_ssl->init();
+  if(Curl_ssl->init)
+    return Curl_ssl->init();
+  return 1;
 }
 
 static CURLcode multissl_connect(struct Curl_cfilter *cf,
@@ -1939,8 +1941,8 @@
     else {
       *palpn = CURL_HTTP_VERSION_NONE;
       failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
-      /* TODO: do we want to fail this? Previous code just ignored it and
-       * some vtls backends even ignore the return code of this function. */
+      /* Previous code just ignored it and some vtls backends even ignore the
+       * return code of this function. */
       /* return CURLE_NOT_BUILT_IN; */
       goto out;
     }
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.c b/Utilities/cmcurl/lib/vtls/vtls_scache.c
index 9c04b77..365e945 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_scache.c
+++ b/Utilities/cmcurl/lib/vtls/vtls_scache.c
@@ -75,13 +75,35 @@
   BIT(hmac_set);           /* if key_salt and key_hmac are present */
 };
 
+#define CURL_SCACHE_MAGIC 0x000e1551
+
+#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
+
 struct Curl_ssl_scache {
+  unsigned int magic;
   struct Curl_ssl_scache_peer *peers;
   size_t peer_count;
   int default_lifetime_secs;
   long age;
 };
 
+static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
+{
+  struct Curl_ssl_scache *scache = NULL;
+  /* If a share is present, its ssl_scache has preference over the multi */
+  if(data->share && data->share->ssl_scache)
+    scache = data->share->ssl_scache;
+  else if(data->multi && data->multi->ssl_scache)
+    scache = data->multi->ssl_scache;
+  if(scache && !GOOD_SCACHE(scache)) {
+    failf(data, "transfer would use an invalid scache at %p, denied",
+          (void *)scache);
+    DEBUGASSERT(0);
+    return NULL;
+  }
+  return scache;
+}
+
 static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
 {
   if(s->sdata) {
@@ -306,6 +328,7 @@
     return CURLE_OUT_OF_MEMORY;
   }
 
+  scache->magic = CURL_SCACHE_MAGIC;
   scache->default_lifetime_secs = (24*60*60); /* 1 day */
   scache->peer_count = max_peers;
   scache->peers = peers;
@@ -322,8 +345,9 @@
 
 void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
 {
-  if(scache) {
+  if(scache && GOOD_SCACHE(scache)) {
     size_t i;
+    scache->magic = 0;
     for(i = 0; i < scache->peer_count; ++i) {
       cf_ssl_scache_clear_peer(&scache->peers[i]);
     }
@@ -359,7 +383,7 @@
     char abspath[_MAX_PATH];
     if(_fullpath(abspath, path, _MAX_PATH))
       return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
-#else
+#elif defined(HAVE_REALPATH)
     if(path[0] != '/') {
       char *abspath = realpath(path, NULL);
       if(abspath) {
@@ -588,6 +612,13 @@
   CURLcode result = CURLE_OK;
 
   *ppeer = NULL;
+  if(!GOOD_SCACHE(scache)) {
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  CURL_TRC_SSLS(data, "find peer slot for %s among %zu slots",
+                ssl_peer_key, scache->peer_count);
+
   /* check for entries with known peer_key */
   for(i = 0; scache && i < scache->peer_count; i++) {
     if(scache->peers[i].ssl_peer_key &&
@@ -792,7 +823,7 @@
                              const char *ssl_peer_key,
                              struct Curl_ssl_session *s)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode result;
   DEBUGASSERT(ssl_config);
@@ -801,6 +832,10 @@
     Curl_ssl_session_destroy(s);
     return CURLE_OK;
   }
+  if(!GOOD_SCACHE(scache)) {
+    Curl_ssl_session_destroy(s);
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
 
   Curl_ssl_scache_lock(data);
   result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
@@ -826,7 +861,7 @@
                               const char *ssl_peer_key,
                               struct Curl_ssl_session **ps)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct Curl_ssl_scache_peer *peer = NULL;
   struct Curl_llist_node *n;
@@ -870,7 +905,7 @@
                                  void *sobj,
                                  Curl_ssl_scache_obj_dtor *sobj_free)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct Curl_ssl_scache_peer *peer = NULL;
   CURLcode result;
@@ -878,6 +913,11 @@
   DEBUGASSERT(sobj);
   DEBUGASSERT(sobj_free);
 
+  if(!scache) {
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    goto out;
+  }
+
   result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
   if(result || !peer) {
     CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
@@ -898,7 +938,7 @@
                              const char *ssl_peer_key,
                              void **sobj)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct Curl_ssl_scache_peer *peer = NULL;
   CURLcode result;
@@ -924,7 +964,7 @@
                                 struct Curl_easy *data,
                                 const char *ssl_peer_key)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct Curl_ssl_scache_peer *peer = NULL;
   CURLcode result;
@@ -977,6 +1017,9 @@
   CURLcode result = CURLE_OK;
 
   *ppeer = NULL;
+  if(!GOOD_SCACHE(scache))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
   /* look for an entry that matches salt+hmac exactly or has a known
    * ssl_peer_key which salt+hmac's to the same. */
   for(i = 0; scache && i < scache->peer_count; i++) {
@@ -1021,7 +1064,7 @@
                                  const unsigned char *shmac, size_t shmac_len,
                                  const unsigned char *sdata, size_t sdata_len)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  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;
@@ -1092,7 +1135,7 @@
                                  curl_ssls_export_cb *export_fn,
                                  void *userptr)
 {
-  struct Curl_ssl_scache *scache = data->state.ssl_scache;
+  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct Curl_ssl_scache_peer *peer;
   struct dynbuf sbuf, hbuf;
   struct Curl_llist_node *n;
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index 82b21af..546ba03 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -770,7 +770,7 @@
   int i;
   char *str;
 
-  for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) {
+  for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) {
     size_t n;
     if((strncmp(str, "TLS13", 5) == 0) != tls13)
       continue;
diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt
index 166b301..04d986b 100644
--- a/Utilities/cmlibarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/CMakeLists.txt
@@ -887,8 +887,8 @@
   IF(OPENSSL_FOUND)
     SET(HAVE_LIBCRYPTO 1)
     INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
-    LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_CRYPTO_LIBRARY})
-    SET(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
+    list(APPEND ADDITIONAL_LIBS OpenSSL::Crypto)
+    set(CMAKE_REQUIRED_LIBRARIES OpenSSL::Crypto)
     SET(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
     LA_CHECK_INCLUDE_FILE("openssl/evp.h" HAVE_OPENSSL_EVP_H)
     CHECK_FUNCTION_EXISTS(PKCS5_PBKDF2_HMAC_SHA1 HAVE_PKCS5_PBKDF2_HMAC_SHA1)
@@ -1018,9 +1018,8 @@
       # was found on this platform.
       IF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
         IF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
-          INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
-	  LIST(APPEND ADDITIONAL_LIBS ${OPENSSL_LIBRARIES})
-	   LIST(REMOVE_DUPLICATES ADDITIONAL_LIBS)
+          LIST(APPEND ADDITIONAL_LIBS OpenSSL::Crypto)
+          LIST(REMOVE_DUPLICATES ADDITIONAL_LIBS)
         ENDIF ("${IMPLEMENTATION}" MATCHES "^OPENSSL$" AND OPENSSL_FOUND)
       ENDIF (ARCHIVE_CRYPTO_${ALGORITHM}_${IMPLEMENTATION})
       ENDIF(NOT ARCHIVE_CRYPTO_${ALGORITHM})